From c1748ddd7b65f5ca4cafe9748da3da2f28710cdf Mon Sep 17 00:00:00 2001 From: "Zhang, Guoming" Date: Thu, 22 Apr 2021 02:02:04 +0000 Subject: [PATCH 01/11] Fix the graph_optimization bug that fp32 only mode doesn't work on CPX host. --- lpot/strategy/strategy.py | 2 +- test/test_graph_optimization.py | 46 +++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/lpot/strategy/strategy.py b/lpot/strategy/strategy.py index 9a933f49add..702320fb6d8 100644 --- a/lpot/strategy/strategy.py +++ b/lpot/strategy/strategy.py @@ -200,7 +200,7 @@ def __init__(self, model, conf, q_dataloader=None, q_func=None, new_list = [] for cfg in cfg_list: if self.graph_optimization_mode: - if cfg['activation']['dtype'] in ['bf16', 'fp32']: + if cfg['activation']['dtype'] in self.cfg.graph_optimization.precisions: new_list.append(cfg) else: if cfg['activation']['dtype'] not in fallback_precision_list: diff --git a/test/test_graph_optimization.py b/test/test_graph_optimization.py index a6351a672e9..001409397eb 100644 --- a/test/test_graph_optimization.py +++ b/test/test_graph_optimization.py @@ -351,5 +351,51 @@ def test_graph_optimization_without_yaml_with_precisions(self): self.assertEqual(found_cast_op, False) + @disable_random() + def test_graph_optimization_fp32_only_with_force_bf16(self): + os.environ['FORCE_BF16'] = '1' + + x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") + top_relu = tf.nn.relu(x) + paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) + x_pad = tf.pad(top_relu, paddings, "CONSTANT") + conv_weights = tf.compat.v1.get_variable("weight", [3, 3, 16, 16], + initializer=tf.compat.v1.random_normal_initializer()) + conv_weights_2 = tf.compat.v1.get_variable("weight_2", [3, 8, 16, 16], + initializer=tf.compat.v1.random_normal_initializer()) + conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") + relu = tf.nn.relu(conv) + + max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") + conv_bias = tf.compat.v1.get_variable("bias", [16], + initializer=tf.compat.v1.random_normal_initializer()) + conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[ + 1, 2, 2, 1], padding="VALID", name='conv1_3') + conv_bias = tf.math.add(conv_1, conv_bias) + relu6 = tf.nn.relu6(conv_bias, name='op_to_store') + + out_name = relu6.name.split(':')[0] + with tf.compat.v1.Session() as sess: + sess.run(tf.compat.v1.global_variables_initializer()) + output_graph_def = graph_util.convert_variables_to_constants( + sess=sess, + input_graph_def=sess.graph_def, + output_node_names=[out_name]) + from lpot.experimental import Graph_Optimization + graph_optimizer = Graph_Optimization() + graph_optimizer.input = 'input' + graph_optimizer.output = 'op_to_store' + + graph_optimizer.model = output_graph_def + output_graph = graph_optimizer() + found_cast_op = False + + for i in output_graph.graph_def.node: + if i.op == 'Cast': + found_cast_op = True + break + + self.assertEqual(found_cast_op, False) + if __name__ == "__main__": unittest.main() From 991e26e6ebdb72a2a2b9df910127be8896474844 Mon Sep 17 00:00:00 2001 From: "Zhang, Guoming" Date: Sun, 9 May 2021 17:22:40 +0800 Subject: [PATCH 02/11] =?UTF-8?q?Fix=20the=20graph=20optimization=20bug=20?= =?UTF-8?q?that=20inputs/outputs=20field=20is=20not=20optio=E2=80=A6=20(#1?= =?UTF-8?q?2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix the graph optimization bug that inputs/outputs field is not optional. Signed-off-by: Zhang, Guoming * Add postfix. --- lpot/experimental/graph_optimization.py | 7 ++++--- test/test_graph_optimization.py | 11 +++-------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/lpot/experimental/graph_optimization.py b/lpot/experimental/graph_optimization.py index 23e8a4133e2..a33630e1bbf 100644 --- a/lpot/experimental/graph_optimization.py +++ b/lpot/experimental/graph_optimization.py @@ -20,6 +20,7 @@ import random import yaml import sys +import tempfile import numpy as np from lpot.model.model import get_model_fwk_name from ..conf.config import Conf @@ -58,8 +59,8 @@ def __init__(self, conf_fname=None): self._eval_func = None self._precisions = 'fp32' - self._input = None - self._output = None + self._input = [] + self._output = [] self.conf = None self.__init_env(self.conf_name, self.model) @@ -205,7 +206,7 @@ def gen_graph_optimization_yaml(self, model_obj): default_yaml_template['model']['inputs'] = self._input default_yaml_template['model']['outputs'] = self._output - graph_optimization_yaml_path = '/tmp/graph_optimization.yaml' + graph_optimization_yaml_path = tempfile.mkstemp(suffix='.yaml')[1] with open(graph_optimization_yaml_path, 'w', encoding='utf-8') as f: yaml.dump(default_yaml_template, f) self.conf = Conf(graph_optimization_yaml_path) diff --git a/test/test_graph_optimization.py b/test/test_graph_optimization.py index 001409397eb..3cfe926b973 100644 --- a/test/test_graph_optimization.py +++ b/test/test_graph_optimization.py @@ -55,7 +55,7 @@ def build_fake_yaml_2(): def build_fake_yaml_3(): fake_yaml_3 = ''' model: - name: fake_yaml_2 + name: fake_yaml_3 framework: tensorflow inputs: input outputs: op_to_store @@ -207,16 +207,15 @@ def test_graph_optimization_without_yaml(self): graph_optimizer.model = output_graph_def output_graph = graph_optimizer() found_cast_op = False - for i in output_graph.graph_def.node: if i.op == 'Cast': found_cast_op = True break - self.assertEqual(found_cast_op, True) + self.assertEqual(found_cast_op, False) @disable_random() - def test_graph_optimization_without_yaml(self): + def test_graph_optimization_with_yaml(self): x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") top_relu = tf.nn.relu(x) @@ -258,7 +257,6 @@ def test_graph_optimization_without_yaml(self): self.assertEqual(found_cast_op, True) - class TestGraphOptmizationFP32(unittest.TestCase): @disable_random() @@ -336,8 +334,6 @@ def test_graph_optimization_without_yaml_with_precisions(self): from lpot.experimental import Graph_Optimization graph_optimizer = Graph_Optimization() graph_optimizer.precisions = 'fp32' - graph_optimizer.input = 'input' - graph_optimizer.output = 'op_to_store' graph_optimizer.model = output_graph_def output_graph = graph_optimizer() @@ -350,7 +346,6 @@ def test_graph_optimization_without_yaml_with_precisions(self): self.assertEqual(found_cast_op, False) - @disable_random() def test_graph_optimization_fp32_only_with_force_bf16(self): os.environ['FORCE_BF16'] = '1' From 31ef70297fff080a92f0a9e7fef2e9b118bd83ee Mon Sep 17 00:00:00 2001 From: Feng Tian Date: Mon, 10 May 2021 10:46:19 +0800 Subject: [PATCH 03/11] fix print log line break issue. --- lpot/experimental/graph_optimization.py | 4 ++-- lpot/model/model.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lpot/experimental/graph_optimization.py b/lpot/experimental/graph_optimization.py index a33630e1bbf..2cf21e12a24 100644 --- a/lpot/experimental/graph_optimization.py +++ b/lpot/experimental/graph_optimization.py @@ -289,8 +289,8 @@ def model(self, user_model): from .common import Model as LpotModel if not isinstance(user_model, LpotModel): - logger.warning('force convert user raw model to lpot model, \ - better initialize lpot.common.Model and set....') + logger.warning('force convert user raw model to lpot model, ' + 'better initialize lpot.common.Model and set....') self.user_model = LpotModel(user_model) else: self.user_model = user_model diff --git a/lpot/model/model.py b/lpot/model/model.py index 2c7acbff4fa..160a1e29897 100644 --- a/lpot/model/model.py +++ b/lpot/model/model.py @@ -193,8 +193,8 @@ def validate_graph_node(graph_def, node_names): all_node_name = [node.name for node in graph_def.node] for user_input_name in node_names: if user_input_name not in all_node_name: - logger.info("Input node name {} doesn't exist in the model, \ - please check the yaml.".format(user_input_name)) + logger.info(str("Input node name {} doesn't exist in the model, " + + "please check the yaml.").format(user_input_name)) return False return True From 3e96813a9aac163f4bce90d6705befc759ef0384 Mon Sep 17 00:00:00 2001 From: "Zhang, Guoming" Date: Mon, 10 May 2021 22:35:25 +0800 Subject: [PATCH 04/11] =?UTF-8?q?Fix=20the=20graph=20optimization=20issue?= =?UTF-8?q?=20that=20setting=20precision=20to=20bf16=20led=20c=E2=80=A6=20?= =?UTF-8?q?(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix the graph optimization issue that setting precision to bf16 led crash on non-avx512-bf16 enabled platform. * Enhance the graph optimization by sorting the specified precisions. * Update the doc. Co-authored-by: Tian, Feng --- docs/graph_optimization.md | 11 +++++ lpot/experimental/graph_optimization.py | 26 +++++++++--- lpot/strategy/auto_mixed_precision.py | 2 + test/test_graph_optimization.py | 55 ++++++++++++++++++++++++- 4 files changed, 88 insertions(+), 6 deletions(-) diff --git a/docs/graph_optimization.md b/docs/graph_optimization.md index de7db86aa75..ae3d4dde18d 100644 --- a/docs/graph_optimization.md +++ b/docs/graph_optimization.md @@ -39,6 +39,17 @@ LPOT would run graph optimization under FP32 optimization by default. In other w graph_optimizer.model = '/path/to/model' optimized_model = graph_optimizer() ``` +Note the **fp32** is optional when the **bf16** is set to precisions field. The below example has the identical action under the hardware platform supports bf16, e.g, the CPX platform. + ```python + from lpot.experimental import Graph_Optimization + graph_optimizer = Graph_Optimization() + graph_optimizer.precisions = 'bf16' + graph_optimizer.model = '/path/to/model' + optimized_model = graph_optimizer() + ``` +For those platforms without bf16 enabling, like CLX. LPOT also could leverage the graph optimization feature to generate the model under bf16 precision.The usage is just adding the `FORCE_BF16=1` before the cmd. +e.g, `FORCE_BF16=1 /path/to/executable_lpot_wrapper`. If we don't add such prefix `FORCE_BF16=1`, the LPOT would exit consequently. + #### 2.2. *Auto-mixed precision with auto-tuning.* diff --git a/lpot/experimental/graph_optimization.py b/lpot/experimental/graph_optimization.py index 2cf21e12a24..c0d582a508c 100644 --- a/lpot/experimental/graph_optimization.py +++ b/lpot/experimental/graph_optimization.py @@ -18,19 +18,20 @@ import os import pickle import random -import yaml -import sys import tempfile +import sys import numpy as np +import yaml from lpot.model.model import get_model_fwk_name from ..conf.config import Conf from ..conf.dotdict import deep_get, deep_set, DotDict from ..strategy import STRATEGIES from ..utils import logger from ..utils.create_obj_from_config import create_dataloader +from ..utils.utility import CpuInfo from ..model import BaseModel as LpotModel -class Graph_Optimization(object): +class Graph_Optimization(): """Graph_Optimization class automatically searches for optimal quantization recipes for low precision model inference, achieving best tuning objectives like inference performance within accuracy loss constraints. @@ -202,7 +203,17 @@ def gen_graph_optimization_yaml(self, model_obj): logger.info('Graph optimization only supports Tensorflow at current stage.') sys.exit(0) default_yaml_template['model']['framework'] = get_model_fwk_name(model_obj.root) - default_yaml_template['graph_optimization']['precisions'] = [str(self._precisions)] + + if self._precisions == ['bf16'] and not CpuInfo().bf16: + if os.getenv('FORCE_BF16') == '1': + logger.warning("Graph optimization will be enforced even " \ + "the hardware platform doesn't support bf16 instruction.") + else: + logger.info("Graph optimization exits due to the hardware " \ + "doesn't support bf16 instruction.") + sys.exit(0) + + default_yaml_template['graph_optimization']['precisions'] = self._precisions default_yaml_template['model']['inputs'] = self._input default_yaml_template['model']['outputs'] = self._output @@ -217,7 +228,11 @@ def precisions(self): @precisions.setter def precisions(self, customized_precisions): - self._precisions = customized_precisions + if isinstance(customized_precisions, list): + self._precisions = sorted([i.strip() for i in customized_precisions]) + elif isinstance(customized_precisions, str): + self._precisions = sorted([i.strip() for i in customized_precisions.split(',')]) + @property def input(self): @@ -291,6 +306,7 @@ def model(self, user_model): if not isinstance(user_model, LpotModel): logger.warning('force convert user raw model to lpot model, ' 'better initialize lpot.common.Model and set....') + self.user_model = LpotModel(user_model) else: self.user_model = user_model diff --git a/lpot/strategy/auto_mixed_precision.py b/lpot/strategy/auto_mixed_precision.py index 42584f95cfb..e4b71f5f64a 100644 --- a/lpot/strategy/auto_mixed_precision.py +++ b/lpot/strategy/auto_mixed_precision.py @@ -94,6 +94,8 @@ def next_tune_cfg(self): if op[1] in combined_cfg.keys() and len(op_cfg) > 0: op_cfgs['op'][op] = copy.deepcopy( self._get_common_cfg(combined_cfg[op[1]], op_cfg)) + elif op[1] not in combined_cfg.keys() or not op_cfg: + pass else: op_cfgs['op'][op] = copy.deepcopy( self.opwise_tune_cfgs[op][0]) diff --git a/test/test_graph_optimization.py b/test/test_graph_optimization.py index 3cfe926b973..ae7a396dcd7 100644 --- a/test/test_graph_optimization.py +++ b/test/test_graph_optimization.py @@ -8,7 +8,7 @@ from tensorflow.python.framework import graph_util from lpot.adaptor.tf_utils.util import disable_random - +from lpot.utils.utility import CpuInfo def build_fake_yaml(): fake_yaml = ''' @@ -69,6 +69,59 @@ def build_fake_yaml_3(): yaml.dump(y, f) f.close() +class TestGraphOptimizationOnNonBF16Host(unittest.TestCase): + @classmethod + def setUpClass(self): + build_fake_yaml() + + @classmethod + def tearDownClass(self): + os.remove('fake_yaml.yaml') + + @disable_random() + def test_bf16_cfg_on_non_bf16_enabled_host(self): + x = tf.compat.v1.placeholder(tf.float32, [1, 300, 300, 16], name="input") + top_relu = tf.nn.relu(x) + paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) + x_pad = tf.pad(top_relu, paddings, "CONSTANT") + conv_weights = tf.compat.v1.get_variable("weight", [3, 3, 16, 16], + initializer=tf.compat.v1.random_normal_initializer()) + conv_weights_2 = tf.compat.v1.get_variable("weight_2", [3, 8, 16, 16], + initializer=tf.compat.v1.random_normal_initializer()) + conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") + relu = tf.nn.relu(conv) + + max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") + conv_bias = tf.compat.v1.get_variable("bias", [16], + initializer=tf.compat.v1.random_normal_initializer()) + conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[ + 1, 2, 2, 1], padding="VALID", name='conv1_3') + conv_bias = tf.math.add(conv_1, conv_bias) + relu6 = tf.nn.relu6(conv_bias, name='op_to_store') + out_name = relu6.name.split(':')[0] + with tf.compat.v1.Session() as sess: + sess.run(tf.compat.v1.global_variables_initializer()) + output_graph_def = graph_util.convert_variables_to_constants( + sess=sess, + input_graph_def=sess.graph_def, + output_node_names=[out_name]) + from lpot.experimental import Graph_Optimization, common + graph_optimizer = Graph_Optimization('fake_yaml.yaml') + dataset = graph_optimizer.dataset('dummy', shape=(100, 300, 300, 16), label=True) + graph_optimizer.eval_dataloader = common.DataLoader(dataset) + graph_optimizer.model = output_graph_def + output_graph = graph_optimizer() + found_cast_op = False + + for i in output_graph.graph_def.node: + if i.op == 'Cast': + found_cast_op = True + break + + if CpuInfo().bf16: + self.assertEqual(found_cast_op, True) + else: + self.assertEqual(found_cast_op, False) class TestGraphOptimization(unittest.TestCase): @classmethod From 13274adfbe36979f154103b4259e953253f3e71b Mon Sep 17 00:00:00 2001 From: Feng Tian Date: Mon, 10 May 2021 10:43:10 +0800 Subject: [PATCH 05/11] bump release version to v1.3.1 --- lpot/version.py | 2 +- meta.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lpot/version.py b/lpot/version.py index eae7da3f70f..98b0a7bb0ff 100644 --- a/lpot/version.py +++ b/lpot/version.py @@ -15,4 +15,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.3" +__version__ = "1.3.1" diff --git a/meta.yaml b/meta.yaml index f0db37ffef8..1140375d7af 100644 --- a/meta.yaml +++ b/meta.yaml @@ -1,4 +1,4 @@ -{% set version = "1.3" %} +{% set version = "1.3.1" %} {% set buildnumber = 0 %} package: name: lpot From 8e76c2f160ed82e7581608c548e6884d4adec2d5 Mon Sep 17 00:00:00 2001 From: "Tian, Feng" Date: Thu, 29 Apr 2021 13:23:31 +0800 Subject: [PATCH 06/11] Doc updates for online website creation (#5) Signed-off-by: Deb Taylor Reviewed-by: Feng Tian Co-authored-by: Deb Taylor --- .github/workflows/publish.yml | 29 ++ .gitignore | 1 + CONTRIBUTING.md | 38 -- Makefile | 29 ++ README.md | 195 +++----- _static/custom.css | 13 + _static/index.html | 1 + api-documentation/apis.rst | 20 + conf.py | 184 ++++++++ contributions.md | 127 +++++ docs/PTQ.md | 6 +- docs/QAT.md | 27 +- docs/Quantization.md | 7 +- docs/adaptor.md | 107 +++-- docs/api-introduction.md | 210 +++++++++ docs/backend_quant.md | 17 +- docs/benchmark.md | 17 +- docs/dataloader.md | 29 +- docs/dataset.md | 15 +- docs/doclist.rst | 66 +++ docs/dynamic_quantization.md | 10 +- docs/full_model_list.md | 35 +- docs/graph_optimization.md | 36 +- docs/incompatible_changes.md | 16 +- docs/introduction.md | 203 -------- docs/metric.md | 69 ++- docs/mixed_precision.md | 18 +- docs/model.md | 18 +- docs/pruning.md | 42 +- docs/tensorboard.md | 128 ++--- docs/transform.md | 5 +- docs/tuning_strategies.md | 14 +- docs/tutorial.md | 61 ++- docs/ux.md | 137 +++--- examples/helloworld/README.md | 2 + examples/pytorch/huggingface_models/README.md | 5 +- examples_readme.md | 6 + getting_started.md | 443 ++++++++++++++++++ index.rst | 25 + legal_information.md | 34 +- make.bat | 36 ++ releases_info.md | 24 + SECURITY.md => security_policy.md | 3 +- sphinx-requirements.txt | 5 + template.png | Bin 0 -> 37834 bytes welcome.md | 22 + 46 files changed, 1764 insertions(+), 771 deletions(-) create mode 100644 .github/workflows/publish.yml delete mode 100644 CONTRIBUTING.md create mode 100644 Makefile create mode 100644 _static/custom.css create mode 100644 _static/index.html create mode 100644 api-documentation/apis.rst create mode 100644 conf.py create mode 100644 contributions.md create mode 100644 docs/api-introduction.md create mode 100644 docs/doclist.rst delete mode 100644 docs/introduction.md create mode 100644 examples_readme.md create mode 100644 getting_started.md create mode 100644 index.rst create mode 100644 make.bat create mode 100644 releases_info.md rename SECURITY.md => security_policy.md (92%) create mode 100644 sphinx-requirements.txt create mode 100644 template.png create mode 100644 welcome.md diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000000..e4e37f8fc72 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,29 @@ +name: Publish + +on: + push: + branches: + - master + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: Install dependencies + run: | + export PATH="$HOME/.local/bin:$PATH" + sudo apt-get install -y python3-setuptools + pip3 install --user -r sphinx-requirements.txt + - name: Build the docs + run: | + export PATH="$HOME/.local/bin:$PATH" + make html + - name: Push the docs + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: _build/html + publish_branch: latestHTML \ No newline at end of file diff --git a/.gitignore b/.gitignore index a2143c1227b..abeff1f177e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ *.log *.swp *.onnx +_build diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index bd09e046f2e..00000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,38 +0,0 @@ -# Contributing guidelines -If you have improvements to Intel® Low Precision Optimization Tool, send us your pull requests for -[review](https://github.com/intel/lpot/pulls)! For those -just getting started, Github has a -[how to](https://help.github.com/articles/using-pull-requests/). - -## Pull Request Checklist -Before sending your pull requests, please make sure that you followed this -list. - -- Read [contributing guidelines](CONTRIBUTING.md). -- Changes are consistent with the Python [Coding Style](https://github.com/google/styleguide/blob/gh-pages/pyguide.md). -- Use pylint to check your Python code -- Use flake8 and autopep8 to make Python code clean -- Add unit tests in [Unit Tests](https://github.com/intel/lpot/tree/master/test) to cover the code you would like to contribute. -- Run [Unit Tests](https://github.com/intel/lpot/tree/master/test). - -## Pull Request Template -### Change Summary -Include a detailed summary of the change please. - -### Change Motivation -Include an explanation of the motivation for the change please. - -### Change Limit -Include an explanation about the regression your change might bring. - -### Test Info -- For bug fix, provide test steps to reproduce your issue. -- For new features, provide test steps besides unit tests if necessary. - -### Environment Info -Privide the development or test environment info. -- OS -- CPU info -- Python version -- Dependent component version - diff --git a/Makefile b/Makefile new file mode 100644 index 00000000000..462f2a9b90e --- /dev/null +++ b/Makefile @@ -0,0 +1,29 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXPROJ = ProjectnameIntelLowPrecisionOptimizationTool +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + + +html: + $(SPHINXBUILD) -b html "$(SOURCEDIR)" "$(BUILDDIR)/html" $(SPHINXOPTS) $(O) + cp _static/index.html $(BUILDDIR)/html/index.html + mkdir "$(BUILDDIR)/html/docs/imgs" + cp docs/imgs/infrastructure.png "$(BUILDDIR)/html/docs/imgs/infrastructure.png" + cp docs/imgs/workflow.png "$(BUILDDIR)/html/docs/imgs/workflow.png" + + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/README.md b/README.md index a692ff8646b..1b6e6e51f3a 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,44 @@ -Intel® Low Precision Optimization Tool -====================================== +Introduction to Intel® LPOT +=========================== -Intel® Low Precision Optimization Tool (Intel® LPOT) is an open-source Python* library that delivers a unified low-precision inference interface across multiple Intel-optimized DL frameworks on both CPUs and GPUs. It supports automatic accuracy-driven tuning strategies, along with additional objectives such as optimizing for performance, model size, and memory footprint. It also provides easy extension capability for new backends, tuning strategies, metrics, and objectives. +The Intel® Low Precision Optimization Tool (Intel® LPOT) is an open-source Python library that delivers a unified low-precision inference interface across multiple Intel-optimized Deep Learning (DL) frameworks on both CPUs and GPUs. It supports automatic accuracy-driven tuning strategies, along with additional objectives such as optimizing for performance, model size, and memory footprint. It also provides easy extension capability for new backends, tuning strategies, metrics, and objectives. > **Note** > > GPU support is under development. - - - - - - - - - -
InfrastructureWorkflow
+| Infrastructure | Workflow | +| - | - | +| ![LPOT Infrastructure](./docs/imgs/infrastructure.png "Infrastructure") | ![LPOT Workflow](./docs/imgs/workflow.png "Workflow") | -Supported Intel optimized DL frameworks are: +Supported Intel-optimized DL frameworks are: * [TensorFlow\*](https://github.com/Intel-tensorflow/tensorflow), including [1.15.0 UP2](https://github.com/Intel-tensorflow/tensorflow/tree/v1.15.0up2), [1.15.0 UP1](https://github.com/Intel-tensorflow/tensorflow/tree/v1.15.0up1), [2.1.0](https://github.com/Intel-tensorflow/tensorflow/tree/v2.1.0), [2.2.0](https://github.com/Intel-tensorflow/tensorflow/tree/v2.2.0), [2.3.0](https://github.com/Intel-tensorflow/tensorflow/tree/v2.3.0), [2.4.0](https://github.com/Intel-tensorflow/tensorflow/tree/v2.4.0) * [PyTorch\*](https://pytorch.org/), including [1.5.0+cpu](https://download.pytorch.org/whl/torch_stable.html), [1.6.0+cpu](https://download.pytorch.org/whl/torch_stable.html) * [Apache\* MXNet](https://mxnet.apache.org), including [1.6.0](https://github.com/apache/incubator-mxnet/tree/1.6.0), [1.7.0](https://github.com/apache/incubator-mxnet/tree/1.7.0) * [ONNX\* Runtime](https://github.com/microsoft/onnxruntime), including [1.6.0](https://github.com/microsoft/onnxruntime/tree/v1.6.0) +**Visit the Intel® LPOT website at: .** -# Installation +## Installation -The Intel® LPOT library is released as part of +The Intel® LPOT library is released as part of the [Intel® oneAPI AI Analytics Toolkit](https://software.intel.com/content/www/us/en/develop/tools/oneapi/ai-analytics-toolkit.html) (AI Kit). The AI Kit provides a consolidated package of Intel's latest deep learning and -machine optimizations all in one place for ease of development. Along with +machine optimizations all in one place for ease of development. Along with LPOT, the AI Kit includes Intel-optimized versions of deep learning frameworks -(such as TensorFlow and PyTorch) and high performing Python libraries to streamline -end-to-end data science and AI workflows on Intel architectures. +(such as TensorFlow and PyTorch) and high-performing Python libraries to +streamline end-to-end data science and AI workflows on Intel architectures. + -## Install for Linux +### Linux Installation You can install just the LPOT library from binary or source, or you can get -the Intel optimized framework together with the LPOT -library by installing the Intel® oneAPI AI Analytics Toolkit. +the Intel-optimized framework together with the LPOT library by installing the +Intel® oneAPI AI Analytics Toolkit. -### Install from binary +#### Install from binary - ```shell + ```Shell # install from pip pip install lpot @@ -51,20 +46,21 @@ library by installing the Intel® oneAPI AI Analytics Toolkit. conda install lpot -c conda-forge -c intel ``` -### Install from source +#### Install from source - ```shell + ```Shell git clone https://github.com/intel/lpot.git cd lpot pip install -r requirements.txt python setup.py install ``` -### Install from AI Kit + +#### Install from AI Kit The AI Kit, which includes the LPOT library, is distributed through many common channels, including from Intel's website, YUM, APT, Anaconda, and more. -[Select and download](https://software.intel.com/content/www/us/en/develop/tools/oneapi/ai-analytics-toolkit/download.html) +Select and [download](https://software.intel.com/content/www/us/en/develop/tools/oneapi/ai-analytics-toolkit/download.html) the AI Kit distribution package that's best suited for you and follow the [Get Started Guide](https://software.intel.com/content/www/us/en/develop/documentation/get-started-with-ai-linux/top.html) for post-installation instructions. @@ -72,27 +68,27 @@ for post-installation instructions. |[Download AI Kit](https://software.intel.com/content/www/us/en/develop/tools/oneapi/ai-analytics-toolkit/) |[AI Kit Get Started Guide](https://software.intel.com/content/www/us/en/develop/documentation/get-started-with-ai-linux/top.html) | |---|---| -## Install for Windows +### Windows Installation -### **Prerequisites** +**Prerequisites** -The following prerequisites and requirements must be satisfied in order to install successfully: +The following prerequisites and requirements must be satisfied for a successful installation: - Python version: 3.6 or 3.7 or 3.8 -- Download and install anaconda: [anaconda](https://anaconda.org/) +- Download and install [anaconda](https://anaconda.org/). - Create a virtual environment named lpot in anaconda: - ```shell - # Here we install python 3.7 for instance. You can also choose python 3.6 & 3.8. - conda create -n lpot python=3.7 - conda activate lpot - ``` + ```shell + # Here we install python 3.7 for instance. You can also choose python 3.6 & 3.8. + conda create -n lpot python=3.7 + conda activate lpot + ``` -### Install from binary +#### Install from binary - ```shell + ```Shell # install from pip pip install lpot @@ -100,45 +96,44 @@ The following prerequisites and requirements must be satisfied in order to insta conda install lpot -c conda-forge -c intel ``` -### Install from source +#### Install from source - ```shell - git clone https://github.com/intel/lpot.git - cd lpot - pip install -r requirements.txt - python setup.py install - ``` +```shell +git clone https://github.com/intel/lpot.git +cd lpot +pip install -r requirements.txt +python setup.py install +``` + +## Documentation -# Getting started +**Get Started** -* [Introduction](docs/introduction.md) explains Intel® Low Precision Optimization Tool's API. -* [Transform](docs/transform.md) introduces how to utilize LPOT buildin data processing and how to develop a custom data processing method. -* [Dataset](docs/dataset.md) introudces how to utilize LPOT buildin dataset and how to develop a custom dataset. -* [Metric](docs/metric.md) introduces how to utilize LPOT buildin metrics and how to develop a custom metric. -* [Tutorial](docs/tutorial.md) provides comprehensive instructions on how to utilize Intel® Low Precision Optimization Tool's features with examples. -* [Examples](examples) are provided to demonstrate the usage of Intel® Low Precision Optimization Tool in different frameworks: [TensorFlow](examples/tensorflow), [PyTorch](examples/pytorch), [MXNet](examples/mxnet) and [ONNX Runtime](examples/onnxrt). -* [UX](docs/ux.md) is a web based system to simplify Intel® Low Precision Optimization Tool usage. +* [APIs](docs/api-introduction.md) explains Intel® Low Precision Optimization Tool's API. +* [Transform](docs/transform.md) introduces how to utilize LPOT's built-in data processing and how to develop a custom data processing method. +* [Dataset](docs/dataset.md) introduces how to utilize LPOT's built-in dataset and how to develop a custom dataset. +* [Metric](docs/metric.md) introduces how to utilize LPOT's built-in metrics and how to develop a custom metric. +* [Tutorial](docs/tutorial.md) provides comprehensive instructions on how to utilize LPOT's features with examples. +* [Examples](/examples) are provided to demonstrate the usage of LPOT in different frameworks: TensorFlow, PyTorch, MXNet, and ONNX Runtime. +* [UX](docs/ux.md) is a web-based system used to simplify LPOT usage. * [Intel oneAPI AI Analytics Toolkit Get Started Guide](https://software.intel.com/content/www/us/en/develop/documentation/get-started-with-ai-linux/top.html) explains the AI Kit components, installation and configuration guides, and instructions for building and running sample apps. * [AI and Analytics Samples](https://github.com/oneapi-src/oneAPI-samples/tree/master/AI-and-Analytics) includes code samples for Intel oneAPI libraries. +**Deep Dive** -# Deep Dive - -* [Quantization](docs/Quantization.md) is the processes that enable inference and training by performing computations at low precision data type, such as fixed point integers. LPOT supports [Post-Training Quantization](docs/PTQ.md) and [Quantization-Aware Training](docs/QAT.md) +* [Quantization](docs/Quantization.md) are processes that enable inference and training by performing computations at low-precision data types, such as fixed-point integers. LPOT supports Post-Training Quantization ([PTQ](docs/PTQ.md)) and Quantization-Aware Training ([QAT](docs/QAT.md)). Note that ([Dynamic Quantization](docs/dynamic_quantization.md)) currently has limited support. * [Pruning](docs/pruning.md) provides a common method for introducing sparsity in weights and activations. * [Benchmarking](docs/benchmark.md) introduces how to utilize the benchmark interface of LPOT. * [Mixed precision](docs/mixed_precision.md) introduces how to enable mixed precision, including BFP16 and int8 and FP32, on Intel platforms during tuning. -* [Graph Optimization](docs/graph_optimization.md) introduces how to enable graph optimization for fp32 and auto-mixed precision. -* [TensorBoard](docs/tensorboard.md) provides tensor histogram and execution graph for tuning debugging purpose. - +* [Graph Optimization](docs/graph_optimization.md) introduces how to enable graph optimization for FP3232 and auto-mixed precision. +* [TensorBoard](docs/tensorboard.md) provides tensor histograms and execution graphs for tuning debugging purposes. -# Advanced Topics +**Advanced Topics** * [Adaptor](docs/adaptor.md) is the interface between LPOT and framework. The method to develop adaptor extension is introduced with ONNX Runtime as example. * [Strategy](docs/tuning_strategies.md) can automatically optimized low-precision recipes for deep learning models to achieve optimal product objectives like inference performance and memory usage with expected accuracy criteria. The method to develop a new strategy is introduced. - -# System Requirements +## System Requirements Intel® Low Precision Optimization Tool supports systems based on [Intel 64 architecture or compatible processors](https://en.wikipedia.org/wiki/X86-64), specially optimized for the following CPUs: @@ -149,7 +144,7 @@ Intel® Low Precision Optimization Tool requires installing the pertinent Intel- ### Validated Hardware/Software Environment - +
@@ -209,11 +204,11 @@ Intel® Low Precision Optimization Tool requires installing the pertinent Intel-
Platform
-# Validated Models +### Validated Models -Intel® Low Precision Optimization Tool provides numerous examples to show promising accuracy loss with the best performance gain. Below table lists some key models as showcases. Full quantized model list on various frameworks is available in [Model List](docs/full_model_list.md) +Intel® Low Precision Optimization Tool provides numerous examples to show promising accuracy loss with the best performance gain. A full quantized model list on various frameworks is available in the [Model List](docs/full_model_list.md). - +
@@ -355,7 +350,7 @@ Intel® Low Precision Optimization Tool provides numerous examples to show promi
Framework
- +
@@ -476,62 +471,10 @@ Intel® Low Precision Optimization Tool provides numerous examples to show promi
Framework
+## Additional Content -# Known Issues - -The MSE tuning strategy does not work with the PyTorch adaptor layer. This strategy requires a comparison between the FP32 and INT8 tensors to decide which op impacts the final quantization accuracy. The PyTorch adaptor layer does not implement this inspect tensor interface. Therefore, do not choose the MSE tuning strategy for PyTorch models. - -# Incompatible Changes - -[LPOT v1.2](https://github.com/intel/lpot/tree/v1.2) introduces incompatible changes in user facing APIs. Please refer to [incompatible changes](./docs/incompatible_changes.md) to know which incompatible changes are made in v1.2. - -[LPOT v1.2.1](https://github.com/intel/lpot/tree/v1.2.1) solves this backward compatible issues introduced in v1.2 by moving new user facing APIs to lpot.experimental package and keep old one as is. Please refer to [introduction](./docs/introduction.md) to know the details of user-facing APIs. - -# Support - -Submit your questions, feature requests, and bug reports to the -[GitHub issues](https://github.com/intel/lpot/issues) page. You may also reach out to lpot.maintainers@intel.com. - -# Contribution - -We welcome community contributions to Intel® Low Precision Optimization -Tool. If you have an idea on how to improve the library, refer to the following: - -* For changes impacting the public API, submit an [RFC pull request](CONTRIBUTING.md#RFC_pull_requests). -* Ensure that the changes are consistent with the [code contribution guidelines](CONTRIBUTING.md#code_contribution_guidelines) and [coding style](CONTRIBUTING.md#coding_style). -* Ensure that you can run all the examples with your patch. -* Submit a [pull request](https://github.com/intel/lpot/pulls). - -For additional details, see [contribution guidelines](CONTRIBUTING.md). - -This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](CODE_OF_CONDUCT.md) code of conduct. - -# License - -Intel® Low Precision Optimization Tool is licensed under [Apache License Version 2.0](http://www.apache.org/licenses/LICENSE-2.0). This software includes components that have separate copyright notices and licensing terms. Your use of the source code for these components is subject to the terms and conditions of the following licenses. - -Apache License Version 2.0: -* [Intel TensorFlow Quantization Tool](https://github.com/IntelAI/tools) - -MIT License: -* [bayesian-optimization](https://github.com/fmfn/BayesianOptimization) - -See the accompanying [LICENSE](LICENSE) file for full license text and copyright notices. - --------- - -View [Legal Information](legal_information.md). - -## Citation - -If you use Intel® Low Precision Optimization Tool in your research or you wish to refer to the tuning results published in the [Validated Models](#validated-models), use the following BibTeX entry. - -``` -@misc{Intel® Low Precision Optimization Tool, - author = {Feng Tian, Chuanqi Wang, Guoming Zhang, Penghui Cheng, Pengxin Yuan, Haihao Shen, and Jiong Gong}, - title = {Intel® Low Precision Optimization Tool}, - howpublished = {\url{https://github.com/intel/lpot}}, - year = {2020} -} -``` - +* [Release Information](releases_info.md) +* [Contribution Guidelines](contributions.md) +* [Legal](legal_information.md) +* [Security Policy](security_policy.md) +* [Intel® LPOT Website](https://intel.github.io/lpot) diff --git a/_static/custom.css b/_static/custom.css new file mode 100644 index 00000000000..2acb7e76f83 --- /dev/null +++ b/_static/custom.css @@ -0,0 +1,13 @@ +/* make the page 1000px */ +.wy-nav-content { + max-width: 1000px; +} + +/* code block highlight color in rtd changed to lime green, no no no */ + +.rst-content tt.literal, .rst-content code.literal, .highlight { + background: #f0f0f0; +} +.rst-content tt.literal, .rst-content code.literal { + color: #000000; +} diff --git a/_static/index.html b/_static/index.html new file mode 100644 index 00000000000..5f62e3d9bef --- /dev/null +++ b/_static/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/api-documentation/apis.rst b/api-documentation/apis.rst new file mode 100644 index 00000000000..c78c7cef76d --- /dev/null +++ b/api-documentation/apis.rst @@ -0,0 +1,20 @@ +APIs +#### + +.. automodule:: lpot.benchmark + :members: + +.. autoclass:: lpot.benchmark.Benchmark + :members: + +.. automodule:: lpot.objective + :members: + +.. automodule:: lpot.pruning + :members: + +.. automodule:: lpot.quantization + :members: + +.. automodule:: lpot.version + :members: \ No newline at end of file diff --git a/conf.py b/conf.py new file mode 100644 index 00000000000..9fb1709fab7 --- /dev/null +++ b/conf.py @@ -0,0 +1,184 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys +sys.path.insert(0, os.path.abspath('.')) +import importlib.util +moduleName = 'version' +modulePath = os.getcwd() + '/lpot/version.py' +spec = importlib.util.spec_from_file_location(moduleName,modulePath) +LPOTversion = importlib.util.module_from_spec(spec) +spec.loader.exec_module(LPOTversion) + + +# -- Project information ----------------------------------------------------- + +project = 'Intel® Low Precision Optimization Tool' +copyright = '2021, Intel® Low Precision Optimization Tool' +author = 'Intel® LPOT developers' + +# The short X.Y version +version = LPOTversion.__version__ +# The full version, including alpha/beta/rc tags +release = '' + + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ["recommonmark","sphinx_markdown_tables","sphinx_md", "sphinx.ext.autodoc"] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = ['.rst', '.md'] + +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path . +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'examples','legacy/seq2seq*'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +# html_theme = "asteroid_sphinx_theme" +# html_theme = "classic" +# html_theme = "alabaster" +# html_theme = 'sphinx_book_theme' +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = 'ProjectnameIntelLowPrecisionOptimizationTooldoc' + + +# -- Options for LaTeX output ------------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'ProjectnameIntelLowPrecisionOptimizationTool.tex', '\\textgreater{} Project name: Intel® Low Precision Optimization Tool Documentation', + 'Various', 'manual'), +] + + +# -- Options for manual page output ------------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'projectnameintellowprecisionoptimizationtool', '> Project name: Intel® Low Precision Optimization Tool Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ---------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'ProjectnameIntelLowPrecisionOptimizationTool', '> Project name: Intel® Low Precision Optimization Tool Documentation', + author, 'ProjectnameIntelLowPrecisionOptimizationTool', 'One line description of project.', + 'Miscellaneous'), +] + +def setup(app): + app.add_stylesheet("custom.css") + +from os import getenv + +sphinx_md_useGitHubURL = True +baseBranch = "master" +commitSHA = getenv('GITHUB_SHA') +githubBaseURL = 'https://github.com/' + (getenv('GITHUB_REPOSITORY') or 'intel/lpot') + '/' +githubFileURL = githubBaseURL + "blob/" +githubDirURL = githubBaseURL + "tree/" +if commitSHA: + githubFileURL = githubFileURL + commitSHA + "/" + githubDirURL = githubDirURL + commitSHA + "/" +else: + githubFileURL = githubFileURL + baseBranch + "/" + githubDirURL = githubDirURL + baseBranch + "/" +sphinx_md_githubFileURL = githubFileURL +sphinx_md_githubDirURL = githubDirURL \ No newline at end of file diff --git a/contributions.md b/contributions.md new file mode 100644 index 00000000000..270857251d6 --- /dev/null +++ b/contributions.md @@ -0,0 +1,127 @@ +Contribution Guidelines +======================= + +If you have improvements to Intel® Low Precision Optimization Tool, send your pull requests for +[review](https://github.com/intel/lpot/pulls). If you are new to Github, view the pull request +[How To](https://help.github.com/articles/using-pull-requests/). + + +## Pull Request Checklist +Before sending your pull requests, follow the information below: + +- Changes are consistent with the Python [Coding Style](https://github.com/google/styleguide/blob/gh-pages/pyguide.md). +- Use pylint to check your Python code. +- Use flake8 and autopep8 to make Python code clean. +- Add unit tests in [Unit Tests](https://github.com/intel/lpot/tree/master/test) to cover the code you would like to contribute. +- Run [Unit Tests](https://github.com/intel/lpot/tree/master/test). + +## Pull Request Template + +**Change Summary** + +Include a detailed summary of the change. + +**Change Motivation** + +Include an explanation for the change. + +**Change Limit** + +Include an explanation about the regression your change might bring. + +**Test Info** +- For bug fixes, provide test steps to reproduce your issue. +- For new features, provide test steps besides unit tests if necessary. + +**Environment Info** +Provide the development or test environment info. +- OS +- CPU info +- Python version +- Dependent component version + +## Support + +Submit your questions, feature requests, and bug reports to the +[GitHub issues](https://github.com/intel/lpot/issues) page. You may also reach out to lpot.maintainers@intel.com. + +## Contributor Covenant Code of Conduct + +This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct. + +### Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +### Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +### Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +### Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +### Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project maintainers at mlp.mlpc.dl@intel.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +### Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], [version 1.4][ver1.4]. + +[homepage]: https://www.contributor-covenant.org + +[ver1.4]: https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +For answers to common questions about this code of conduct, see the [FAQ][FAQ-page] page. + +[FAQ-page]: https://www.contributor-covenant.org/faq diff --git a/docs/PTQ.md b/docs/PTQ.md index 65984add3ab..6fcdf4cea64 100644 --- a/docs/PTQ.md +++ b/docs/PTQ.md @@ -2,9 +2,10 @@ ## Design -Post-training static quantization (PTQ) involves not just converting the weights from **float** to **int**, but also first feeding batches of data through the network and computing the resulting distributions of the different activations (specifically, this is done by inserting observer modules at different points that record this data). These distributions are then used to determine specifically how the different activations should be quantized at inference time (a simple technique would be to simply divide the entire range of activations into 256 levels, but we support more sophisticated methods as well). This additional step allows us to pass quantized values between operations instead of converting these values to floats - and then back to ints - between every operation, resulting in a significant speed-up. +Post-training static quantization (PTQ) involves not just converting the weights from **float** to **int**, but also first feeding batches of data through the network and computing the resulting distributions of the different activations. Sspecifically, this is done by inserting observer modules at different points that record this data. These distributions are then used to determine specifically how the different activations should be quantized at inference time. A simple technique would be to simply divide the entire range of activations into 256 levels, but we support more sophisticated methods as well. This additional step allows us to pass quantized values between operations instead of converting these values to floats - and then back to ints - between every operation, resulting in a significant speed-up. ## PyTorch Usage + ### MobileNetV2 Model Architecture Define the pre-trained MobileNetV2 model architecture. The following @@ -355,6 +356,7 @@ QConfig(activation=functools.partial( NOTE: Dynamic Quantization currently is only supported with onnxruntime backend. +> **Note** +> +> Dynamic Quantization currently only supports the onnxruntime backend. diff --git a/docs/adaptor.md b/docs/adaptor.md index c1b749b0095..9d49f8f1441 100644 --- a/docs/adaptor.md +++ b/docs/adaptor.md @@ -1,18 +1,21 @@ Adaptor -================= +======= ## Introduction -Intel® Low Precision Optimization Tool built the low-precision inference solution upon popular Deep Learning frameworks -such as TensorFlow, PyTorch, MXNet and ONNX Runtime. The adaptor layer is the bridge between LPOT tuning strategy and -framework vanilla quantization APIs. +Intel® Low Precision Optimization Tool (LPOT) built the low-precision inference +solution on popular Deep Learning frameworks such as TensorFlow, PyTorch, +MXNet, and ONNX Runtime. The adaptor layer is the bridge between the LPOT +tuning strategy and vanilla framework quantization APIs. ## Adaptor Design -Intel® Low Precision Optimization Tool supports new adaptor extension by implementing a subclass of `Adaptor` class in lpot.adaptor package - and registering this strategy by `adaptor_registry` decorator. +LPOT supports a new adaptor extension by +implementing a subclass `Adaptor` class in the lpot.adaptor package +and registering this strategy by the `adaptor_registry` decorator. + +For example, a user can implement an `Abc` adaptor like below: -for example, user can implement an `Abc` adaptor like below: ```python @adaptor_registry class AbcAdaptor(Adaptor): @@ -33,30 +36,34 @@ class AbcAdaptor(Adaptor): ... ``` -`quantize` function is used to do calibration and quantization in post-training quantization. -`evaluate` function is used to run evaluation on validation dataset. -`query_fw_capability` function is used to run query framework quantization capability and intersects with user yaml configuration setting to -`query_fused_patterns` function is used to run query framework graph fusion capability and decide the fusion tuning space. +* `quantize` function is used to perform calibration and quantization in post-training quantization. +* `evaluate` function is used to run an evaluation on a validation dataset. +* `query_fw_capability` function is used to run a query framework quantization capability and intersects with the user yaml configuration. +* `query_fused_patterns` function is used to run a query framework graph fusion capability and decide the fusion tuning space. ### Query API -#### **Background** -Besides the adaptor API, we also introduced the Query API which describes the behavior of the specific framework. -With this API, the LPOT is easy to query below information of the current runtime framework. -* The runtime version information; -* The Quantizable ops' type; -* The supported sequence of each quantizable op; +#### Background + +Besides the adaptor API, we also introduced the Query API which describes the +behavior of a specific framework. With this API, LPOT can easily query the +following information on the current runtime framework. + +* The runtime version information. +* The Quantizable ops type. +* The supported sequence of each quantizable op. * The instance of each sequence. -In the past, the above information were generally defined and hidden in every corner of the code and make it hard to maintain effectively. With the Query API, we only need to create one unify yaml file and call corresponding API to get the information, e.g, the [tensorflow.yaml](../lpot/adaptor/tensorflow.yaml) keeps the current Tensorflow framework ability. Theoretically, we don't recommend the end user make any modification if the requirement is not clearly. +In the past, the above information was generally defined and hidden in every corner of the code which made effective maintenance difficult. With the Query API, we only need to create one unified yaml file and call the corresponding API to get the information. For example, the [tensorflow.yaml](../lpot/adaptor/tensorflow.yaml) keeps the current Tensorflow framework ability. We recommend that the end user not make modifications if requirements are not clear. + +#### Unify Config Introduction -#### **Unify Config Introduction** Below is a fragment of the Tensorflow configuration file. * **precisions** field defines the supported precision for LPOT. - - valid_mixed_precision enumerate all supported precision combinations for specific scenario. e.g, if one hardware doesn't support bf16, it should be `int8 + fp32`. + - valid_mixed_precision enumerates all supported precision combinations for specific scenario. For example, if one hardware doesn't support bf16, it should be `int8 + fp32`. * **ops** field defines the valid OP type list for each precision. -* **capabilities** field focus on the quantization ability of specific op, like granularity, scheme and algorithm. Note, the activation here means the input activation of the op rather than its output. +* **capabilities** field focuses on the quantization ability of specific ops such as granularity, scheme, and algorithm. Note that the activation here means the input activation of the op rather than its output. * **patterns** field defines the supported fusion sequence of each op. ```yaml @@ -184,29 +191,30 @@ Below is a fragment of the Tensorflow configuration file. 'MatMul + BiasAdd', ] ``` -#### **Query API Introduction** -We defines the abstract class `QueryBackendCapability` in [query.py](../lpot/adaptor/query.py#L21). Each framework should inherit it and implement the member function if needed. You may refer to Tensorflow implementation [TensorflowQuery](../lpot/adaptor/tensorflow.py#L628) +#### Query API Introduction +The abstract class `QueryBackendCapability` is defined in [query.py](../lpot/adaptor/query.py#L21). Each framework should inherit it and implement the member function if needed. Refer to Tensorflow implementation [TensorflowQuery](../lpot/adaptor/tensorflow.py#L628). -Customize a New Framework Backend -================= -Let us take onnxruntime as an example. ONNX Runtime is a backend proposed by Microsoft, and it's based on MLAS kernel by default. -Onnxruntime already has [quantization tools](https://github.com/microsoft/onnxruntime/tree/master/onnxruntime/python/tools/quantization), so the question becomes how to integrate onnxruntime quantization tools into LPOT. -1. capability +## Customize a New Framework Backend + +Look at onnxruntime as an example. ONNX Runtime is a backend proposed by Microsoft, and is based on the MLAS kernel by default. +Onnxruntime already has [quantization tools](https://github.com/microsoft/onnxruntime/tree/master/onnxruntime/python/tools/quantization), so the question becomes how to integrate onnxruntime quantization tools into LPOT. + +1. Capability - User should explore quantization capability at first. According to [onnx_quantizer](https://github.com/microsoft/onnxruntime/blob/503b61d897074a494f5798069308ee67d8fb9ace/onnxruntime/python/tools/quantization/onnx_quantizer.py#L77), the quantization tools support following attributes: - 1.1 whether per_channel - 1.2 whether reduce_range - 1.3 QLinear mode or Integer mode (which is only seen in onnxruntime) - 1.4 whether static (static quantization or dynamic quantization) - 1.4 weight_qtype (choices are float32, int8 and uint8) - 1.5 input_qtype (choices are float32, int8 and uint8) - 1.6 quantization_params (None if dynamic quantization) - 1.7 &1.8 nodes_to_quantize, nodes_to_exclude - 1.9 op_types_to_quantize - - so we can pass a tune capability to LPOT like + The user should explore quantization capability first. According to [onnx_quantizer](https://github.com/microsoft/onnxruntime/blob/503b61d897074a494f5798069308ee67d8fb9ace/onnxruntime/python/tools/quantization/onnx_quantizer.py#L77), the quantization tools support the following attributes: + * whether per_channel + * whether reduce_range + * QLinear mode or Integer mode (which is only seen in onnxruntime) + * whether static (static quantization or dynamic quantization) + * weight_qtype (choices are float32, int8 and uint8) + * input_qtype (choices are float32, int8 and uint8) + * quantization_params (None if dynamic quantization) + * &1.8 nodes_to_quantize, nodes_to_exclude + * op_types_to_quantize + + We can pass a tune capability to LPOT such as: ```yaml {'optypewise': {'conv': @@ -233,9 +241,11 @@ Onnxruntime already has [quantization tools](https://github.com/microsoft/onnxr } ``` -2. parse tune config +2. Parse tune config - LPOT will generate a tune config from your tune capability like + LPOT can generate a tune config from your tune capability such as the + following: + ```yaml { 'fuse': {'int8': [['CONV2D', 'RELU', 'BN'], ['CONV2D', 'RELU']], @@ -266,13 +276,14 @@ Onnxruntime already has [quantization tools](https://github.com/microsoft/onnxr } } ``` - then you can parse this config into format that ONNXQuantizer can accept - please make sure whether your quantization API support model wise or op wise quantization. for example, node "conv1" use "minmax" algorithm and node "conv2" use "KL" algorithm, or the whole model must use "minmax" or "KL" in general. + Then you can parse this config into a format that ONNXQuantizer can accept. + Verify whether your quantization API supports model wise or op wise quantization. For example, node "conv1" uses the "minmax" algorithm and node "conv2" uses the "KL" algorithm, or the whole model must use "minmax" or "KL" in general. -3. pre-optimize - if your backend support FP32 graph optimization, you can apply it in **query_fw_capability** and quantize your optimized fp32 model instead of original model +3. Pre-optimize + If your backend supports FP32 graph optimization, you can apply it in **query_fw_capability** and quantize your optimized fp32 model instead of + the original model: >model = self.pre_optimized_model if self.pre_optimized_model else model -4. do quantization +4. Do quantization - This part depend on your backend implementations you may refer to [onnxruntime](../lpot/adaptor/onnxrt.py) as an example. + This part depends on your backend implementations. Refer to [onnxruntime](../lpot/adaptor/onnxrt.py) as an example. diff --git a/docs/api-introduction.md b/docs/api-introduction.md new file mode 100644 index 00000000000..4ec2750ffab --- /dev/null +++ b/docs/api-introduction.md @@ -0,0 +1,210 @@ +API Documentation +================= + +## Introduction + +Intel® Low Precision Optimization Tool is an open-source Python library designed to help users quickly deploy low-precision inference solutions on popular deep learning (DL) frameworks such as TensorFlow*, PyTorch*, MXNet, and ONNX Runtime. It automatically optimizes low-precision recipes for deep learning models in order to achieve optimal product objectives, such as inference performance and memory usage, with expected accuracy criteria. + + +## User-facing APIs + +These APIs are intended to unify low-precision quantization interfaces cross multiple DL frameworks for the best out-of-the-box experiences. + +> **Note** +> +> LPOT is continuously improving user-facing APIs to create a better user experience. + +> Two sets of user-facing APIs exist. One is the default one supported from LPOT v1.0 for backwards compatibility. The other set consists of new APIs in +the `lpot.experimental` package. + +> We recommend that you use the APIs located in lpot.experimental. All examples have been updated to use the experimental APIs. + +The major differences between the default use-facing APIs and the experimental APIs are: + +1. The experimental APIs abstract the `lpot.experimental.common.Model` concept to cover those cases whose weight and graph files are stored separately. +2. The experimental APIs unifiy the calling style of the `Quantization`, `Pruning`, and `Benchmark` classes by setting model, calibration dataloader, evaluation dataloader, and metric through class attributes rather than passing them as function inputs. +3. The experimental APIs refine LPOT built-in transforms/datasets/metrics by unifying the APIs cross different framework backends. + +## Experimental user-facing APIs + +Experimental user-facing APIs consist of the following components: + +### Quantization-related APIs + +```python +# lpot.experimental.Quantization +class Quantization(object): + def __init__(self, conf_fname): + ... + + def __call__(self): + ... + + @property + def calib_dataloader(self): + ... + + @property + def eval_dataloader(self): + ... + + @property + def model(self): + ... + + @property + def metric(self): + ... + + @property + def postprocess(self, user_postprocess): + ... + + @property + def q_func(self): + ... + + @property + def eval_func(self): + ... + +``` +The `conf_fname` parameter used in the class initialization is the path to the user yaml configuration file. This yaml file is used to control the entire tuning behavior on the model. + +**LPOT User YAML Syntax** + +> Intel® Low Precision Optimization Tool provides template yaml files for [Post-Training Quantization](../lpot/template/ptq.yaml), [Quantization-Aware Training](../lpot/template/qat.yaml), and [Pruning](../lpot/template/pruning.yaml) scenarios. Refer to these template files to understand the meaning of each field. + +> Note that most fields in the yaml templates are optional. View the [HelloWorld Yaml](../examples/helloworld/tf_example2/conf.yaml) example for reference. + +```python +# Typical Launcher code +from lpot.experimental import Quantization, common + +# optional if LPOT built-in dataset could be used as model input in yaml +class dataset(object): + def __init__(self, *args): + ... + + def __getitem__(self, idx): + # return single sample and label tuple without collate. label should be 0 for label-free case + ... + + def len(self): + ... + +# optional if LPOT built-in metric could be used to do accuracy evaluation on model output in yaml +class custom_metric(object): + def __init__(self): + ... + + def update(self, predict, label): + # metric update per mini-batch + ... + + def result(self): + # final metric calculation invoked only once after all mini-batch are evaluated + # return a scalar to lpot for accuracy-driven tuning. + # by default the scalar is higher-is-better. if not, set tuning.accuracy_criterion.higher_is_better to false in yaml. + ... + +quantizer = Quantization(conf.yaml) +quantizer.model = common.Model('/path/to/model') +# below two lines are optional if LPOT built-in dataset is used as model calibration input in yaml +cal_dl = dataset('/path/to/calibration/dataset') +quantizer.calib_dataloader = common.DataLoader(cal_dl, batch_size=32) +# below two lines are optional if LPOT built-in dataset is used as model evaluation input in yaml +dl = dataset('/path/to/evaluation/dataset') +quantizer.eval_dataloader = common.DataLoader(dl, batch_size=32) +# optional if LPOT built-in metric could be used to do accuracy evaluation in yaml +quantizer.metric = common.Metric(custom_metric) +q_model = quantizer() +q_model.save('/path/to/output/dir') +``` + +`model` attribute in `Quantization` class is an abstraction of model formats across different frameworks. LPOT supports passing the path of `keras model`, `frozen pb`, `checkpoint`, `saved model`, `torch.nn.model`, `mxnet.symbol.Symbol`, `gluon.HybirdBlock`, and `onnx model` to instantiate a `lpot.experimental.common.Model()` class and set to `quantizer.model`. + +`calib_dataloader` and `eval_dataloader` attribute in `Quantization` class is used to set up a calibration dataloader by code. It is optional to set if the user sets corresponding fields in yaml. + +`metric` attribute in `Quantization` class is used to set up a custom metric by code. It is optional to set if user finds LPOT built-in metric could be used with their model and sets corresponding fields in yaml. + +`postprocess` attribute in `Quantization` class is not necessary in most of the use cases. It is only needed when the user wants to use the LPOT built-in metric but the model output can not directly be handled by LPOT built-in metrics. In this case, the user can register a transform to convert the model output to the expected one required by the LPOT built-in metric. + +`q_func` attribute in `Quantization` class is only for `Quantization Aware Training` case, in which the user needs to register a function that takes `model` as the input parameter and executes the entire training process with self-contained training hyper-parameters. + +`eval_func` attribute in `Quantization` class is reserved for special cases. If the user had an evaluation function when train a model, the user must implement a `calib_dataloader` and leave `eval_dataloader` as None. Then, modify this evaluation function to take `model` as the input parameter and return a higher-is-better scaler. In some scenarios, it may reduce development effort. + + +### Pruning-related APIs (POC) + +```python +class Pruning(object): + def __init__(self, conf_fname): + ... + + def on_epoch_begin(self, epoch): + ... + + def on_batch_begin(self, batch_id): + ... + + def on_batch_end(self): + ... + + def on_epoch_end(self): + ... + + def __call__(self): + ... + + @property + def model(self): + ... + + @property + def q_func(self): + ... + +``` + +This API is used to do sparsity pruning. Currently, it is a Proof of Concept; LPOT only supports `magnitude pruning` on PyTorch. + +To learn how to use this API, refer to the [pruning document](../docs/pruning.md). + +### Benchmarking-related APIs +```python +class Benchmark(object): + def __init__(self, conf_fname): + ... + + def __call__(self): + ... + + @property + def model(self): + ... + + @property + def metric(self): + ... + + @property + def b_dataloader(self): + ... + + @property + def postprocess(self, user_postprocess): + ... +``` + +This API is used to measure model performance and accuracy. + +To learn how to use this API, refer to the [benchmarking document](../docs/benchmark.md). + +## Default user-facing APIs + +The default user-facing APIs exist for backwards compatiblity from the v1.0 release. Refer to [v1.1 API](https://github.com/intel/lpot/blob/v1.1/docs/introduction.md) to understand how the default user-facing APIs work. + +View the [HelloWorld example](/examples/helloworld/tf_example6) that uses default user-facing APIs for user reference. + +Full examples using default user-facing APIs can be found [here](https://github.com/intel/lpot/tree/v1.1/examples). diff --git a/docs/backend_quant.md b/docs/backend_quant.md index 84a679b69ff..72f924623c9 100644 --- a/docs/backend_quant.md +++ b/docs/backend_quant.md @@ -1,4 +1,7 @@ -### Quantization Support Matrix +Quantization Support Matrix +=========================== + +This document provides a quantization support matrix for the following frameworks listed below: | Framework | Backend Library | Symmetric Quantization | Asymmetric Quantization | | :-------------- |:---------------:| ---------------:|---------------:| @@ -8,35 +11,35 @@ | MXNet | [oneDNN](https://github.com/oneapi-src/oneDNN) | Activation (int8/uint8), Weight (int8) | - | | ONNX Runtime | [MLAS](https://github.com/microsoft/onnxruntime/tree/master/onnxruntime/core/mlas) | Weight (int8) | Activation (uint8) | -#### TensorFlow +### TensorFlow + Symmetric Quantization + int8: scale = 2 * max(abs(rmin), abs(rmax)) / (max(int8) - min(int8) - 1) + uint8: scale = max(rmin, rmax) / (max(uint8) - min(uint8)) -#### PyTorch +### PyTorch + Symmetric Quantization + int8: scale = max(abs(rmin), abs(rmax)) / (float(max(int8) - min(int8)) / 2) + uint8: scale = max(abs(rmin), abs(rmax)) / (float(max(int8) - min(int8)) / 2) + Asymmetric Quantization + uint8: scale = (rmax - rmin) / (max(uint8) - min(uint8)); zero_point = min(uint8) - round(rmin / scale) -#### PyTorch IPEX +### PyTorch IPEX + Symmetric Quantization + int8: scale = 2 * max(abs(rmin), abs(rmax)) / (max(int8) - min(int8) - 1) + uint8: scale = max(rmin, rmax) / (max(uint8) - min(uint8)) -#### MXNet +### MXNet + Symmetric Quantization + int8: scale = 2 * max(abs(rmin), abs(rmax)) / (max(int8) - min(int8) - 1) + uint8: scale = max(rmin, rmax) / (max(uint8) - min(uint8)) -#### ONNX Runtime +### ONNX Runtime + Symmetric Quantization + int8: scale = 2 * max(abs(rmin), abs(rmax)) / (max(int8) - min(int8) - 1) + Asymmetric Quantization + uint8: scale = (rmax - rmin) / (max(uint8) - min(uint8)); zero_point = min(uint8) - round(rmin / scale) -#### Reference +### Reference + oneDNN: [Lower Numerical Precision Deep Learning Inference and Training](https://software.intel.com/content/www/us/en/develop/articles/lower-numerical-precision-deep-learning-inference-and-training.html) + FBGEMM: [FBGEMM Quantization](https://github.com/pytorch/pytorch/blob/master/torch/quantization/observer.py) + MLAS: [MLAS Quantization](https://github.com/microsoft/onnxruntime/blob/master/onnxruntime/python/tools/quantization/onnx_quantizer.py) diff --git a/docs/benchmark.md b/docs/benchmark.md index 4ec6269172c..f5283e3ae44 100644 --- a/docs/benchmark.md +++ b/docs/benchmark.md @@ -1,10 +1,11 @@ Benchmarking -=============== +============ -Benchmarking feature of LPOT is used to measure the model performance with the objective settings, user can get the performance of the models between float32 model and quantized low precision model in same scenarios that they configured in yaml. Benchmarking is always used after a quantization process. +The benchmarking feature of LPOT is used to measure the model performance with the objective settings; the user can get the performance of the models between the float32 model and the quantized low precision model in the same scenarios that they configured in Yaml. Benchmarking is always used after a quantization process. -# how to use it -## config evaluation filed in yaml file +The following examples show how to use benchmarking. + +## Config evaluation filed in a yaml file ```yaml evaluation: # optional. required if user doesn't provide eval_func in lpot.Quantization. @@ -44,11 +45,11 @@ evaluation: # optional. required if use mean: [0.485, 0.456, 0.406] ``` -in this example config you can see there is 2 sub-fields named 'accuracy' and 'performance', benchmark module will get the accuracy and performance of the model. User can also remove the performance field to only get accuracy of the model or the opposite. It's flexible to configure the benchmark you want. +The above example config two sub-fields named 'accuracy' and 'performance' which indicates that the benchmark module will get the accuracy and performance of the model. The user can also remove the performance field to only get model accuracy or performance. It's flexible enough to configure the benchmark you want. -## use user specific dataloader to run benchmark +## Use a user-specific dataloader to run benchmark -In this case, you should config your dataloader and lpot will construct an evaluation function to run the benchmarking. User can also register postprocess transform and metric to get the accuracy. +In this case, configure your dataloader and LPOT will construct an evaluation function to run the benchmarking. The user can also register the postprocess transform and metric to get the accuracy. ```python dataset = Dataset() # dataset class that implement __getitem__ method or __iter__ method @@ -64,5 +65,5 @@ results = evaluator() ### Examples -[Benchamrk example](../examples/tensorflow/image_recognition/run_benchmark.sh). +Refer to the [Benchmark example](../examples/tensorflow/image_recognition/run_benchmark.sh). diff --git a/docs/dataloader.md b/docs/dataloader.md index 299a415358e..0465375a997 100644 --- a/docs/dataloader.md +++ b/docs/dataloader.md @@ -1,30 +1,31 @@ DataLoader -========================================= +========== -Deep Learning has been encountering larger and larger datasets which are so memory consuming. Before, working with large datasets requires loading them into memory all at once. It is impossible due to the lack of memory, we must figure out an efficient data generation scheme. This is not only about handle the lack of memory in large datasets, also about make the process of loading data faster enough using multi processing/thread. We call the data generation object as 'DataLoader'. +Deep Learning often encounters large datasets that are memory-consuming. Previously, working with large datasets required loading them into memory all at once. The constant lack of memory resulted in the need for an efficient data generation scheme. This is not only about handling the lack of memory in large datasets, but also about making the process of loading data faster using a multi-processing thread. We call the data generation object a DataLoader. -With the importance of DataLoader, different framework have their own DataLoadermodule, as for Intel® Low Precision Optimization Tool, it needs to calibrate the inputs/outputs of each layer of the model, framework specific DataLoader has different features and API that will make it hard to use them same way in the tool. Another request is, the tool also treat batch size as a tuning parameter, that means the tool can dynamically change the batch size to get accuracy target. The third reason is for easy of use, an unified DataLoader API can make it easy to config dataloader in yaml file without any code modification. Considering about all these advantages the tool has implemented an internal DataLoader. +With the importance of a dataloader, different frameworks can have their own DataLoadermodule. As for LPOT, it needs to calibrate the inputs/outputs of each layer of the model; the framework-specific dataloader has different features and APIs that will make it hard to use them same way in the tool. Another request is that the tool also treat batch size as a tuning parameter which means the tool can dynamically change the batch size to get the accuracy target. The third reason is for ease of use; a unified DataLoader API can make it easy to config dataloader in a yaml file without any code modification. Considering about all these advantages, the tool has implemented an internal dataloader. -DataLoader takes dataset as input parameter and loads data from dataset when needed. +The dataloader takes a dataset as the input parameter and loads data from the dataset when needed. -Dataset is a container which holds all data that should be used by dataloader, and have the ability to be fetched by index or created as an iterator. One can implement a specific Dataset by inhereting from class Dataset with implementing `__iter__` method or `__getitem__` method, while implementing `__getitem__` method, `__len__` method is recommended. +A dataset is a container which holds all data that can be used by the dataloader, and have the ability to be fetched by index or created as an iterator. One can implement a specific dataset by inhereting from the Dataset class by implementing `__iter__` method or `__getitem__` method, while implementing `__getitem__` method, `__len__` method is recommended. -Dataset use Transform as its data process component, Transform contains 3 different part, aimng at different part of the life cycle of data processing, it is: +A dataset uses transform as its data process component. Transform contains three parts, aiming at different parts of the life cycle of data processing: - 1. preprocessing +* preprocessing - 2. postprocessing +* postprocessing - 3. general +* general -General Transform can be used in both preprocessing and postprocessing, one can also implement a specific transform by inheriting from class Transform with implementing `__call__` method. Usually, DataLoader will use Transform for preprocessing and postprocessing transform is used to give right processed data to metric to update. Transforms also support to compose together to be one and serially implement the transforms. +A general transform can be used in both preprocessing and postprocessing; one can also implement a specific transform by inheriting from the Transform class by implementing the `__call__` method. Usually, a dataloader will use the transform for preprocessing and the postprocessing transform is used to give the right processed data to the metric to update. Transforms also compose together to be one and serially implement the transforms. -Transform for preprocessing will be launched in Dataset `__getitem__` or `__next__` method, that means transform is used after dataloader has loaded batched data and before the data given to model for inference. That helps reduce the memory compared with load and process all data at once. Transform for postprocessing is used in evaluation function of internal lpot to process the inferenced data and the processed data is used by metric. +Transform for preprocessing will be launched in the dataset `__getitem__` or `__next__` method; that means the transform is used after the dataloader has loaded batched data and before the data is given to the model for inference. That helps reduce the memory compared with load and process all data at once. Transform for postprocessing is used in evaluation function of the internal LPOT to process the inferenced data and the processed data is used by metric. # How to use it -## Config dataloader in yaml file -In this case dataloader will created after the Quantization object initialized. As calibration and evaluation may have different Transform and dataset, you can config different dataloader in yaml file. +## Config dataloader in a yaml file + +In this case, the dataloader is created after the Quantization object is initialized. As calibrations and evaluations may have different transforms and datasets, you can config different dataloaders in a yaml file. ```yaml quantization: # optional. tuning constraints on model-wise for advance user to reduce tuning space. @@ -81,7 +82,7 @@ evaluation: # optional. required if use std: [0.229, 0.224, 0.225] ``` -## Create user specific dataloader +## Create a user-specific dataloader ```python calib_data = mx.io.ImageRecordIter(path_imgrec=dataset, diff --git a/docs/dataset.md b/docs/dataset.md index 300849e6853..e8f574c73de 100644 --- a/docs/dataset.md +++ b/docs/dataset.md @@ -1,11 +1,11 @@ Dataset -================== +======= -User can use LPOT builtin datasets as well as register their own datasets. +Users can use LPOT built-in dataset objects as well as register their own datasets. -## Builtin dataset support list +## Built-in dataset support list -LPOT supports builtin dataloader on popular industry dataset. Please refer to 'examples/helloworld/tf_example1' about how to config a builtin dataloader. +LPOT supports built-in dataloaders on popular industry datasets. Refer to this [HelloWorld example](/examples/helloworld/tf_example6) to learn how to configure a built-in dataloader. #### TensorFlow @@ -25,6 +25,7 @@ LPOT supports builtin dataloader on popular industry dataset. Please refer to 'e | TFRecordDataset(root, transform, filter) | **root** (str): filename of dataset
**transform** (transform object, default=None): transform to process input data
**filter** (Filter objects, default=None): filter out examples according to specific conditions |Root is a full path to tfrecord file, which contains the file name. | **In yaml file:**
dataset:
   TFRecordDataset:
     root: /path/to/tfrecord
**In user code:**
from lpot.experimental.data import DATASETS
datasets = DATASETS(framework)
dataset = datasets['TFRecordDataset'] (root, transform=transform) | | bert(root, label_file, task, transform, filter) | **root** (str): path of dataset
**label_file** (str): path of label file
**task** (str, default='squad'): task type of model
**model_type** (str, default='bert'): model type, support 'bert'.
**transform** (transform object, default=None): transform to process input data
**filter** (Filter objects, default=None): filter out examples according to specific conditions | This dataset supports tfrecord data, please refer to [Guide](../examples/tensorflow/nlp/bert/README.md) to create tfrecord file first. | **In yaml file:**
dataset:
   bert:
     root: /path/to/root
     label_file: /path/to/label_file
     task: squad
     model_type: bert
**In user code:**
from lpot.experimental.data import DATASETS
datasets = DATASETS(framework)
dataset = datasets['bert'] (root, label_file, transform=transform) | + #### PyTorch | Dataset | Parameters | Comments | Usage | @@ -66,9 +67,9 @@ LPOT supports builtin dataloader on popular industry dataset. Please refer to 'e | dummy(shape, low, high, dtype, label, transform, filter) | **shape** (list or tuple):support create multi shape tensors, use list of tuples for each tuple in the list, will create a such size tensor.
**low** (list or float, default=-128.):low out the tensor value range from[0, 1] to [0, low] or [low, 0] if low < 0, if float, will implement all tensors with same low value.
**high** (list or float, default=127.):high the tensor value by add all tensor element value high. If list, length of list should be same with shape list
**dtype** (list or str, default='float32'):support multi tensor dtype setting. If list, length of list should be same with shape list, if str, all tensors will use same dtype. dtype support 'float32', 'float16', 'uint8', 'int8', 'int32', 'int64', 'bool'
**label** (bool, default=False):whether to return 0 as label
**transform** (transform object, default=None): dummy dataset does not need transform. If transform is not None, it will ignore it.
**filter** (Filter objects, default=None): filter out examples according to specific conditions | This dataset is to construct a dataset from a specific shape, the value range is calculated from: low * stand_normal(0, 1) + high. | **In yaml file:**
dataset:
   dummy:
     shape: [3, 224, 224, 3]
     low: 0.0
     high: 127.0
     dtype: float32
     label: False
**In user code:**
from lpot.experimental.data import DATASETS
datasets = DATASETS(framework)
dataset = datasets['dummy'] (shape, low, high, dtype, label, transform=None, filter=None) | -## User specific dataset +## User-specific dataset -User can register their own dataset as follows: +Users can register their own datasets as follows: ```python class Dataset(object): @@ -84,7 +85,7 @@ class Dataset(object): ``` -After defining the dataset class, user can pass it to quantizer. +After defining the dataset class, pass it to the quantizer: ```python from lpot import Quantization, common diff --git a/docs/doclist.rst b/docs/doclist.rst new file mode 100644 index 00000000000..185d184e396 --- /dev/null +++ b/docs/doclist.rst @@ -0,0 +1,66 @@ +Developer Documentation +####################### + +Read the following material as you learn how to use LPOT. + +Get Started +=========== + +* `Transform `__ introduces how to utilize LPOT's built-in data processing and how to develop a custom data processing method. +* `Dataset `__ introduces how to utilize LPOT's built-in dataset and how to develop a custom dataset. +* `Metrics `__ introduces how to utilize LPOT's built-in metrics and how to develop a custom metric. +* `UX `__ is a web-based system used to simplify LPOT usage. +* `Intel oneAPI AI Analytics Toolkit Get Started Guide `__ explains the AI Kit components, installation and configuration guides, and instructions for building and running sample apps. +* `AI and Analytics Samples `__ includes code samples for Intel oneAPI libraries. + +.. toctree:: + :maxdepth: 1 + :hidden: + + transform.md + dataset.md + metric.md + ux.md + Intel oneAPI AI Analytics Toolkit Get Started Guide + AI and Analytics Samples + + +Deep Dive +========= + +* `Quantization `__ are processes that enable inference and training by performing computations at low-precision data types, such as fixed-point integers. LPOT supports Post-Training Quantization (`PTQ `__) and Quantization-Aware Training (`QAT `__). Note that `Dynamic Quantization `__ currently has limited support. +* `Pruning `__ provides a common method for introducing sparsity in weights and activations. +* `Benchmarking `__ introduces how to utilize the benchmark interface of LPOT. +* `Mixed precision `__ introduces how to enable mixed precision, including BFP16 and int8 and FP32, on Intel platforms during tuning. +* `Graph Optimization `__ introduces how to enable graph optimization for FP3232 and auto-mixed precision. +* `TensorBoard `__ provides tensor histograms and execution graphs for tuning debugging purposes. + + +.. toctree:: + :maxdepth: 1 + :hidden: + + Quantization.md + PTQ.md + QAT.md + dynamic_quantization.md + pruning.md + benchmark.md + mixed_precision.md + graph_optimization.md + tensorboard.md + + +Advanced Topics +=============== + +* `Adaptor `__ is the interface between LPOT and framework. The method to develop adaptor extension is introduced with ONNX Runtime as example. +* `Tuning strategies `__ can automatically optimized low-precision recipes for deep learning models to achieve optimal product objectives like inference performance and memory usage with expected accuracy criteria. The method to develop a new strategy is introduced. + + +.. toctree:: + :maxdepth: 1 + :hidden: + + adaptor.md + tuning_strategies.md \ No newline at end of file diff --git a/docs/dynamic_quantization.md b/docs/dynamic_quantization.md index 0738d0fb9c2..6036249ebed 100644 --- a/docs/dynamic_quantization.md +++ b/docs/dynamic_quantization.md @@ -1,12 +1,12 @@ # Dynamic Quantization -### Now only onnxruntime backend support dynamic quantization. +> **Note** -[^1]The key idea with dynamic quantization as described here is that we are going to determine the scale factor for activations dynamically based on the data range observed at runtime. This ensures that the scale factor is “tuned” so that as much signal as possible about each observed dataset is preserved. +> Dynamic quantization currently only supports the onnxruntime backend. -Dynamic quantization is relatively free of tuning parameters which makes it well suited to be added into production pipelines as a standard part of NLP models. +As a [quantization](./Quantization.md) class, dynamic quantization allows users to determine the scale factor for activations dynamically based on the data range that's observed at runtime as opposed to using other methods that entail multiplying a float point value by some scale factor and then rounding the result to a whole number. As noted in [PyTorch documentation][PyTorch-DynQuant], this "ensures that the scale factor is 'tuned' so that as much signal as possible about each observed dataset is preserved." Since it does not use a lot of tuning parameters, dynamic quantization is a good match for NLP models. -Take onnxruntime bert_base model as an example, users can specific quantization method like the following yaml: +The onnxruntime bert_base model provides an example where users can create a specific quantization method like the following yaml: ```yaml @@ -29,4 +29,4 @@ tuning: timeout: 0 # optional. tuning timeout (seconds). default value is 0 which means early stop. combine with max_trials field to decide when to exit. random_seed: 9527 # optional. random seed for deterministic tuning. ``` -[^1]: https://pytorch.org/tutorials/recipes/recipes/dynamic_quantization.html +[PyTorch-DynQuant]: https://pytorch.org/tutorials/recipes/recipes/dynamic_quantization.html diff --git a/docs/full_model_list.md b/docs/full_model_list.md index 8246b518f7c..cb3253beaff 100644 --- a/docs/full_model_list.md +++ b/docs/full_model_list.md @@ -1,19 +1,20 @@ -### Full Validated Models +Full Validated Models +===================== -Below tables are models enabled by Intel® Low Precision Optimization Tool. +The below tables are models enabled by the Intel® Low Precision Optimization Tool. -TensorFlow 2.x models: +### TensorFlow 2.x models - +
- - + + @@ -237,9 +238,9 @@ TensorFlow 2.x models:
   
Framework   
   
Version   
   
Model   
   
Dataset   
   
   
   
   
AccuracyPerformance speed up
   
INT8   Tuning Accuracy   
-TensorFlow 1.x models: +### TensorFlow 1.x models - +
@@ -391,10 +392,9 @@ TensorFlow 1.x models:
   
Framework   
-PyTorch models: +### PyTorch models - - +
@@ -656,10 +656,9 @@ PyTorch models:
Framework
-Quantization-aware training models: - +### Quantization-aware training models - +
@@ -701,9 +700,9 @@ Quantization-aware training models:
Framework
-MXNet models: +### MXNet models - +
@@ -805,9 +804,9 @@ MXNet models:
Framework
-ONNX Models: +### ONNX Models - +
diff --git a/docs/graph_optimization.md b/docs/graph_optimization.md index ae3d4dde18d..24bc560e827 100644 --- a/docs/graph_optimization.md +++ b/docs/graph_optimization.md @@ -1,18 +1,22 @@ -## Graph Optimization Introduction +Graph Optimization +================== -The graph optimization is mainly focus on below two kind of scenarios. +## Introduction -1. **FP32 optimization**. This is similar to TensorFlow optimization tool [optimize_for_inference](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/tools/optimize_for_inference.py) while LPOT enables more optimizations (e.g., common subexpression elimination). +Graph optimization is primarily focused on two scenarios, shown below: -2. **Auto-mixed precision optimization**. LPOT generates the optimal model with auto-mixed precision ([bfloat16](https://cloud.google.com/tpu/docs/bfloat16) & FP32) and allows the further auto-tuning per accuracy requirement. +1. **FP32 optimization**. This is similar to the TensorFlow optimization tool [optimize_for_inference](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/tools/optimize_for_inference.py) while LPOT enables more optimizations (such as common subexpression elimination). + +2. **Auto-mixed precision optimization**. LPOT generates the optimal model with auto-mixed precision ([bfloat16](https://cloud.google.com/tpu/docs/bfloat16) and FP32) and allows for additional auto-tuning per accuracy requirements. ## How to use it -Generally, we list below three examples to demonstrate the usage of graph optimization API. -#### 1. **FP32 Optimization** +See the following three examples which demonstrate graph optimization API usage. + +### FP32 Optimization -LPOT would run graph optimization under FP32 optimization by default. In other words, it equals the user set the precisions to **'fp32'** explicitly, +LPOT runs the graph optimization under FP32 Optimization by default. In other words, the **precisions** field is explicitly set to **fp32**: ```python from lpot.experimental import Graph_Optimization @@ -24,11 +28,11 @@ LPOT would run graph optimization under FP32 optimization by default. In other w optimized_model = graph_optimizer() ``` -#### 2. **Auto-mixed Precision Optimization** +### Auto-mixed Precision Optimization - #### 2.1. *Default auto-mixed precision* +#### Default auto-mixed precision - The only difference between this and default mode is the `bf16` needs to be added into the precisions filed. +The only difference between this and the default mode (FP32 optimization) is that **bf16** must be added to the **precisions** field. ```python from lpot.experimental import Graph_Optimization @@ -51,19 +55,19 @@ For those platforms without bf16 enabling, like CLX. LPOT also could leverage th e.g, `FORCE_BF16=1 /path/to/executable_lpot_wrapper`. If we don't add such prefix `FORCE_BF16=1`, the LPOT would exit consequently. - #### 2.2. *Auto-mixed precision with auto-tuning.* +#### Auto-mixed precision with auto-tuning + +LPOT also supports tuning the model in graph optimization mode. The end user must replace the quantization field with graph_optimization parts such as shown below. The **precisions** field only supports **bf16** and **fp32**. - LPOT also supports the tuning the model under graph optimization mode. -The end user need to replace the quantization field with graph_optimization parts like below. The precisions filed only supports 'bf16' and 'fp32'. ```yaml graph_optimization: precisions: ['bf16', 'fp32'] ``` - Note, if we remove the evaluation field from the yaml, the graph optimization will only convert the model depends on the precisions setting. +Note that if we remove the evaluation field from the yaml file, the graph optimization will only convert the model depending on the precisions setting. - When the graph_optimization field set and the evaluation field exists in the yaml, LPOT will execute the similar process like quantization. It means the LPOT would convert op into bf16 as much as possible and check the metric later, if the metric meet the criterion, the LPOT would exit or it would fallback one op to fp32 and re-run the above process till it meet the exit policy setting. +When the graph_optimization field is set and the evaluation field exists in the yaml file, LPOT executes the similar process like quantization. It means the LPOT converts op into bf16 as much as possible and checks the metric later. If the metric meets the criterion, LPOT exits or it fallbacks one op to fp32 and re-runs the above process until it meets the exit policy setting. - Below is the example of using the yaml to trigger the graph optimization. +Below is an example of using yaml to trigger graph optimization. ```python from lpot.experimental import Graph_Optimization diff --git a/docs/incompatible_changes.md b/docs/incompatible_changes.md index be77bbcc392..98f8500932b 100644 --- a/docs/incompatible_changes.md +++ b/docs/incompatible_changes.md @@ -1,14 +1,14 @@ -# incompatible changes between v1.2 and v1.1 +# Incompatible changes between v1.2 and v1.1 -## user facing APIs +## User-facing APIs -The user facing APIs are changed between v1.2 and v1.1. The major changes are: +The user-facing APIs are changed between v1.2 and v1.1. The major changes are: -1. v1.2 abstracts `lpot.common.Model` concept to cover those cases whose weight and graph files are stored seperately. +1. v1.2 abstracts `lpot.common.Model` concept to cover those cases whose weight and graph files are stored separately. -2. v1.2 unifies the calling style by setting model, calibration dataloader, evaluation dataloader, metric through `quantizer` attributes rather than passing as function inputs. +2. v1.2 unifies the calling style by setting model, calibration dataloader, evaluation dataloader, and metric through `quantizer` attributes rather than passing as function inputs. -Please refer to below examples to know the details. +Refer to below examples for details. ```python # user facing API example in v1.1 @@ -34,8 +34,8 @@ q_model.save('/path/to/output/dir') # explicitly call to save q_model ``` -## built-in transform/dataset/metric APIs +## Built-in transform/dataset/metric APIs v1.2 refines LPOT built-in transform/dataset/metric to unify APIs cross different framework backends. -Please refer to [dataset](./dataset.md), [transform](./transform.md) and [metric](./metric.md) to know how to use them in yaml or code. +Refer to [dataset](./dataset.md), [transform](./transform.md), and [metric](./metric.md) to learn how to use them in yaml or code. diff --git a/docs/introduction.md b/docs/introduction.md deleted file mode 100644 index 3219967653d..00000000000 --- a/docs/introduction.md +++ /dev/null @@ -1,203 +0,0 @@ -Introduction -========================================= - -Intel® Low Precision Optimization Tool is an open-source Python library designed to help users quickly deploy low-precision inference solutions on popular deep learning (DL) frameworks such as TensorFlow, PyTorch, MXNet and ONNX Runtime. It automatically optimizes low-precision recipes for deep learning models in order to achieve optimal product objectives, such as inference performance and memory usage, with expected accuracy criteria. - - -# User-facing API - -The API is intended to unify low-precision quantization interfaces cross multiple DL frameworks for the best out-of-the-box experiences. - -> **NOTE** -> -> LPOT is keeping improving the user-facing APIs for better user experience. -> -> Now there are two sets of user-facing APIs. One is the default one supported from LPOT v1.0 for backward compatibility. Another one is the new APIs in lpot.experimental package. -> We recommend user to use the one in lpot.experimental. All of examples have been updated to use this experimental APIs. -> -> The major differences between the default use-facing APIs and the experiemntal APIs are: -> 1. The experimental APIs abstract `lpot.experimental.common.Model` concept to cover those cases whose weight and graph files are stored seperately. -> 2. The experimental APIs unifiy the calling style of `Quantization`, `Pruning`, and `Benchmark` class by setting model, calibration dataloader, evaluation dataloader, metric through class attributes rather than passing as function inputs. -> 3. The experimental APIs refine LPOT built-in transforms/datasets/metrics by unifying the APIs cross different framework backends. - -## Experimental user-facing APIs - -The experimental user-facing APIs consist of below components: - -### quantization-related APIs -```python -# lpot.experimental.Quantization -class Quantization(object): - def __init__(self, conf_fname): - ... - - def __call__(self): - ... - - @property - def calib_dataloader(self): - ... - - @property - def eval_dataloader(self): - ... - - @property - def model(self): - ... - - @property - def metric(self): - ... - - @property - def postprocess(self, user_postprocess): - ... - - @property - def q_func(self): - ... - - @property - def eval_func(self): - ... - -``` -The `conf_fname` parameter used in the class initialization is the path to user yaml configuration file. This is a yaml file that is used to control the entire tuning behavior on the model. - -> **LPOT User YAML Syntax** -> -> Intel® Low Precision Optimization Tool provides template yaml files for the [Post-Training Quantization](../lpot/template/ptq.yaml), [Quantization-Aware Traing](../lpot/template/qat.yaml), and [Pruning](../lpot/template/pruning.yaml) scenarios. Refer to these template files to understand the meaning of each field. - -> Note that most fields in the yaml templates are optional. View the [HelloWorld Yaml](../examples/helloworld/tf_example2/conf.yaml) example for reference. - -```python -# Typical Launcher code -from lpot.experimental import Quantization, common - -# optional if LPOT built-in dataset could be used as model input in yaml -class dataset(object): - def __init__(self, *args): - ... - - def __getitem__(self, idx): - # return single sample and label tuple without collate. label should be 0 for label-free case - ... - - def len(self): - ... - -# optional if LPOT built-in metric could be used to do accuracy evaluation on model output in yaml -class custom_metric(object): - def __init__(self): - ... - - def update(self, predict, label): - # metric update per mini-batch - ... - - def result(self): - # final metric calculation invoked only once after all mini-batch are evaluated - # return a scalar to lpot for accuracy-driven tuning. - # by default the scalar is higher-is-better. if not, set tuning.accuracy_criterion.higher_is_better to false in yaml. - ... - -quantizer = Quantization(conf.yaml) -quantizer.model = common.Model('/path/to/model') -# below two lines are optional if LPOT built-in dataset is used as model calibration input in yaml -cal_dl = dataset('/path/to/calibration/dataset') -quantizer.calib_dataloader = common.DataLoader(cal_dl, batch_size=32) -# below two lines are optional if LPOT built-in dataset is used as model evaluation input in yaml -dl = dataset('/path/to/evaluation/dataset') -quantizer.eval_dataloader = common.DataLoader(dl, batch_size=32) -# optional if LPOT built-in metric could be used to do accuracy evaluation in yaml -quantizer.metric = common.Metric(custom_metric) -q_model = quantizer() -q_model.save('/path/to/output/dir') -``` - -`model` attribute in `Quantization` class is an abstraction of model formats cross different frameworks. LPOT supports passing the path of `keras model`, `frozen pb`, `checkpoint`, `saved model`, `torch.nn.model`, `mxnet.symbol.Symbol`, `gluon.HybirdBlock`, and `onnx model` to instantiate a `lpot.experimental.common.Model()` class and set to `quantizer.model`. - -`calib_dataloader` and `eval_dataloader` attribute in `Quantization` class is used to setup a calibration dataloader by code. It is optional to set if user sets corresponding fields in yaml. - -`metric` attribute in `Quantization` class is used to setup a custom metric by code. It is optional to set if user finds LPOT built-in metric could be used with their model and sets corresponding fields in yaml. - -`postprocess` attribute in `Quantization` class is not necessary in most of usage cases. It will only be needed when user wants to use LPOT built-in metric but model output could not directly be handled by LPOT built-in metrics. In this case, user could register a transform to convert model output to expected one required by LPOT built-in metric. - -`q_func` attribute in `Quantization` class is only for `Quantization Aware Training` case, in which user need to register a function that takes `model` as input parameter and executes entire training process with self contained training hyper-parameters. - -`eval_func` attribute in `Quantization` class is reserved for special case. If user have had a evaluation function when train a model, user just needs to implement a `calib_dataloader` and leave `eval_dataloader` as None, modify this evaluation function to take `model` as input parameter and return a higher-is-better scaler. In some scenarios, it may reduce developement effort. - - -### pruning-related APIs (POC) -```python -class Pruning(object): - def __init__(self, conf_fname): - ... - - def on_epoch_begin(self, epoch): - ... - - def on_batch_begin(self, batch_id): - ... - - def on_batch_end(self): - ... - - def on_epoch_end(self): - ... - - def __call__(self): - ... - - @property - def model(self): - ... - - @property - def q_func(self): - ... - -``` - -This API is used to do sparsity pruning. Currently it is Proof-of-Concept, LPOT only supports `magnitude pruning` on PyTorch. - -For how to use this API, please refer to [Pruning Document](./pruning.md) - -### benchmarking-related APIs -```python -class Benchmark(object): - def __init__(self, conf_fname): - ... - - def __call__(self): - ... - - @property - def model(self): - ... - - @property - def metric(self): - ... - - @property - def b_dataloader(self): - ... - - @property - def postprocess(self, user_postprocess): - ... -``` - -This API is used to measure the model performance and accuarcy. - -For how to use this API, please refer to [Benchmark Document](./benchmark.md) - -## Default user-facing APIs - -The default user-facing APIs would exist for backward compatiblity from v1.0 release. User could refer to [v1.1 API](https://github.com/intel/lpot/blob/v1.1/docs/introduction.md) to understand how default user-facing APIs work. - -A [HelloWorld example](../examples/helloworld/tf_example6) using default user-facing APIs is provided for user reference. - -Full examples using default user-facing APIs could be found at [here](https://github.com/intel/lpot/tree/v1.1/examples). diff --git a/docs/metric.md b/docs/metric.md index b7fb708bdbc..b937e60bb01 100644 --- a/docs/metric.md +++ b/docs/metric.md @@ -1,15 +1,13 @@ - Metrics -======================== - -As to evaluate the performance of a specific model, we should have general metrics to measure the performance of different model. Different framework always have their own Metric module but with different features and API, as lpot is cross framework tool, it is hard to use them in the same way, another request is that this tool support code-free configuration through yaml file, with built-in metrics, lpot can get the performance and accuracy without code change from user. In special case, user can also register its own metric class through lpot method. +======= +In terms of evaluating the performance of a specific model, we should have general metrics to measure the performance of different models. Different frameworks always have their own Metric module but with different features and APIs. LPOT Metrics supports code-free configuration through a yaml file, with built-in metrics, so that LPOT can achieve performance and accuracy without code changes from the user. In special cases, users can also register their own metric classes through the LPOT method. -# How to use it +## How to use Metrics -## Config built-in metric in yaml file +### Config built-in metric in a yaml file -User can specify some LPOT build-in metric like below: +Users can specify an LPOT built-in metric such as shown below: ```yaml evaluation: @@ -18,11 +16,10 @@ evaluation: topk: 1 ``` -or -## Config custom metric in code +### Config custom metric in code -User can register their own metric as follows: +Users can also register their own metric as follows: ```python class Metric(object): @@ -41,9 +38,9 @@ class Metric(object): ``` -The result() function returns a higher-is-better scalar to reflect model accuracy on evaluation dataset. +The result() function returns a higher-is-better scalar to reflect model accuracy on an evaluation dataset. -After defining the metric class, user needs to register it with a user defined metric name and the metric class +After defining the metric class, users need to register it with a user-defined metric name and the metric class: ```python @@ -56,69 +53,61 @@ q_model = quantizer() ``` -# Builtin metric support list +## Built-in metric support list -LPOT supports some builtin metrics that popularly used in industry. +LPOT supports some built-in metrics that are popularly used in industry. -Please refer to [example](../examples/helloworld/tf_example1) on how to config a builtin metric. +Refer to [this HelloWorld example](/examples/helloworld/tf_example1) on how to config a built-in metric. #### TensorFlow | Metric | Parameters | Inputs | Comments | Usage(In yaml file) | | :------ | :------ | :------ | :------ | :------ | -| topk(k) | **k** (int, default=1): Number of top elements to look at for computing accuracy | preds, labels | Computes top k predictions accuracy. | metric:
   topk:
     k: 1 | +| topk(k) | k (int, default=1): Number of top elements to look at for computing accuracy | preds, labels | Computes top k predictions accuracy. | metric:
   topk:
     k: 1 | | Accuracy() | None | preds, labels | Computes accuracy classification score. | metric:
   Accuracy: {} | | Loss() | None | preds, labels | A dummy metric for directly printing loss, it calculates the average of predictions.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/_modules/mxnet/metric.html#Loss) for details. | metric:
   Loss: {} | -| MAE(compare_label) | **compare_label** (bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Mean Absolute Error (MAE) loss. | metric:
   MAE:
     compare_label: True | -| RMSE(compare_label) | **compare_label** (bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Root Mean Squred Error (RMSE) loss. | metric:
   RMSE:
     compare_label: True | -| MSE(compare_label) | **compare_label** (bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Mean Squared Error (MSE) loss. | metric:
   MSE:
     compare_label: True | +| MAE() | None | preds, labels | Computes Mean Absolute Error (MAE) loss. | metric:
   MAE: {} | +| RMSE() | None | preds, labels | Computes Root Mean Squred Error (RMSE) loss. | metric:
   RMSE: {} | +| MSE() | None | preds, labels | Computes Mean Squared Error (MSE) loss. | metric:
   MSE: {} | | F1() | None | preds, labels | Computes the F1 score of a binary classification problem. | metric:
   F1: {} | -| mAP( anno_path, iou_thrs, map_points) | **anno_path** (str): Annotation path.
**iou_thrs** (float or str, default=0.5): Minimal value for intersection over union that allows to make decision that prediction bounding box is true positive. You can specify one float value between 0 to 1 or string "05:0.05:0.95" for standard COCO thresholds.
**map_points** (int, default=0): The way to calculate mAP. 101 for 101-point interpolated AP, 11 for 11-point interpolated AP, 0 for area under PR curve. | preds, labels | preds is a tuple which supports 2 length: 3 and 4.
If its length is 3, it should contain boxes, scores, classes in turn.
If its length is 4, it should contain target_boxes_num, boxes, scores, classes in turn
labels is a tuple which contains bbox, str_label, int_label, image_id inturn
the length of one of str_label and int_label can be 0 | metric:
   mAP:
     anno_path: /path/to/annotation
     iou_thrs: 0.5
     map_points: 0

If anno_path is not set, metric will use built-in coco_label_map | -| COCOmAP( anno_path, iou_thrs, map_points) | **anno_path** (str): Annotation path.
**iou_thrs** (float or str): Intersection over union threshold. Set to "0.5:0.05:0.95" for standard COCO thresholds.
**map_points** (int): The way to calculate mAP. Set to 101 for 101-point interpolated AP. | preds, labels | preds is a tuple which supports 2 length: 3 and 4.
If its length is 3, it should contain boxes, scores, classes in turn.
If its length is 4, it should contain target_boxes_num, boxes, scores, classes in turn
labels is a tuple which contains bbox, str_label, int_label, image_id inturn
the length of one of str_label and int_label can be 0 | metric:
   COCOmAP:
     anno_path: /path/to/annotation

If anno_path is not set, metric will use built-in coco_label_map | -| VOCmAP( anno_path, iou_thrs, map_points) | **anno_path** (str): Annotation path.
**iou_thrs** (float or str): Intersection over union threshold. Set to 0.5.
**map_points** (int): The way to calculate mAP. The way to calculate mAP. Set to 0 for area under PR curve. | preds, labels | preds is a tuple which supports 2 length: 3 and 4.
If its length is 3, it should contain boxes, scores, classes in turn.
If its length is 4, it should contain target_boxes_num, boxes, scores, classes in turn
labels is a tuple which contains bbox, str_label, int_label, image_id inturn
the length of one of str_label and int_label can be 0 | metric:
   VOCmAP:
     anno_path: /path/to/annotation

If anno_path is not set, metric will use built-in coco_label_map | +| COCOmAP(anno_path) | anno_path(str, default=None):annotation path | preds, labels | preds is a tuple which supports 2 length: 3 and 4.
If its length is 3, it should contain boxes, scores, classes in turn.
If its length is 4, it should contain target_boxes_num, boxes, scores, classes in turn
labels is a tuple which contains bbox, str_label, int_label, image_id inturn
the length of one of str_label and int_label can be 0 | metric:
   COCOmAP:
     anno_path: /path/to/annotation
If anno_path is not set, metric will use built-in coco_label_map | | BLEU() | None | preds, labels | BLEU score computation between labels and predictions. An approximate BLEU scoring method since we do not glue word pieces or decode the ids and tokenize the output. By default, we use ngram order of 4 and use brevity penalty. Also, this does not have beam search | metric:
   BLEU: {} | | SquadF1() | None | preds, labels | Evaluate v1.1 of the SQuAD dataset | metric:
   SquadF1: {} | -| SquadF1() | None | preds, labels | Evaluate v1.1 of the SQuAD dataset | metric:
   SquadF1: {} | #### PyTorch | Metric | Parameters | Inputs | Comments | Usage(In yaml file) | | :------ | :------ | :------ | :------ | :------ | -| topk(k) | **k** (int, default=1): Number of top elements to look at for computing accuracy | preds, labels | Calculates the top-k categorical accuracy. | metric:
   topk:
     k: 1 | +| topk(k) | k (int, default=1): Number of top elements to look at for computing accuracy | preds, labels | Calculates the top-k categorical accuracy. | metric:
   topk:
     k: 1 | | Accuracy() | None | preds, labels | Calculates the accuracy for binary, multiclass and multilabel data.
Please refer [Pytorch docs](https://pytorch.org/ignite/metrics.html#ignite.metrics.Accuracy) for details. | metric:
   Accuracy: {} | | Loss() | None | preds, labels | A dummy metric for directly printing loss, it calculates the average of predictions.
Please refer [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/_modules/mxnet/metric.html#Loss) for details. | metric:
   Loss: {} | -| MAE(compare_label) | **compare_label** (bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Mean Absolute Error (MAE) loss. | metric:
   MAE:
     compare_label: True | -| RMSE(compare_label) | **compare_label** (bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Root Mean Squred Error (RMSE) loss. | metric:
   RMSE:
     compare_label: True | -| MSE(compare_label) | **compare_label** (bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Mean Squared Error (MSE) loss. | metric:
   MSE:
     compare_label: True |(https://pytorch.org/ignite/metrics.html#ignite.metrics.MeanSquaredError) for details. | metric:
   MSE: {} | +| MAE() | None | preds, labels | Calculates the mean absolute error.
Please refer [Pytorch docs](https://pytorch.org/ignite/metrics.html#ignite.metrics.MeanAbsoluteError) for details. | metric:
   MAE: {} | +| RMSE() | None | preds, labels | Calculates the root mean squared error.
Please refer [Pytorch docs](https://pytorch.org/ignite/metrics.html#ignite.metrics.RootMeanSquaredError) for details. | metric:
   RMSE: {} | +| MSE() | None | preds, labels | Calculates the mean squared error.
Please refer [Pytorch docs](https://pytorch.org/ignite/metrics.html#ignite.metrics.MeanSquaredError) for details. | metric:
   MSE: {} | | F1() | None | preds, labels | Computes the F1 score of a binary classification problem. | metric:
   F1: {} | #### MXNet | Metric | Parameters | Inputs | Comments | Usage(In yaml file) | | :------ | :------ | :------ | :------ | :------ | -| topk(k) | **k** (int, default=1): Number of top elements to look at for computing accuracy | preds, labels | Computes top k predictions accuracy. | metric:
   topk:
     k: 1 | +| topk(k) | k (int, default=1): Number of top elements to look at for computing accuracy | preds, labels | Computes top k predictions accuracy. | metric:
   topk:
     k: 1 | | Accuracy() | None | preds, labels | Computes accuracy classification score.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/api/metric/index.html#mxnet.metric.Accuracy) for details. | metric:
   Accuracy: {} | | Loss() | None | preds, labels | A dummy metric for directly printing loss, it calculates the average of predictions.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/_modules/mxnet/metric.html#Loss) for details. | metric:
   Loss: {} | | MAE() | None | preds, labels | Computes Mean Absolute Error (MAE) loss.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/api/metric/index.html#mxnet.metric.MAE) for details. | metric:
   MAE: {} | -| RMSE(compare_label) | **compare_label** (bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Root Mean Squred Error (RMSE) loss. | metric:
   RMSE:
     compare_label: True | +| RMSE() | None | preds, labels | Computes Root Mean Squred Error (RMSE) loss.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/api/metric/index.html#mxnet.metric.RMSE) for details. | metric:
   RMSE: {} | | MSE() | None | preds, labels | Computes Mean Squared Error (MSE) loss.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/api/metric/index.html#mxnet.metric.MSE) for details. | metric:
   MSE: {} | -| F1() | None | preds, labels | Computes the F1 score of a binary classification problem. | metric:
   F1: {} | +| F1() | None | preds, labels | Computes the F1 score of a binary classification problem.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/api/metric/index.html#mxnet.metric.F1) for details. | metric:
   F1: {} | #### ONNXRT | Metric | Parameters | Inputs | Comments | Usage(In yaml file) | | :------ | :------ | :------ | :------ | :------ | -| topk(k) | **k** (int, default=1): Number of top elements to look at for computing accuracy | preds, labels | Computes top k predictions accuracy. | metric:
   topk:
     k: 1 | +| topk(k) | k (int, default=1): Number of top elements to look at for computing accuracy | preds, labels | Computes top k predictions accuracy. | metric:
   topk:
     k: 1 | | Accuracy() | None | preds, labels |Computes accuracy classification score. | metric:
   Accuracy: {} | | Loss() | None | preds, labels | A dummy metric for directly printing loss, it calculates the average of predictions.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/_modules/mxnet/metric.html#Loss) for details. | metric:
   Loss: {} | -| MAE(compare_label) | **compare_label** (bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Mean Absolute Error (MAE) loss. | metric:
   MAE:
     compare_label: True | -| RMSE(compare_label) | **compare_label** (bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Root Mean Squred Error (RMSE) loss. | metric:
   RMSE:
     compare_label: True | -| MSE(compare_label) | **compare_label** (bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Mean Squared Error (MSE) loss. | metric:
   MSE:
     compare_label: True | -| F1() | None | preds, labels | Computes the F1 score of a binary classification problem. | metric:
   F1: {} | -| mAP( anno_path, iou_thrs, map_points) | **anno_path** (str): Annotation path.
**iou_thrs** (float or str, default=0.5): Minimal value for intersection over union that allows to make decision that prediction bounding box is true positive. You can specify one float value between 0 to 1 or string "05:0.05:0.95" for standard COCO thresholds.
**map_points** (int, default=0): The way to calculate mAP. 101 for 101-point interpolated AP, 11 for 11-point interpolated AP, 0 for area under PR curve. | preds, labels | preds is a tuple which supports 2 length: 3 and 4.
If its length is 3, it should contain boxes, scores, classes in turn.
If its length is 4, it should contain target_boxes_num, boxes, scores, classes in turn
labels is a tuple which contains bbox, str_label, int_label, image_id inturn
the length of one of str_label and int_label can be 0 | metric:
   mAP:
     anno_path: /path/to/annotation
     iou_thrs: 0.5
     map_points: 0

If anno_path is not set, metric will use built-in coco_label_map | -| COCOmAP( anno_path, iou_thrs, map_points) | **anno_path** (str): Annotation path.
**iou_thrs** (float or str): Intersection over union threshold. Set to "0.5:0.05:0.95" for standard COCO thresholds.
**map_points** (int): The way to calculate mAP. Set to 101 for 101-point interpolated AP. | preds, labels | preds is a tuple which supports 2 length: 3 and 4.
If its length is 3, it should contain boxes, scores, classes in turn.
If its length is 4, it should contain target_boxes_num, boxes, scores, classes in turn
labels is a tuple which contains bbox, str_label, int_label, image_id inturn
the length of one of str_label and int_label can be 0 | metric:
   COCOmAP:
     anno_path: /path/to/annotation

If anno_path is not set, metric will use built-in coco_label_map | -| VOCmAP( anno_path, iou_thrs, map_points) | **anno_path** (str): Annotation path.
**iou_thrs** (float or str): Intersection over union threshold. Set to 0.5.
**map_points** (int): The way to calculate mAP. The way to calculate mAP. Set to 0 for area under PR curve. | preds, labels | preds is a tuple which supports 2 length: 3 and 4.
If its length is 3, it should contain boxes, scores, classes in turn.
If its length is 4, it should contain target_boxes_num, boxes, scores, classes in turn
labels is a tuple which contains bbox, str_label, int_label, image_id inturn
the length of one of str_label and int_label can be 0 | metric:
   VOCmAP:
     anno_path: /path/to/annotation

If anno_path is not set, metric will use built-in coco_label_map | - - +| MAE() | None | preds, labels | Computes Mean Absolute Error (MAE) loss. | metric:
   MAE: {} | +| RMSE() | None | preds, labels | Computes Root Mean Squred Error (RMSE) loss.| metric:
   RMSE: {} | +| MSE() | None | preds, labels | Computes Mean Squared Error (MSE) loss. | metric:
   MSE: {} | +| F1() | None | preds, labels | Computes the F1 score of a binary classification problem. | metric:
   F1: {} | \ No newline at end of file diff --git a/docs/mixed_precision.md b/docs/mixed_precision.md index 26bb9e44188..26e43d35c51 100644 --- a/docs/mixed_precision.md +++ b/docs/mixed_precision.md @@ -1,5 +1,7 @@ -BF16 Convert -========================================= +Mixed Precision +=============== + +## BF16 Convert The recent growth of Deep Learning has driven the development of more complex models that require significantly more compute and memory capabilities. Several low precision numeric formats have been proposed to address the problem. Google's [bfloat16](https://cloud.google.com/tpu/docs/bfloat16) and the [FP16: IEEE](https://en.wikipedia.org/wiki/Half-precision_floating-point_format) half-precision format are two of the most widely used sixteen bit formats. [Mixed precision](https://arxiv.org/abs/1710.03740) training and inference using low precision formats have been developed to reduce compute and bandwidth requirements. @@ -9,11 +11,9 @@ Intel has worked with the TensorFlow development team to enhance TensorFlow to i Intel® Low Precision Optimization Tool can support op-wise BF16 precision for TensorFlow now. With BF16 support, it can get a mixed precision model with acceptable accuracy and performance or others objective goals. This document will give a simple introduction of TensorFlow BF16 convert transformation and how to use the BF16. -# BF16 Convert Transformation in TensorFlow +## BF16 Convert Transformation in TensorFlow -
- -
+![Mixed Precision](imgs/bf16_convert_tf.png "Mixed Precision Graph") ### Three steps @@ -29,9 +29,9 @@ Intel® Low Precision Optimization Tool can support op-wise BF16 precision for T After the mixed precision graph generated, there are still some optimization need to be applied to improved the performance, for example `Cast + Cast` and so on. The `BF16Convert` transformer also apply a depth-first method to make it possible to take the ops use `BF16` which can support `BF16` datatype to reduce the insertion of `Cast` op. -# How to use it +## Using BF16 Convert -> BF16 support has enabled in `intel-tensorflow` [`2.3.0`](https://pypi.org/project/intel-tensorflow/2.3.0/)/[`2.4.0`](https://pypi.org/project/intel-tensorflow/2.4.0/)/[`1.15.0up1`](https://github.com/Intel-tensorflow/tensorflow/tree/v1.15.0up1)/[`1.15.0up2`](https://github.com/Intel-tensorflow/tensorflow/tree/v1.15.0up2) and `intel-tensorflow-avx512` [`2.3.0`](https://pypi.org/project/intel-tensorflow-avx512/2.3.0/)/[`2.4.0`](https://pypi.org/project/intel-tensorflow-avx512/2.4.0/). On hardware side, it need the CPU support `avx512_bf16` instruction set. We also support force enable it for debug usage by using set the environment variable `FORCE_BF16=1`. But without above 2 sides support, the poor performance or other problems may expect. +BF16 support has enabled in `intel-tensorflow` [`2.3.0`](https://pypi.org/project/intel-tensorflow/2.3.0/)/[`2.4.0`](https://pypi.org/project/intel-tensorflow/2.4.0/)/[`1.15.0up1`](https://github.com/Intel-tensorflow/tensorflow/tree/v1.15.0up1)/[`1.15.0up2`](https://github.com/Intel-tensorflow/tensorflow/tree/v1.15.0up2) and `intel-tensorflow-avx512` [`2.3.0`](https://pypi.org/project/intel-tensorflow-avx512/2.3.0/)/[`2.4.0`](https://pypi.org/project/intel-tensorflow-avx512/2.4.0/). On hardware side, it need the CPU support `avx512_bf16` instruction set. We also support force enable it for debug usage by using set the environment variable `FORCE_BF16=1`. But without above 2 sides support, the poor performance or other problems may expect. For now this feature will be auto enabled in the env with `intel-tensorflow` `>=2.3.0` and `avx512_bf16` instruction set support plantform. To get better ferformance with `BF16` datatype, the `intel-tensorflow-avx512` is recommended, or build intel tensorflow (take [tag `v1.15.0up2`](https://github.com/Intel-tensorflow/tensorflow/tree/v1.15.0up2) as example) from source code by using below command, @@ -44,4 +44,4 @@ bazel build --cxxopt=-D_GLIBCXX_USE_CXX11_ABI=0 --copt=-O3 --copt=-Wformat --cop ./bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/ ``` -By default, `BF16` has been added into activation and weight supported datatype if the tensorflow version and CPU meet the requirements. We can disable it in `yaml` config file by specifying the datatype for activation and weight. For now, only the `Basic` strategy `BF16` support has been tested. +By default, `BF16` has been added into activation and weight supported datatype if the tensorflow version and CPU meet the requirements. We can disable it in the yaml config file by specifying the datatype for activation and weight. For now, only the `Basic` strategy `BF16` support has been tested. diff --git a/docs/model.md b/docs/model.md index 5e6868038a5..54f4579fc1e 100644 --- a/docs/model.md +++ b/docs/model.md @@ -1,9 +1,9 @@ Model -=============== +===== -Model feature of LPOT is used to encapsulate the behavior of model building and saving. By just give information of different model format and framework_specific_info, LPOT will do optimizations and quantization on this model object and return a LPOT Model object for further model persisting or benchmark. LPOT Model make it possible to keep neccessary information of a model which is needed during optimization and quantization like the input/output names, workspace path and other model format knowledges. This helps unify the features gap bring by different model format and frameworks. +The LPOT Model feature is used to encapsulate the behavior of model building and saving. By simply providing information such as different model formats and framework_specific_info, LPOT performs optimizations and quantization on this model object and returns an LPOT Model object for further model persisting or benchmarking. An LPOT Model helps users to maintain necessary model information which is needed during optimization and quantization such as the input/output names, workspace path, and other model format knowledge. This helps unify the features gap brought by different model formats and frameworks. -User can create, use and save a model in this way: +Users can create, use, and save models in the following manner: ```python from lpot import Quantization, common @@ -14,9 +14,9 @@ q_model.save(save_path) ``` -# Framework model support list +## Framework model support list -#### TensorFlow +### TensorFlow | Model format | Parameters | Comments | Usage | | ------ | ------ |------|------| @@ -32,7 +32,7 @@ q_model.save(save_path) | tf1.x saved model | **model**(str): path to saved model, **framework_specific_info**(dict): information about model and framework, such as input_tensor_names, input_tensor_names, workspace_path and name
**kwargs**(dict): other required parameters | **Save format**:
saved model | from lpot.experimental import Quantization, common
quantizer = Quantization(args.config)
quantizer.model = common.Model(model)
q_model = quantizer()
**model is the path of model, like ./path/to/saved_model/** | | tf2.x checkpoint | | Not support yes. As tf2.x checkpoint only has weight and does not contain any description of the computation, please use different tf2.x model for quantization | | -The following methods can be used in tensorflow model +The following methods can be used in the TensorFlow model: ```python graph_def = model.graph_def @@ -46,14 +46,14 @@ input_tensor = model.input_tensor output_tensor = model.output_tensor ``` -#### MXNet +### MXNet | Model format | Parameters | Comments | Usage | | ------ | ------ |------|------| | mxnet.gluon.HybridBlock | **model**(mxnet.gluon.HybridBlock): mxnet.gluon.HybridBlock object
**framework_specific_info**(dict): information about model and framework
**kwargs**(dict): other required parameters | **Save format**:
save_path.json | from lpot.experimental import Quantization, common
quantizer = Quantization(args.config)
quantizer.model = common.Model(model)
q_model = quantizer()
**model is mxnet.gluon.HybridBlock object** | | mxnet.symbol.Symbol | **model**(tuple): tuple of symbol, arg_params, aux_params
**framework_specific_info**(dict): information about model and framework
**kwargs**(dict): other required parameters | **Save format**:
save_path-symbol.json and save_path-0000.params | from lpot.experimental import Quantization, common
quantizer = Quantization(args.config)
quantizer.model = common.Model(model)
q_model = quantizer()
**model is the tuple of symbol, arg_params, aux_params** | -* Get symbol, arg_params, aux_params from symbol and param files +* Get symbol, arg_params, aux_params from symbol and param files. ```python import mxnet as mx @@ -71,7 +71,7 @@ for k, v in save_dict.items(): aux_params[name] = v ``` -#### PyTorch +### PyTorch | Model format | Parameters | Comments | Usage | | ------ | ------ |------|------| diff --git a/docs/pruning.md b/docs/pruning.md index 77eebac657e..3e53d465553 100644 --- a/docs/pruning.md +++ b/docs/pruning.md @@ -1,24 +1,31 @@ -# Introduction -Sparsity is a a measure of how many percents of elements in a tensor are exact zeros[^1]. A tensor is considered sparse if "most" of its elements are zero. Appeartly only non-zero elements will be stored and computed so the inference process could be accelerated due to Tops and memory saved[^2]. -[^1]: https://nervanasystems.github.io/distiller/pruning.html -[^2]: acceleration needs sparse compute kernels which are WIP +Pruning +======= + +## Introduction + +Sparsity is a measure of how many percents of elements in a tensor are [exact zeros][^1]. A tensor is considered sparse if most of its elements are zero. Only non-zero elements will be stored and computed so the inference process could be accelerated due to TOPS (teraoperations/second) and memory saved (acceleration needs sparse compute kernels which are a work in process). The -"norm" function measures how many zero-elements are in a tensor x: -In other words, an element contributes either a value of 1 or 0 to \(l_0\). Anything but an exact zero contributes a value of 1 - that's pretty cool. Sometimes it helps to think about density, the number of non-zero elements (NNZ) and sparsity's complement: +In other words, an element contributes either a value of 1 or 0 to \(l_0\). Anything but an exact zero contributes a value of 1 - which is good. Sometimes it helps to think about density, the number of non-zero elements (NNZ) and sparsity's complement: \[ density = 1 - sparsity \] -A common method for introducing sparsity in weights and activations is called pruning. Pruning is the application of a binary criteria to decide which weights to prune: weights which match the pruning criteria are assigned a value of zero. Pruned elements are "trimmed" from the model: we replace their values with zero and also make sure they don't take part in the back-propagation process.

+A common method for introducing sparsity in weights and activations is called **pruning**. Pruning is the application of a binary criteria to decide which weights to prune: weights which match the pruning criteria are assigned a value of zero. Pruned elements are "trimmed" from the model: we replace their values with zero and also make sure they don't take part in the back-propagation process.

+ + +## Design + +The pruning process is similar to quantization-aware training (QAT). Intel® Low Precision Optimization Tool will do related model transformation during training and retrain the model to meet the accuracy goal. + +We implemented two kinds of object: Pruner and PrunePolicy. First, we define a sparsity goal (model-wise or op-wise, depending on whether there are ops not suitable for pruning) and the way to reach the sparsity goal (usually we increase the sparsity target linearly as the epoches). The pruner is in singleton mode, and will update the sparsity goal and schedule all PrunePolicy during different phases of training. +PrunePolicy carries different pruning algos. For example, MagnitudePrunePolicy sets thresholds of absolute value so that elements whose absolute value lower than the threshold will be zeroed. The zeroing process happens at the beginning and end of each minibatch iteration. -# Design -Pruning process is sometimes similar with quantization-aware training(QAT). Intel® Low Precision Optimization Tool will do related model transformation during training and retrain the model to meet the accuracy goal. - We implemented 2 kinds of object: Pruner and PrunePolicy. Firstly we define a sparsity goal(model-wise or op-wise, depending on whether there are ops not suitable for pruning) and the way to reach sparsity goal(Usually we increase the sparsity target linearly as the epoches). The pruner is in singleton mode, and will update sparsity goal and schedule all PrunePolicy on different phase of training. - PrunePolicy carries different pruning algos. For example, MagnitudePrunePolicy set thresholds of absolute value so that elements whose absolute value lower than the threshold will be zeroed. The zeroing process happens on beginning and end of each minibatch iteration. +## Usage + +Pruning configs need to be added into yaml as a pruning field. Global parameters contain **start_epoch** (on which epoch pruning begins), **end_epoch** (on which epoch pruning ends), **init_sparsity** (initial sparsity goal default 0), **target_sparsity** (target sparsity goal) and **frequency** (of updating sparsity). At least one pruner instance needs to be defined under specific algos (currently only magnitude supported). You can override all global params in a specific pruner using field names and specify which weight of model to be pruned. If no weight is specified, all weights of the model will be pruned. -# Usage -Pruning configs need to be added into yaml as pruning field. Global parameters contain **start_epoch** (on which epoch pruning begins), **end_epoch** (on which epoch pruning ends), **init_sparsity** (initial sparsity goal default 0), **target_sparsity** (target sparsity goal) and **frequency** (of updating sparsity). At least one pruner instance need to be defined, under specific algos (currently only magnitude supported). you can override all global params in specific pruner using field names and specify which weight of model to be pruned. if no weight specified, all weights of model will be pruned. ```yaml pruning: magnitude: @@ -33,8 +40,10 @@ pruning: target_sparsity: 0.25 ``` -# Examples -Users must pass a modified training function to Intel® Low Precision Optimization Tool. Take a typical pytorch training function as example. +## Examples + +Users must pass a modified training function to Intel® Low Precision Optimization Tool. Take a typical PyTorch training function as example: + ```python def p_func(model): # from lpot.pruning import Pruner @@ -53,7 +62,8 @@ def p_func(model): # pruner.on_epoch_end(epoch=epoch) evaluate(model) ``` -Note the commented lines are how pruner do model transformation. Then users can use LPOT like the following: +Note the commented lines are how the pruner does model transformation. Then users can use LPOT like the following: + ```python from lpot import Pruning, common prune = Pruning(args.config) @@ -62,3 +72,5 @@ prune.eval_dataloader = val_loader prune.q_func = p_func q_model = prune() ``` + +[^1]: https://nervanasystems.github.io/distiller/pruning.html \ No newline at end of file diff --git a/docs/tensorboard.md b/docs/tensorboard.md index 044319ffc1f..4637b409eb7 100644 --- a/docs/tensorboard.md +++ b/docs/tensorboard.md @@ -1,18 +1,22 @@ -# Introduction +TensorBoard +=========== -TensorBoard is a suite of web applications for inspecting and understanding your topology runs and graphs (see [TensorFlow TensorBoard](https://github.com/tensorflow/tensorboard) and [PyTorch TensorBoard](https://github.com/pytorch/pytorch/tree/master/torch/utils/tensorboard)). Intel® Low Precision Optimization Tool performs accuracy driven quantization, the tuning process will quantize the tensor, do graph transformation and optimization to achieve optimal performance under accuracy requirement. If you want to observe the behaviors of the optimizations, or you may want to find the reason why an accuracy target cannot be met, TensorBoard can provide you some valuable information.You can inspect the graph and tensor after each run of tuning and if a model cannot meet accuracy requirement user can analyze through the comparison of FP32 and int8 tensor histogram. +## Introduction -We collect the TensorBoard event summary during evaluation, the first time is on baseline FP32 model and later on at the end of each tuning runs based on quantized model. The TensorBoard log directory is named baseline_acc_ and tune__acc_, to indicate the stage and accuracy of the data is generated. User can select the data he or she has interest to observe with TensorBoard. +TensorBoard is a suite of web applications that provide measurements and visualizations used to inspect and understand your machine learning workflow for [TensorFlow TensorBoard](https://github.com/tensorflow/tensorboard) and [PyTorch TensorBoard](https://github.com/pytorch/pytorch/tree/master/torch/utils/tensorboard). Intel® Low Precision Optimization Tool performs accuracy-driven quantization; the tuning process quantizes the tensor and performs graph transformation and optimization to achieve optimal performance under accuracy requirement. If you want to observe the behaviors of the optimizations, or if you want to discover why an accuracy target cannot be met, TensorBoard can provide you with some valuable information. You can inspect the graph and tensor after each tuning run. If a model cannot meet accuracy requirements, you can analyze the comparison of FP32 and the INT8 tensor histogram. +We collect the TensorBoard event summary during evaluation. The first time is on the baseline FP32 model and later on at the end of each tuning runs are based on the quantized model. The TensorBoard log directory is named baseline_acc_ and tune__acc_, to indicate the stage and accuracy of the data that is generated. Users can select their data of interest to observe with TensorBoard. -PyTorch TensorBoard -================================ -# Design -The implementation of PyTorch TensorBoard basically have 3 steps: -1. before evaluation in the _pre_eval_hook() instruments observers in the model; -2. during evaluation the observers will collect tensor information in a dict data structure; -3. after evaluation dump the graph and tensor information with TensorBoard summary writer in _post_eval_hook(). +## PyTorch TensorBoard + +### Design + +PyTorch TensorBoard implementation includes three steps: + +* Before evaluation in the _pre_eval_hook() where instruments observers are placed in the model. +* During evaluation where observers collect tensor information in a dict data structure. +* After evaluation where the graph and tensor information is dumped with the TensorBoard summary writer in _post_eval_hook(). The detailed algorithm can be described by the Pseudo code: @@ -105,89 +109,89 @@ def _post_eval_hook(self, model, **args): ``` -# Usage -(Introduce the usage method of the feature) -1. Add "tensorboard: true" in yaml file. -2. Run quantization tuning, a "./runs" folder will be generated in working folder. +### Usage + +1. Add "tensorboard: true" in the yaml file. +2. Run quantization tuning; a "./runs" folder is generated in the working folder. 3. Start tensorboard: -```shell - tensorboard --bind_all --logdir_spec baseline:./runs/eval/tune_0_acc0.80,tune_1:././runs/eval/tune_1_acc0.79 -``` -# Examples + ``shell + tensorboard --bind_all --logdir_spec baseline:./runs/eval/tune_0_acc0.80,tune_1:././runs/eval/tune_1_acc0.79 + `` + +### Examples ```shell examples/pytorch/image_recognition/imagenet/cpu/ptq/run_tuning_dump_tensor.sh ``` -TensorFlow Tensorboard -================================ -# Design -The implementation of TensorFlow TensorBoard basically have 4 steps: -1. before evaluation we create the TensorBoard summary write and write graph, collect fp32 and node name for inspection and dump the histogram of weights and bias tensor directly from graph_def. -2. Run get_tensor_by_name_with_import() to get data output tensors. -3. Run session.run() to predict and get the inference result of the output tensor list collected in 2. -4. Enumerate the output tensor and write histogram. +## TensorFlow Tensorboard + +### Design -See lpot/adaptor/tensorflow.py evaluate() function for details. +TensorFlow TensorBoard implementation includes four steps: -# Usage +1. Before evaluation where we create the TensorBoard summary write and write graph, collect FP32 and node names for inspection, and dump the histogram of weights and bias tensor directly from graph_def. +2. Run get_tensor_by_name_with_import() where we get data output tensors. +3. Run session.run() to predict and get the inference result of the output tensor list collected in the previous step. +4. Enumerate the output tensor and write the histogram. + +See the [tensorflow.py](https://github.com/intel/lpot/tree/master/lpot/adaptor/tensorflow.py) evaluate() function for details. + +### Usage + +1. Add "tensorboard: true" in the yaml file. + +2. Run quantization tuning; a "./runs" folder is generated in the working folder. For example: -1. Add "tensorboard: true" in yaml file. -2. Run quantization tuning, a "./runs" folder will be generated in working folder. For example: ```shell ls ./runs/eval baseline_acc_0.776 tune_1_acc_0.095 ``` The baseline_acc_0.776 folder contains the FP32 event log and 0.776 is the FP32 accuracy. tune_1_acc_0.095 contains the evaluation event log of the first run of tuning. + 3. Start tensorboard: + ```shell tensorboard --bind_all --logdir_spec baseline:./runs_v3/eval/baseline_acc_0.776/,tune_1:./runs_v3/eval/tune_1_acc_0.095/ ``` -# Examples +### Examples +1. Add "tensorboard: true" into examples/tensorflow/image_recognition/inceptionv3.yaml. In order to demonstrate the usage of TensorBoard, remove the following lines which are added to skip the quantization of 'v0/cg/conv0/conv2d/Conv2D' to avoid a known limitation. -1. Add "tensorboard: true" into examples/tensorflow/image_recognition/inceptionv3.yaml. In order to demonstrate the usage of TensorBoard, please remove the following lines which is added to skip the quantization of 'v0/cg/conv0/conv2d/Conv2D' to avoid a known limitation. -```yaml - op_wise: { - 'v0/cg/conv0/conv2d/Conv2D': { - 'activation': {'dtype': ['fp32']}, + ```yaml + op_wise: { + 'v0/cg/conv0/conv2d/Conv2D': { + 'activation': {'dtype': ['fp32']}, + } } - } -``` -2. Run tuning: -```shell -bash run_tuning.sh --topology=inception_v3 --dataset_location= \ - --input_model=./inceptionv3_fp32_pretrained_model.pb --output_model=./lpot_inceptionv3.pb --config=./inceptionv3_dump_tensor.yaml -``` -3. Start TensorBoard -```shell -tensorboard --bind_all --logdir_spec baseline:./runs_v3/eval/baseline_acc_0.776/,tune_1:./runs_v3/eval/tune_1_acc_0.095/ -``` + ``` -4. In order to find the reason why tune_1 got so poor an accuracy, we can observe the TensorBoard. -1). On the Graphs tab, select "baseline/." in "Run" box, find the first 'Conv2d' op after 'input' op, the op name is "v0/cg/conv0/Relu". +2. Run tuning: + ```shell + bash run_tuning.sh --topology=inception_v3 --dataset_location= \ + --input_model=./inceptionv3_fp32_pretrained_model.pb --output_model=./lpot_inceptionv3.pb --config=./inceptionv3_dump_tensor.yaml + ``` -
- -
+3. Start TensorBoard -2). On the Graphs tab, select "tune_1/." in "Run" box, find the first 'Conv2d' op after 'input' op, the tensor name is 'v0/cg/conv0/conv2d/Conv2D_eightbit_requantize'. + ```shell + tensorboard --bind_all --logdir_spec baseline:./runs_v3/eval/baseline_acc_0.776/,tune_1:./runs_v3/eval/tune_1_acc_0.095/ + ``` +4. In order to find the reason why tune_1 got such poor accuracy, we can observe the TensorBoard. -
- -
+* From the **GRAPHS** tab, select "baseline/." in the "Run" box and find the first 'Conv2d' op after 'input' op. The op name is "v0/cg/conv0/Relu": -3). Switch to the Histograms tab, click op name 'v0/cg/conv0' in the search box, the TensorBoard will group the tensors with the same op name together, you can compare the tensor of baseline 'v0/cg/conv0/Relu' with the tensor of tune_1 'v0/cg/conv0/conv2d/Conv2D_eightbit_requantize_int8.output'. Please note the tensor name could be changed after quantization, so please group the tensor by op name and compare. From the chart we can see the histogram of the first conv2d output tensor are different. The issue is due to a known issue of TensorFlow. After filter the op 'v0/cg/conv0/conv2d/Conv2D' by adding "op_wise" in yaml file, the issue will disappear. - +![TensorBoard Baseline](imgs/tensorboard_baseline_v0_cg_conv0.png "TensorBoard Baseline") -
- -
+* From the **GRAPHS** tab, select "tune_1/." in the "Run" box and find the first 'Conv2d' op after 'input' op. The tensor name is 'v0/cg/conv0/conv2d/Conv2D_eightbit_requantize': - +![TensorBoard Tuning](imgs/tensorboard_tune_1_v0_cg_conv0.png "TensorBoard Tuning") +* Switch to the **HISTOGRAMS** tab. Click the 'v0/cg/conv0' op name in the search box. TensorBoard groups the tensors with the same op name together so you can compare the tensor of baseline 'v0/cg/conv0/Relu' with the tensor of tune_1 'v0/cg/conv0/conv2d/Conv2D_eightbit_requantize_int8.output'. Note that the tensor name can be changed after quantization, so group the tensor by op name and compare. From the chart below, we can see that the histogram of the first conv2d output tensor are different. This is due to a known TensorFlow issue. After filtering the 'v0/cg/conv0/conv2d/Conv2D' op by adding "op_wise" in the yaml file, the issue disappears. + +![TensorBoard Histogram](imgs/tensorboard_v0_cg_conv0_histogram.png "TensorBoard Histogram") diff --git a/docs/transform.md b/docs/transform.md index aefb5074e86..c9528d8c102 100644 --- a/docs/transform.md +++ b/docs/transform.md @@ -1,6 +1,7 @@ Transform -==================== -LPOT supports builtin preprocessing methods on diffrent framework backend. Pleaes refer to 'examples/helloworld/tf_example1' about how to config a transform in dataloader. +========= + +LPOT supports built-in preprocessing methods on different framework backends. Refer to [this HelloWorld example](/examples/helloworld/tf_example1) on how to configure a transform in a dataloader. ## Transform support list diff --git a/docs/tuning_strategies.md b/docs/tuning_strategies.md index d3a9a109960..89e93ec011f 100644 --- a/docs/tuning_strategies.md +++ b/docs/tuning_strategies.md @@ -1,4 +1,5 @@ -# Tuning Strategies +Tuning Strategies +================= ## Introduction @@ -16,9 +17,7 @@ Each strategy generates the next quantization configuration according to its logic and the last quantization result. The function of strategies is shown below: -
- -
+![Tuning Strategy](imgs/strategy.png "Strategy Framework") Strategies begin with an adaptor layer (Framework Adaptor) where the user passes a framework-specific model to initialize an instance of the @@ -33,7 +32,7 @@ tuning phase stops when the `accuracy` criteria is met. ## Configurations -Detailed configuration templates can be found in [`here`](../lpot/template). +Detailed configuration templates can be found [here](../lpot/template). ### Model-specific configurations @@ -307,8 +306,7 @@ tuning: ``` -Customize a New Tuning Strategy -====================== +## Customize a New Tuning Strategy Intel® Low Precision Optimization Tool supports new strategy extension by implementing a subclass of `TuneStrategy` class in lpot.strategy package and registering this strategy by `strategy_registry` decorator. @@ -331,4 +329,4 @@ The `next_tune_cfg` function is used to yield the next tune configuration accord all the tuning space till a quantization configuration meets pre-defined accuracy criterion. If the traverse behavior of `TuneStrategy` base class does not meet new strategy requirement, it could re-implement `traverse` function with self own logic. -An example like this is under [TPE Strategy](../lpot/strategy/tpe.py). +An example like this is under [TPE Strategy](../lpot/strategy/strategy.py). diff --git a/docs/tutorial.md b/docs/tutorial.md index f087469f60a..ab1f104a337 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -1,37 +1,32 @@ Tutorial -========================================= +======== -This tutorial will introduce step by step instructions on how to integrate models with Intel® Low Precision Optimization Tool (LPOT) with examples. +This tutorial provides instructions (with examples) on how to integrate models with Intel® Low Precision Optimization Tool (LPOT). -
Framework
- - - - - - -
Steps of enabling model with LPOT
+The following diagram shows steps for enabling model with LPOT: -# Usage Examples +![Tutorial](imgs/tutorial.png "Tutorial") -To write lanuncher code, user need prepare four components: +## Usage Examples -1. `Dataloader/Dataset` -2. `Model` -3. `Postprocess` *optional* -4. `Metric` +To write lanuncher code, a user needs to prepare four components: -LPOT will construct the whole quantization/pruning process by these four ingredients. +* `Dataloader/Dataset` +* `Model` +* `Postprocess` *optional* +* `Metric` -LPOT has added built-in supports on popular dataloader/dataset and metric to ease the preparation. Please refer to [dataset](./dataset.md) and [metric](./metric.md) to know how to use them in yaml. +LPOT constructs the whole quantization/pruning process using these four components. -LPOT also supports register custom dataset and custom metric by code. +LPOT has added built-in support for popular dataloaders/datasets and metrics to ease the preparation. Refer to [dataset](./dataset.md) and [metric](./metric.md) to learn how to use them in yaml. -As for model, LPOT abstract a common API, named as [lpot.experimental.common.Model](../lpot/experimental/common/model.py), to cover the case in which model, weight, and other necessary info, are separately stored. Please refer to [model](./model.md) to know how to use it. +LPOT also supports registering custom datasets and custom metrics by code. -Postprocess is treat as a specical transform by LPOT which is only needed when model output is mismatching with the expected input of LPOT built-in metrics. if user is using custom metric, the postprocess is not needed indeed as custom metric implementation need ensure it can handle model output correctly. On the other hand, the postprocess logic becomes part of custom metric implementation. +As for model, LPOT abstract a common API, named [lpot.experimental.common.Model](../lpot/experimental/common/model.py), to cover the case in which model, weight, and other necessary info are separately stored. Refer to [model](./model.md) to learn how to use it. -Below is an example of how to enable LPOT on TensorFlow mobilenet_v1 with built-in dataloader, dataset and metric. +Postprocess is treated as a specical transform by LPOT which is only needed when a model output is mismatching with the expected input of LPOT built-in metrics. If a user is using a custom metric, the postprocess is not needed as the custom metric implementation needed ensures it can handle the model output correctly. On the other hand, the postprocess logic becomes part of the custom metric implementation. + +The example below shows how to enable LPOT on TensorFlow mobilenet_v1 with a built-in dataloader, dataset, and metric. ```python # main.py @@ -76,9 +71,9 @@ evaluation: ``` -In this example, we use a LPOT built-in dataset “ImageRecord” and metric “topk”. +In this example, we use an LPOT built-in `ImageRecord` dataset and a `topk` metric. -If user wants to use a dataset or metric which does not support by LPOT built-in, user could register a custom one like below helloworld example. +If the user wants to use a dataset or metric that is not supported by the LPOT built-in, the user can register a custom one as demonstrated in the below helloworld example. ```python # main.py @@ -123,11 +118,11 @@ quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=1) quantizer.model = common.Model('../models/simple_model') q_model = quantizer() ``` -Note: - -In the customized dataset, the `__getitem__()` interface must be implemented and return single sample and label. In this example, it returns the (image, label) pair. User could return (image, 0) for label-free case. +> **Note** +> +> In the customized dataset, the `__getitem__()` interface must be implemented and return a single sample and label. In this example, it returns the (image, label) pair. The user can return (image, 0) for a label-free case. -In the customized metric, the update() function records the predict result of each mini-batch, the result() function would be invoked by LPOT at the end of evaluation to return a scalar to reflect model accuracy. By default, this scalar is higher-is-better. If this scalar returned from customerized metric is a lower-is-better value, `tuning.accuracy_criterion.higher_is_better` in yaml should be set to `False`. +In the customized metric, the update() function records the predicted result of each mini-batch. The result() function is invoked by LPOT at the end of the evaluation to return a scalar to reflect model accuracy. By default, this scalar is higher-is-better. If this scalar returned from the customerized metric is a lower-is-better value, `tuning.accuracy_criterion.higher_is_better` in yaml should be set to `False`. ```yaml # conf.yaml @@ -145,10 +140,10 @@ tuning: random_seed: 100 ``` -# Helloworld Examples +## Helloworld Examples -1. Builtin dataloader and metric example: see [README](../examples/helloworld/tf_example1/README.md) for more details. -2. TensorFlow checkpoint: see [tf_example4](../examples/helloworld/tf_example4) for more details. -3. Enable benchmark for performance and accuracy measurement: see [tf_example5](../examples/helloworld/tf_example5) for more details. -4. TensorFlow slim model: see [tf_example3](../examples/helloworld/tf_example3/README.md) for more details. +1. Built-in dataloader and metric example: see [tf_example1](/examples/helloworld/tf_example1) for more details. +2. TensorFlow checkpoint: see [tf_example4](/examples/helloworld/tf_example4) for more details. +3. Enable benchmark for performance and accuracy measurement: see [tf_example5](/examples/helloworld/tf_example5) for more details. +4. TensorFlow slim model: see [tf_example3](/examples/helloworld/tf_example3) for more details. diff --git a/docs/ux.md b/docs/ux.md index 3017bad9d41..15f9ba9c890 100644 --- a/docs/ux.md +++ b/docs/ux.md @@ -1,95 +1,92 @@ -Intel® Low Precision Optimization Tool UX -========================================= +LPOT UX +======= + +## Start the UX + +1. Start the LPOT UX server: -# Starting the UX -1. Start LPOT UX server: ```shell lpot_ux ``` -1. Server prints information how to access the Web UI. +2. The server prints information on how to access the Web UI. + + An example message looks like this: - Example message looks like: ```text LPOT UX Server started. Setup port forwarding from your local port 5000 to 5000 on this machine. Then open address http://localhost:5000/?token=338174d13706855fc6924cec7b3a8ae8 ``` - Please make sure that requested port forwarding is set up (depending on your OS), then open address in your web browser + Make certain that requested port forwarding is set up (depending on your OS) and then open the address in your web browser. + +## My Models list -# My Models list -This view lists all Model Configurations defined on given server. +This view lists all Model Configurations defined on a given server. -You can create new model using pre-defined models using a New Model Wizard or Examples +You can create a new model using pre-defined models by using a New Model Wizard or **Examples**: ![My models list](imgs/ux/my_models.png "My models list") -# New Model Configuration from New Model Wizard -## Basic parameters -1. If you have all related files in one directory, you can point your Workspace there. +## New Model Configuration from New Model Wizard +### Basic parameters + +1. If all related files are located in one directory, point your Workspace there. Click the ![Change Current Workspace Button](imgs/ux/workspace_change.png "Change") - button (on the top-left part of UX) and navigate to desired directory. - - In opened modal window, click "Choose" to confirm your selection. -1. Open the wizard by clicking ![Create low precision model button image](imgs/ux/model_create_new.png "Create low precision model") button. -1. In the wizard fill in all required fields (marked by a *) + button (on the top-left part of UX) and navigate to the desired directory. Click **Choose** to confirm your selection. + +2. Open the Wizard by clicking the ![Create low precision model button image](imgs/ux/model_create_new.png "Create low precision model") button. + +3. Enter information in all required fields (marked by a *) in the Wizard: ![Basic parameters wizard](imgs/ux/wizard_basic.png "Basic parameters") -1. You can either save this configuration (by clicking "Save"), or change some advanced parameters (by clicking "Next"). -## Advanced parameters -On advanced parameters page, you can select how more features of tuning, quantization and benchmarking will be configured. - ![Advanced parameters wizard](imgs/ux/wizard_advanced.png "Advanced parameters") +4. Either save this configuration (by clicking **Save**), or change some advanced parameters (by clicking **Next**). + +### Advanced parameters + +From the advanced parameters page, you can configure more features such as tuning, quantization, and benchmarking. + +![Advanced parameters wizard](imgs/ux/wizard_advanced.png "Advanced parameters") + +## New Model Configuration from Examples -# New Model Configuration from Examples ![Examples](imgs/ux/examples.png "Examples") -We have some models prepared to test the tuning with. Visit "Examples" tab to: -1. Download a model to selected Workspace. -1. Download predefined configuration file for models. -1. When both model and configuration are downloaded, you can point to Dataset to be used and finally click "Add to my models". -1. New model will be added to "My models" list, ready for tuning. - -# Custom dataset or metric -If you choose "custom" in Dataset or Metric section, appropriate code template will be generated for you to fill in with your code. - -Path to the template will be available by clicking "Copy code template path" button in right-most column in My models list. - -Follow the comments in generated code template to fill in required methods with your own code. - -# Bert model tuning -![Bert model Wizard](imgs/ux/model_create_bert.png "Bert model Wizard") -1. Follow [instructions](https://github.com/intel/lpot/blob/master/examples/tensorflow/nlp/bert/README.md) to: - * install Intel Tensorflow 1.15 up2 - * prepare dataset and a frozen pb model -1. In the "Create low precision model": - * select created frozen model - * choose NLP as model domain - * select input_file, batch_size in inputs (in that order) - * choose "custom" in output and enter `IteratorGetNext:3, unstack:0, unstack:1` in input field - * in "Calibration/label_file", select "dev-v1.1.json" file from dataset created in step 1 - * in "Calibration/dataset location", select "evel.tf_record" file from dataset created in step 1 - * in "Evaluation/Transforms/label_file", select "dev-v1.1.json" file from dataset created in step 1 - * in "Evaluation/Transforms/vocab_file", select "vocab.txt" file from dataset created in step 1 - * click Finish or change Advanced parameters - -# Tuning -Having a Model Configuration created, you can: -1. See generated config (by clicking the "Show config" link). -1. Start the tuning: - 1. Click the blue arrow ![Start Tuning button](imgs/ux/tuning_start.png "Start tuning") to start the tuning. - 1. You can now click on "Show output" link to see logs generated during tuning. - 1. Your model will be tuned according to configuration. - 1. When tuning is finished, you will see accuracy results in My models list: - - "Accuracy" section will display comparison in accuracy metric between original and tuned model, - - "Model size" will compare sizes of both models - - when automatic benchmarking is finished, "Throughput" will show performance gain from tuning - -# Advanced options -## TLS connection encryption - -You can provide your own certificate and key to the server in order to use TLS encrypted communication between UI and server. - -Add two parameters to server start command: + +Included are models you can use to test tuning. Visit **Examples** to: + +* Download a model to a selected Workspace. +* Download a predefined configuration file for models. + +When both models and configurations are downloaded, you can point to the Dataset to be used and then click **Add to my models**. A new model will be added to the **My models** list, ready for tuning. + +## Custom dataset or metric + +If you choose **custom** in the Dataset or Metric section, the appropriate code templates will be generated for you to fill in with your code. The path to the template will be available by clicking the **Copy code template path** button located in the right-most column in the **My models** list. + +Follow the comments in the generated code template to fill in required methods with your own code. + +## Tuning + +Now that you have created a Model Configuration, you can do the following: + +* See the generated config (by clicking the **Show config** link). +* Start the tuning process: + * Click the blue arrow ![Start Tuning button](imgs/ux/tuning_start.png "Start tuning") to start the tuning. + * Click **Show output** to see logs that are generated during tuning. + * Your model will be tuned according to configuration. + * When the tuning is finished, you will see accuracy results in the **My Models** list: + - The **Accuracy** section displays comparisons in accuracy metrics between the original and tuned models. + - **Model size** compares the sizes of both models. + - When automatic benchmarking is finished, **Throughput** shows the performance gain from tuning. + +## Advanced options +### TLS connection encryption + +You can provide your own certificate and key to the server in order to use TLS-encrypted communication between the UI and the server. + +Add two parameters to the server start command: + ``` lpot_ux --certfile path_to_cert.crt --keyfile path_to_private_key.key ``` diff --git a/examples/helloworld/README.md b/examples/helloworld/README.md index 1a240794dfb..263a9d868a1 100644 --- a/examples/helloworld/README.md +++ b/examples/helloworld/README.md @@ -1,3 +1,5 @@ +# Hello World Examples + Enter the following commands to prepare a dataset and pretrained models for the included Hello World examples: ```shell diff --git a/examples/pytorch/huggingface_models/README.md b/examples/pytorch/huggingface_models/README.md index d63acaed93d..ebf16ef2d2f 100644 --- a/examples/pytorch/huggingface_models/README.md +++ b/examples/pytorch/huggingface_models/README.md @@ -1,11 +1,8 @@ -# Original BERT README - -Please refer [BERT README](BERT_README.md) and [README](examples/text-classification/README.md) - Step-by-Step ============ This document is used to list steps of reproducing PyTorch BERT tuning zoo result. +Original BERT documents please refer to [BERT README](BERT_README.md) and [README](examples/text-classification/README.md). > **Note** > diff --git a/examples_readme.md b/examples_readme.md new file mode 100644 index 00000000000..2449fb2b1d5 --- /dev/null +++ b/examples_readme.md @@ -0,0 +1,6 @@ +Examples +======== + +A wide variety of examples are provided to demonstrate the usage of Intel® Low Precision Optimization Tool in different frameworks: TensorFlow, PyTorch, MXNet, and ONNX Runtime. + +View the examples from the [LPOT GitHub repo](https://github.com/intel/lpot/tree/master/examples). \ No newline at end of file diff --git a/getting_started.md b/getting_started.md new file mode 100644 index 00000000000..1ce7b84070c --- /dev/null +++ b/getting_started.md @@ -0,0 +1,443 @@ +Getting Started +=============== + +## Installation + +The Intel® LPOT library is released as part of the +[Intel® oneAPI AI Analytics Toolkit](https://software.intel.com/content/www/us/en/develop/tools/oneapi/ai-analytics-toolkit.html) (AI Kit). +The AI Kit provides a consolidated package of Intel's latest deep learning and +machine optimizations all in one place for ease of development. Along with +LPOT, the AI Kit includes Intel-optimized versions of deep learning frameworks +(such as TensorFlow and PyTorch) and high-performing Python libraries to +streamline end-to-end data science and AI workflows on Intel architectures. + + +### Linux Installation + +You can install just the LPOT library from binary or source, or you can get +the Intel-optimized framework together with the LPOT library by installing the +Intel® oneAPI AI Analytics Toolkit. + +#### Install from binary + + ```Shell + # install from pip + pip install lpot + + # install from conda + conda install lpot -c conda-forge -c intel + ``` + +#### Install from source + + ```Shell + git clone https://github.com/intel/lpot.git + cd lpot + pip install -r requirements.txt + python setup.py install + ``` + +#### Install from AI Kit + +The AI Kit, which includes the LPOT +library, is distributed through many common channels, +including from Intel's website, YUM, APT, Anaconda, and more. +Select and [download](https://software.intel.com/content/www/us/en/develop/tools/oneapi/ai-analytics-toolkit/download.html) +the AI Kit distribution package that's best suited for you and follow the +[Get Started Guide](https://software.intel.com/content/www/us/en/develop/documentation/get-started-with-ai-linux/top.html) +for post-installation instructions. + +|[Download AI Kit](https://software.intel.com/content/www/us/en/develop/tools/oneapi/ai-analytics-toolkit/) |[AI Kit Get Started Guide](https://software.intel.com/content/www/us/en/develop/documentation/get-started-with-ai-linux/top.html) | +|---|---| + +### Windows Installation + +**Prerequisites** + +The following prerequisites and requirements must be satisfied for a successful installation: + +- Python version: 3.6 or 3.7 or 3.8 + +- Download and install [anaconda](https://anaconda.org/). + +- Create a virtual environment named lpot in anaconda: + + ```shell + # Here we install python 3.7 for instance. You can also choose python 3.6 & 3.8. + conda create -n lpot python=3.7 + conda activate lpot + ``` + +#### Install from binary + + ```Shell + # install from pip + pip install lpot + + # install from conda + conda install lpot -c conda-forge -c intel + ``` + +#### Install from source + +```shell +git clone https://github.com/intel/lpot.git +cd lpot +pip install -r requirements.txt +python setup.py install +``` + +## Tutorials and Examples + +Read the following resources to learn how to use LPOT. + +### Tutorial + +The [Tutorial](../docs/tutorial.md) provides comprehensive instructions on how to utilize Intel® Low Precision Optimization Tool's features with examples. + +### Examples + +[Examples](examples_readme.md) are provided to demonstrate the usage of Intel® Low Precision Optimization Tool in different frameworks: TensorFlow, PyTorch, MXNet, and ONNX Runtime. Hello World examples are also available. + + +## Developer Documentation + +View LPOT [Documentation](docs/doclist.rst) for getting started, deep dive, and advanced resources to help you use and develop LPOT. + +## System Requirements + +Intel® Low Precision Optimization Tool supports systems based on [Intel 64 architecture or compatible processors](https://en.wikipedia.org/wiki/X86-64), specially optimized for the following CPUs: + +* Intel Xeon Scalable processor (formerly Skylake, Cascade Lake, and Cooper Lake) +* future Intel Xeon Scalable processor (code name Sapphire Rapids) + +Intel® Low Precision Optimization Tool requires installing the pertinent Intel-optimized framework version for TensorFlow, PyTorch, and MXNet. + +### Validated Hardware/Software Environment + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PlatformOSPythonFrameworkVersion
Cascade Lake

Cooper Lake

Skylake
CentOS 7.8

Ubuntu 18.04
3.6

3.7

3.8
TensorFlow2.4.0
2.2.0
1.15.0 UP1
1.15.0 UP2
2.3.0
2.1.0
1.15.2
PyTorch1.5.0+cpu
1.6.0+cpu
IPEX
MXNet1.7.0
1.6.0
ONNX Runtime1.6.0
+ +## Validated Models + +Intel® Low Precision Optimization Tool provides numerous examples to show promising accuracy loss with the best performance gain. A full quantized model list on various frameworks is available in the [Model List](docs/full_model_list.md). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FrameworkversionModeldatasetAccuracyPerformance speed up
INT8 Tuning AccuracyFP32 Accuracy BaselineAcc Ratio[(INT8-FP32)/FP32]Realtime Latency Ratio[FP32/INT8]
tensorflow2.4.0resnet50v1.5ImageNet76.70%76.50%0.26%3.23x
tensorflow2.4.0Resnet101ImageNet77.20%76.40%1.05%2.42x
tensorflow2.4.0inception_v1ImageNet70.10%69.70%0.57%1.88x
tensorflow2.4.0inception_v2ImageNet74.10%74.00%0.14%1.96x
tensorflow2.4.0inception_v3ImageNet77.20%76.70%0.65%2.36x
tensorflow2.4.0inception_v4ImageNet80.00%80.30%-0.37%2.59x
tensorflow2.4.0inception_resnet_v2ImageNet80.10%80.40%-0.37%1.97x
tensorflow2.4.0Mobilenetv1ImageNet71.10%71.00%0.14%2.88x
tensorflow2.4.0ssd_resnet50_v1Coco37.90%38.00%-0.26%2.97x
tensorflow2.4.0mask_rcnn_inception_v2Coco28.90%29.10%-0.69%2.66x
tensorflow2.4.0vgg16ImageNet72.50%70.90%2.26%3.75x
tensorflow2.4.0vgg19ImageNet72.40%71.00%1.97%3.79x
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FrameworkversionmodeldatasetAccuracyPerformance speed up
INT8 Tuning AccuracyFP32 Accuracy BaselineAcc Ratio[(INT8-FP32)/FP32]Realtime Latency Ratio[FP32/INT8]
pytorch1.5.0+cpuresnet50ImageNet75.96%76.13%-0.23%2.63x
pytorch1.5.0+cpuresnext101_32x8dImageNet79.12%79.31%-0.24%2.61x
pytorch1.6.0a0+24aac32bert_base_mrpcMRPC88.90%88.73%0.19%1.98x
pytorch1.6.0a0+24aac32bert_base_colaCOLA59.06%58.84%0.37%2.19x
pytorch1.6.0a0+24aac32bert_base_sts-bSTS-B88.40%89.27%-0.97%2.28x
pytorch1.6.0a0+24aac32bert_base_sst-2SST-291.51%91.86%-0.37%2.30x
pytorch1.6.0a0+24aac32bert_base_rteRTE69.31%69.68%-0.52%2.15x
pytorch1.6.0a0+24aac32bert_large_mrpcMRPC87.45%88.33%-0.99%2.73x
pytorch1.6.0a0+24aac32bert_large_squadSQUAD92.85%93.05%-0.21%2.01x
pytorch1.6.0a0+24aac32bert_large_qnliQNLI91.20%91.82%-0.68%2.69x
diff --git a/index.rst b/index.rst new file mode 100644 index 00000000000..d484d91af2d --- /dev/null +++ b/index.rst @@ -0,0 +1,25 @@ + +Intel® Low Precision Optimization Tool Documentation +#################################################### + +Welcome to the project. + +Sections +******** + +.. toctree:: + :maxdepth: 1 + + README.md + docs/tutorial.md + examples_readme.md + docs/api-introduction.md + docs/doclist.rst + releases_info.md + contributions.md + legal_information.md + security_policy.md + Intel® LPOT repository + + + diff --git a/legal_information.md b/legal_information.md index 49d97c2fe05..c13d450930b 100644 --- a/legal_information.md +++ b/legal_information.md @@ -1,11 +1,37 @@ -Legal Information {#legal_information} -====================================== +Legal Information +================= -# Trademark Information +## License + +Intel® Low Precision Optimization Tool is licensed under [Apache License Version 2.0](http://www.apache.org/licenses/LICENSE-2.0). This software includes components that have separate copyright notices and licensing terms. Your use of the source code for these components is subject to the terms and conditions of the following licenses. + +Apache License Version 2.0: +* [Intel TensorFlow Quantization Tool](https://github.com/IntelAI/tools) + +MIT License: +* [bayesian-optimization](https://github.com/fmfn/BayesianOptimization) + +See the accompanying [license](https://github.com/intel/lpot/tree/master/LICENSE) file for full license text and copyright notices. + + +## Citation + +If you use Intel® Low Precision Optimization Tool in your research or you wish to refer to the tuning results published in the [Validated Models](getting_started.md#validated-models), use the following BibTeX entry. + +``` +@misc{Intel® Low Precision Optimization Tool, + author = {Feng Tian, Chuanqi Wang, Guoming Zhang, Penghui Cheng, Pengxin Yuan, Haihao Shen, and Jiong Gong}, + title = {Intel® Low Precision Optimization Tool}, + howpublished = {\url{https://github.com/intel/lpot}}, + year = {2020} +} +``` + +## Trademarks Intel, the Intel logo, Intel Atom, Intel Core, Intel Xeon Phi, Pentium, VTune, and Xeon are trademarks of Intel Corporation or its subsidiaries. \* Other names and brands may be claimed as the property of others. -(C) Intel Corporation +Copyright, Intel Corporation diff --git a/make.bat b/make.bat new file mode 100644 index 00000000000..695a8b3ecfd --- /dev/null +++ b/make.bat @@ -0,0 +1,36 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build +set SPHINXPROJ=ProjectnameIntelLowPrecisionOptimizationTool + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% + +:end +popd diff --git a/releases_info.md b/releases_info.md new file mode 100644 index 00000000000..b412bf30cdb --- /dev/null +++ b/releases_info.md @@ -0,0 +1,24 @@ +Release +======= + +## Current Release + +The latest LPOT release is v1.3 (April 2021). + +View new feature information and release downloads for the latest and previous releases on GitHub. Validated configurations and distribution sites are located here as well: + +> + +Contact if you need additional assistance. + +## Known Issues + +The MSE tuning strategy does not work with the PyTorch adaptor layer. This strategy requires a comparison between the FP32 and INT8 tensors to decide which op impacts the final quantization accuracy. The PyTorch adaptor layer does not implement this inspect tensor interface. Therefore, do not choose the MSE tuning strategy for PyTorch models. + +## Incompatible Changes + +[LPOT v1.2](https://github.com/intel/lpot/tree/v1.2) introduces incompatible changes in user facing APIs. Please refer to [incompatible changes](./docs/incompatible_changes.md) to know which incompatible changes are made in v1.2. + +[LPOT v1.2.1](https://github.com/intel/lpot/tree/v1.2.1) solves this backward compatible issues introduced in v1.2 by moving new user facing APIs to lpot.experimental package and keep old one as is. Please refer to [API documentation](/api-documentation/api-introduction.md) to know the details of user-facing APIs. + + diff --git a/SECURITY.md b/security_policy.md similarity index 92% rename from SECURITY.md rename to security_policy.md index eb482d90983..71a71eff1b6 100644 --- a/SECURITY.md +++ b/security_policy.md @@ -1,4 +1,5 @@ -# Security Policy +Security Policy +=============== ## Report a Vulnerability diff --git a/sphinx-requirements.txt b/sphinx-requirements.txt new file mode 100644 index 00000000000..71cfc10b849 --- /dev/null +++ b/sphinx-requirements.txt @@ -0,0 +1,5 @@ +sphinx +sphinx-rtd-theme +recommonmark +sphinx-markdown-tables +sphinx-md \ No newline at end of file diff --git a/template.png b/template.png new file mode 100644 index 0000000000000000000000000000000000000000..e70cef88288c2769d4f495211d12af5ca7e3b8b3 GIT binary patch literal 37834 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

#^vx=nR zGs}}X6p*t6lQKOiZ#WUHxOJ(7c$Z2w)9omFw4C`r~ax?omk;Er3V5nV0*~ zo;svGy_Z-|$?T|3<%G{^J|?RC06x^^x3@WX%l^>2LrKmOxC-hA`O zv19+=r#|(m)<12f|H_+6Xn*TdpZcUcv#>DO^Fny<&>^y$eVL{c(H*<9o-hCa0AK-N zu;-=C$-gMG3v#oMjR61vz;cBvSO5S3aFfCnEC2uifF%PREC2ui0E?l61poj5U@>&C v00000EQSsi0001h#n8b5000266e0hA)cu+r1ej>X00000NkvXXu0mjfpQS{) literal 0 HcmV?d00001 diff --git a/welcome.md b/welcome.md new file mode 100644 index 00000000000..c94ab69cbc7 --- /dev/null +++ b/welcome.md @@ -0,0 +1,22 @@ +Introduction to Intel LPOT +========================== + +The Intel® Low Precision Optimization Tool (Intel® LPOT) is an open-source Python library that delivers a unified low-precision inference interface across multiple Intel-optimized Deep Learning (DL) frameworks on both CPUs and GPUs. It supports automatic accuracy-driven tuning strategies, along with additional objectives such as optimizing for performance, model size, and memory footprint. It also provides easy extension capability for new backends, tuning strategies, metrics, and objectives. + +> **Note** +> +> GPU support is under development. + +| Infrastructure | Workflow | +| - | - | +| ![LPOT Infrastructure](./docs/imgs/infrastructure.png "Infrastructure") | ![LPOT Workflow](./docs/imgs/workflow.png "Workflow") | + +Supported Intel-optimized DL frameworks are: +* [TensorFlow\*](https://github.com/Intel-tensorflow/tensorflow), including [1.15.0 UP2](https://github.com/Intel-tensorflow/tensorflow/tree/v1.15.0up2), [1.15.0 UP1](https://github.com/Intel-tensorflow/tensorflow/tree/v1.15.0up1), [2.1.0](https://github.com/Intel-tensorflow/tensorflow/tree/v2.1.0), [2.2.0](https://github.com/Intel-tensorflow/tensorflow/tree/v2.2.0), [2.3.0](https://github.com/Intel-tensorflow/tensorflow/tree/v2.3.0), [2.4.0](https://github.com/Intel-tensorflow/tensorflow/tree/v2.4.0) +* [PyTorch\*](https://pytorch.org/), including [1.5.0+cpu](https://download.pytorch.org/whl/torch_stable.html), [1.6.0+cpu](https://download.pytorch.org/whl/torch_stable.html) +* [Apache\* MXNet](https://mxnet.apache.org), including [1.6.0](https://github.com/apache/incubator-mxnet/tree/1.6.0), [1.7.0](https://github.com/apache/incubator-mxnet/tree/1.7.0) +* [ONNX\* Runtime](https://github.com/microsoft/onnxruntime), including [1.6.0](https://github.com/microsoft/onnxruntime/tree/v1.6.0) + +[Get started](getting_started.md) with installation, tutorials, examples, and more! + +View the Intel® LPOT repo at: . \ No newline at end of file From 7540e43f4cdc2befdbedd78fca257d68775cab20 Mon Sep 17 00:00:00 2001 From: "Tian, Feng" Date: Tue, 11 May 2021 17:44:19 +0800 Subject: [PATCH 07/11] Fix images and installation wording on README (#21) Signed-off-by: Deb Taylor Reviewed-by: Feng Tian Co-authored-by: Deb Taylor --- README.md | 76 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 1b6e6e51f3a..4f66d33a63f 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,30 @@ The Intel® Low Precision Optimization Tool (Intel® LPOT) is an open-source Pyt > > GPU support is under development. -| Infrastructure | Workflow | -| - | - | -| ![LPOT Infrastructure](./docs/imgs/infrastructure.png "Infrastructure") | ![LPOT Workflow](./docs/imgs/workflow.png "Workflow") | +**Visit the Intel® LPOT online document website at: .** + +## Architecture + +Intel® LPOT features an infrastructure and workflow that aids in increasing performance and faster deployments across architectures. + + +#### Infrastructure + + + Infrastructure + + +Click the image to enlarge it. + +#### Workflow + + + Workflow + + +Click the image to enlarge it. + +#### Supported Frameworks Supported Intel-optimized DL frameworks are: * [TensorFlow\*](https://github.com/Intel-tensorflow/tensorflow), including [1.15.0 UP2](https://github.com/Intel-tensorflow/tensorflow/tree/v1.15.0up2), [1.15.0 UP1](https://github.com/Intel-tensorflow/tensorflow/tree/v1.15.0up1), [2.1.0](https://github.com/Intel-tensorflow/tensorflow/tree/v2.1.0), [2.2.0](https://github.com/Intel-tensorflow/tensorflow/tree/v2.2.0), [2.3.0](https://github.com/Intel-tensorflow/tensorflow/tree/v2.3.0), [2.4.0](https://github.com/Intel-tensorflow/tensorflow/tree/v2.4.0) @@ -17,36 +38,32 @@ Supported Intel-optimized DL frameworks are: * [Apache\* MXNet](https://mxnet.apache.org), including [1.6.0](https://github.com/apache/incubator-mxnet/tree/1.6.0), [1.7.0](https://github.com/apache/incubator-mxnet/tree/1.7.0) * [ONNX\* Runtime](https://github.com/microsoft/onnxruntime), including [1.6.0](https://github.com/microsoft/onnxruntime/tree/v1.6.0) -**Visit the Intel® LPOT website at: .** ## Installation -The Intel® LPOT library is released as part of the -[Intel® oneAPI AI Analytics Toolkit](https://software.intel.com/content/www/us/en/develop/tools/oneapi/ai-analytics-toolkit.html) (AI Kit). -The AI Kit provides a consolidated package of Intel's latest deep learning and -machine optimizations all in one place for ease of development. Along with -LPOT, the AI Kit includes Intel-optimized versions of deep learning frameworks -(such as TensorFlow and PyTorch) and high-performing Python libraries to -streamline end-to-end data science and AI workflows on Intel architectures. +Select the installation based on your operating system. ### Linux Installation -You can install just the LPOT library from binary or source, or you can get -the Intel-optimized framework together with the LPOT library by installing the -Intel® oneAPI AI Analytics Toolkit. +You can install LPOT using one of three options: Install just the LPOT library +from binary or source, or get the Intel-optimized framework together with the +LPOT library by installing the [Intel® oneAPI AI Analytics Toolkit](https://software.intel.com/content/www/us/en/develop/tools/oneapi/ai-analytics-toolkit.html). -#### Install from binary +#### Option 1 Install from binary ```Shell - # install from pip + # install stable version from pip pip install lpot - # install from conda + # install nightly version from pip + pip install -i https://test.pypi.org/simple/ lpot + + # install stable version from from conda conda install lpot -c conda-forge -c intel ``` -#### Install from source +#### Option 2 Install from source ```Shell git clone https://github.com/intel/lpot.git @@ -55,10 +72,17 @@ Intel® oneAPI AI Analytics Toolkit. python setup.py install ``` -#### Install from AI Kit +#### Option 3 Install from AI Kit + +The Intel® LPOT library is released as part of the +[Intel® oneAPI AI Analytics Toolkit](https://software.intel.com/content/www/us/en/develop/tools/oneapi/ai-analytics-toolkit.html) (AI Kit). +The AI Kit provides a consolidated package of Intel's latest deep learning and +machine optimizations all in one place for ease of development. Along with +LPOT, the AI Kit includes Intel-optimized versions of deep learning frameworks +(such as TensorFlow and PyTorch) and high-performing Python libraries to +streamline end-to-end data science and AI workflows on Intel architectures. -The AI Kit, which includes the LPOT -library, is distributed through many common channels, +The AI Kit is distributed through many common channels, including from Intel's website, YUM, APT, Anaconda, and more. Select and [download](https://software.intel.com/content/www/us/en/develop/tools/oneapi/ai-analytics-toolkit/download.html) the AI Kit distribution package that's best suited for you and follow the @@ -85,18 +109,22 @@ The following prerequisites and requirements must be satisfied for a successful conda create -n lpot python=3.7 conda activate lpot ``` +**Installation options** -#### Install from binary +#### Option 1 Install from binary ```Shell - # install from pip + # install stable version from pip pip install lpot + # install nightly version from pip + pip install -i https://test.pypi.org/simple/ lpot + # install from conda conda install lpot -c conda-forge -c intel ``` -#### Install from source +#### Option 2 Install from source ```shell git clone https://github.com/intel/lpot.git From 55a332f18a6f128b16f40a7b8dc5217b77050e90 Mon Sep 17 00:00:00 2001 From: Feng Tian Date: Fri, 23 Apr 2021 16:15:22 +0800 Subject: [PATCH 08/11] Remove unused images and fix some issues in metric.md --- docs/imgs/template.png | Bin 37834 -> 0 bytes docs/metric.md | 29 +++++++++++++++++------------ template.png | Bin 37834 -> 0 bytes 3 files changed, 17 insertions(+), 12 deletions(-) delete mode 100644 docs/imgs/template.png delete mode 100644 template.png diff --git a/docs/imgs/template.png b/docs/imgs/template.png deleted file mode 100644 index e70cef88288c2769d4f495211d12af5ca7e3b8b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37834 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

#^vx=nR zGs}}X6p*t6lQKOiZ#WUHxOJ(7c$Z2w)9omFw4C`r~ax?omk;Er3V5nV0*~ zo;svGy_Z-|$?T|3<%G{^J|?RC06x^^x3@WX%l^>2LrKmOxC-hA`O zv19+=r#|(m)<12f|H_+6Xn*TdpZcUcv#>DO^Fny<&>^y$eVL{c(H*<9o-hCa0AK-N zu;-=C$-gMG3v#oMjR61vz;cBvSO5S3aFfCnEC2uifF%PREC2ui0E?l61poj5U@>&C v00000EQSsi0001h#n8b5000266e0hA)cu+r1ej>X00000NkvXXu0mjfpQS{) diff --git a/docs/metric.md b/docs/metric.md index b937e60bb01..d2dd0ccc121 100644 --- a/docs/metric.md +++ b/docs/metric.md @@ -66,11 +66,13 @@ Refer to [this HelloWorld example](/examples/helloworld/tf_example1) on how to c | topk(k) | k (int, default=1): Number of top elements to look at for computing accuracy | preds, labels | Computes top k predictions accuracy. | metric:
   topk:
     k: 1 | | Accuracy() | None | preds, labels | Computes accuracy classification score. | metric:
   Accuracy: {} | | Loss() | None | preds, labels | A dummy metric for directly printing loss, it calculates the average of predictions.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/_modules/mxnet/metric.html#Loss) for details. | metric:
   Loss: {} | -| MAE() | None | preds, labels | Computes Mean Absolute Error (MAE) loss. | metric:
   MAE: {} | -| RMSE() | None | preds, labels | Computes Root Mean Squred Error (RMSE) loss. | metric:
   RMSE: {} | -| MSE() | None | preds, labels | Computes Mean Squared Error (MSE) loss. | metric:
   MSE: {} | +| MAE(compare_label) | compare_label (bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels | preds, labels | Computes Mean Absolute Error (MAE) loss. | metric:
   MAE: {} | +| RMSE(compare_label) | compare_label (bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels | preds, labels | Computes Root Mean Squred Error (RMSE) loss. | metric:
   RMSE: {} | +| MSE(compare_label) | compare_label (bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels | preds, labels | Computes Mean Squared Error (MSE) loss. | metric:
   MSE: {} | | F1() | None | preds, labels | Computes the F1 score of a binary classification problem. | metric:
   F1: {} | -| COCOmAP(anno_path) | anno_path(str, default=None):annotation path | preds, labels | preds is a tuple which supports 2 length: 3 and 4.
If its length is 3, it should contain boxes, scores, classes in turn.
If its length is 4, it should contain target_boxes_num, boxes, scores, classes in turn
labels is a tuple which contains bbox, str_label, int_label, image_id inturn
the length of one of str_label and int_label can be 0 | metric:
   COCOmAP:
     anno_path: /path/to/annotation
If anno_path is not set, metric will use built-in coco_label_map | +| mAP( anno_path, iou_thrs, map_points) | anno_path (str): Annotation path.
iou_thrs (float or str, default=0.5): Minimal value for intersection over union that allows to make decision that prediction bounding box is true positive. You can specify one float value between 0 to 1 or string "05:0.05:0.95" for standard COCO thresholds.
map_points (int, default=0): The way to calculate mAP. 101 for 101-point interpolated AP, 11 for 11-point interpolated AP, 0 for area under PR curve. | preds, labels | preds is a tuple which supports 2 length: 3 and 4.
If its length is 3, it should contain boxes, scores, classes in turn.
If its length is 4, it should contain target_boxes_num, boxes, scores, classes in turn
labels is a tuple which contains bbox, str_label, int_label, image_id inturn
the length of one of str_label and int_label can be 0 | metric:
   mAP:
     anno_path: /path/to/annotation
     iou_thrs: 0.5
     map_points: 0

If anno_path is not set, metric will use built-in coco_label_map | +| COCOmAP( anno_path, iou_thrs, map_points) | anno_path (str): Annotation path.
iou_thrs (float or str): Intersection over union threshold. Set to "0.5:0.05:0.95" for standard COCO thresholds.
map_points (int): The way to calculate mAP. Set to 101 for 101-point interpolated AP. | preds, labels | preds is a tuple which supports 2 length: 3 and 4.
If its length is 3, it should contain boxes, scores, classes in turn.
If its length is 4, it should contain target_boxes_num, boxes, scores, classes in turn
labels is a tuple which contains bbox, str_label, int_label, image_id inturn
the length of one of str_label and int_label can be 0 | metric:
   COCOmAP:
     anno_path: /path/to/annotation

If anno_path is not set, metric will use built-in coco_label_map | +| VOCmAP( anno_path, iou_thrs, map_points) | anno_path(str): Annotation path.
iou_thrs(float or str): Intersection over union threshold. Set to 0.5.
map_points(int): The way to calculate mAP. The way to calculate mAP. Set to 0 for area under PR curve. | preds, labels | preds is a tuple which supports 2 length: 3 and 4.
If its length is 3, it should contain boxes, scores, classes in turn.
If its length is 4, it should contain target_boxes_num, boxes, scores, classes in turn
labels is a tuple which contains bbox, str_label, int_label, image_id inturn
the length of one of str_label and int_label can be 0 | metric:
   VOCmAP:
     anno_path: /path/to/annotation

If anno_path is not set, metric will use built-in coco_label_map | | BLEU() | None | preds, labels | BLEU score computation between labels and predictions. An approximate BLEU scoring method since we do not glue word pieces or decode the ids and tokenize the output. By default, we use ngram order of 4 and use brevity penalty. Also, this does not have beam search | metric:
   BLEU: {} | | SquadF1() | None | preds, labels | Evaluate v1.1 of the SQuAD dataset | metric:
   SquadF1: {} | @@ -82,9 +84,9 @@ Refer to [this HelloWorld example](/examples/helloworld/tf_example1) on how to c | topk(k) | k (int, default=1): Number of top elements to look at for computing accuracy | preds, labels | Calculates the top-k categorical accuracy. | metric:
   topk:
     k: 1 | | Accuracy() | None | preds, labels | Calculates the accuracy for binary, multiclass and multilabel data.
Please refer [Pytorch docs](https://pytorch.org/ignite/metrics.html#ignite.metrics.Accuracy) for details. | metric:
   Accuracy: {} | | Loss() | None | preds, labels | A dummy metric for directly printing loss, it calculates the average of predictions.
Please refer [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/_modules/mxnet/metric.html#Loss) for details. | metric:
   Loss: {} | -| MAE() | None | preds, labels | Calculates the mean absolute error.
Please refer [Pytorch docs](https://pytorch.org/ignite/metrics.html#ignite.metrics.MeanAbsoluteError) for details. | metric:
   MAE: {} | -| RMSE() | None | preds, labels | Calculates the root mean squared error.
Please refer [Pytorch docs](https://pytorch.org/ignite/metrics.html#ignite.metrics.RootMeanSquaredError) for details. | metric:
   RMSE: {} | -| MSE() | None | preds, labels | Calculates the mean squared error.
Please refer [Pytorch docs](https://pytorch.org/ignite/metrics.html#ignite.metrics.MeanSquaredError) for details. | metric:
   MSE: {} | +| MAE(compare_label) | compare_label(bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Mean Absolute Error (MAE) loss. | metric:
   MAE:
     compare_label: True | +| RMSE(compare_label) | compare_label(bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Root Mean Squred Error (RMSE) loss. | metric:
   RMSE:
     compare_label: True | +| MSE(compare_label) | compare_label(bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Mean Squared Error (MSE) loss. | metric:
   MSE:
     compare_label: True |(https://pytorch.org/ignite/metrics.html#ignite.metrics.MeanSquaredError) for details. | metric:
   MSE: {} | | F1() | None | preds, labels | Computes the F1 score of a binary classification problem. | metric:
   F1: {} | #### MXNet @@ -95,7 +97,7 @@ Refer to [this HelloWorld example](/examples/helloworld/tf_example1) on how to c | Accuracy() | None | preds, labels | Computes accuracy classification score.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/api/metric/index.html#mxnet.metric.Accuracy) for details. | metric:
   Accuracy: {} | | Loss() | None | preds, labels | A dummy metric for directly printing loss, it calculates the average of predictions.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/_modules/mxnet/metric.html#Loss) for details. | metric:
   Loss: {} | | MAE() | None | preds, labels | Computes Mean Absolute Error (MAE) loss.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/api/metric/index.html#mxnet.metric.MAE) for details. | metric:
   MAE: {} | -| RMSE() | None | preds, labels | Computes Root Mean Squred Error (RMSE) loss.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/api/metric/index.html#mxnet.metric.RMSE) for details. | metric:
   RMSE: {} | +| RMSE(compare_label) | compare_label(bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Root Mean Squred Error (RMSE) loss. | metric:
   RMSE:
     compare_label: True | | MSE() | None | preds, labels | Computes Mean Squared Error (MSE) loss.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/api/metric/index.html#mxnet.metric.MSE) for details. | metric:
   MSE: {} | | F1() | None | preds, labels | Computes the F1 score of a binary classification problem.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/api/metric/index.html#mxnet.metric.F1) for details. | metric:
   F1: {} | @@ -107,7 +109,10 @@ Refer to [this HelloWorld example](/examples/helloworld/tf_example1) on how to c | topk(k) | k (int, default=1): Number of top elements to look at for computing accuracy | preds, labels | Computes top k predictions accuracy. | metric:
   topk:
     k: 1 | | Accuracy() | None | preds, labels |Computes accuracy classification score. | metric:
   Accuracy: {} | | Loss() | None | preds, labels | A dummy metric for directly printing loss, it calculates the average of predictions.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/_modules/mxnet/metric.html#Loss) for details. | metric:
   Loss: {} | -| MAE() | None | preds, labels | Computes Mean Absolute Error (MAE) loss. | metric:
   MAE: {} | -| RMSE() | None | preds, labels | Computes Root Mean Squred Error (RMSE) loss.| metric:
   RMSE: {} | -| MSE() | None | preds, labels | Computes Mean Squared Error (MSE) loss. | metric:
   MSE: {} | -| F1() | None | preds, labels | Computes the F1 score of a binary classification problem. | metric:
   F1: {} | \ No newline at end of file +| MAE(compare_label) | compare_label(bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Mean Absolute Error (MAE) loss. | metric:
   MAE:
     compare_label: True | +| RMSE(compare_label) | compare_label(bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Root Mean Squred Error (RMSE) loss. | metric:
   RMSE:
     compare_label: True | +| MSE(compare_label) | compare_label(bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Mean Squared Error (MSE) loss. | metric:
   MSE:
     compare_label: True | +| F1() | None | preds, labels | Computes the F1 score of a binary classification problem. | metric:
   F1: {} | +| mAP( anno_path, iou_thrs, map_points) | anno_path(str): Annotation path.
iou_thrs(float or str, default=0.5): Minimal value for intersection over union that allows to make decision that prediction bounding box is true positive. You can specify one float value between 0 to 1 or string "05:0.05:0.95" for standard COCO thresholds.
map_points(int, default=0): The way to calculate mAP. 101 for 101-point interpolated AP, 11 for 11-point interpolated AP, 0 for area under PR curve. | preds, labels | preds is a tuple which supports 2 length: 3 and 4.
If its length is 3, it should contain boxes, scores, classes in turn.
If its length is 4, it should contain target_boxes_num, boxes, scores, classes in turn
labels is a tuple which contains bbox, str_label, int_label, image_id inturn
the length of one of str_label and int_label can be 0 | metric:
   mAP:
     anno_path: /path/to/annotation
     iou_thrs: 0.5
     map_points: 0

If anno_path is not set, metric will use built-in coco_label_map | +| COCOmAP( anno_path, iou_thrs, map_points) | anno_path(str): Annotation path.
iou_thrs(float or str): Intersection over union threshold. Set to "0.5:0.05:0.95" for standard COCO thresholds.
map_points(int): The way to calculate mAP. Set to 101 for 101-point interpolated AP. | preds, labels | preds is a tuple which supports 2 length: 3 and 4.
If its length is 3, it should contain boxes, scores, classes in turn.
If its length is 4, it should contain target_boxes_num, boxes, scores, classes in turn
labels is a tuple which contains bbox, str_label, int_label, image_id inturn
the length of one of str_label and int_label can be 0 | metric:
   COCOmAP:
     anno_path: /path/to/annotation

If anno_path is not set, metric will use built-in coco_label_map | +| VOCmAP( anno_path, iou_thrs, map_points) | anno_path(str): Annotation path.
iou_thrs(float or str): Intersection over union threshold. Set to 0.5.
map_points(int): The way to calculate mAP. The way to calculate mAP. Set to 0 for area under PR curve. | preds, labels | preds is a tuple which supports 2 length: 3 and 4.
If its length is 3, it should contain boxes, scores, classes in turn.
If its length is 4, it should contain target_boxes_num, boxes, scores, classes in turn
labels is a tuple which contains bbox, str_label, int_label, image_id inturn
the length of one of str_label and int_label can be 0 | metric:
   VOCmAP:
     anno_path: /path/to/annotation

If anno_path is not set, metric will use built-in coco_label_map | diff --git a/template.png b/template.png deleted file mode 100644 index e70cef88288c2769d4f495211d12af5ca7e3b8b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37834 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

#^vx=nR zGs}}X6p*t6lQKOiZ#WUHxOJ(7c$Z2w)9omFw4C`r~ax?omk;Er3V5nV0*~ zo;svGy_Z-|$?T|3<%G{^J|?RC06x^^x3@WX%l^>2LrKmOxC-hA`O zv19+=r#|(m)<12f|H_+6Xn*TdpZcUcv#>DO^Fny<&>^y$eVL{c(H*<9o-hCa0AK-N zu;-=C$-gMG3v#oMjR61vz;cBvSO5S3aFfCnEC2uifF%PREC2ui0E?l61poj5U@>&C v00000EQSsi0001h#n8b5000266e0hA)cu+r1ej>X00000NkvXXu0mjfpQS{) From 7d6030ba7b186a334043f5b073b2a8cf90bb00d2 Mon Sep 17 00:00:00 2001 From: Feng Tian Date: Wed, 12 May 2021 15:51:48 +0800 Subject: [PATCH 09/11] fix sphinx issue: AttributeError: 'Sphinx' object has no attribute 'add_source_suffix' --- conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conf.py b/conf.py index 9fb1709fab7..8c28a7a3494 100644 --- a/conf.py +++ b/conf.py @@ -164,7 +164,7 @@ ] def setup(app): - app.add_stylesheet("custom.css") + app.add_css_file("custom.css") from os import getenv @@ -181,4 +181,4 @@ def setup(app): githubFileURL = githubFileURL + baseBranch + "/" githubDirURL = githubDirURL + baseBranch + "/" sphinx_md_githubFileURL = githubFileURL -sphinx_md_githubDirURL = githubDirURL \ No newline at end of file +sphinx_md_githubDirURL = githubDirURL From ccdb6aec0a95a66b2c747f92a34018e78a91b459 Mon Sep 17 00:00:00 2001 From: PenghuiCheng Date: Mon, 10 May 2021 17:03:41 +0800 Subject: [PATCH 10/11] Remove invalid code for huggingface models --- .../examples/legacy/README.md | 21 - .../multiple_choice/run_multiple_choice.py | 242 ----- .../multiple_choice/utils_multiple_choice.py | 579 ------------ .../pytorch-lightning/lightning_base.py | 391 --------- .../legacy/pytorch-lightning/requirements.txt | 22 - .../legacy/pytorch-lightning/run_glue.py | 201 ----- .../legacy/pytorch-lightning/run_glue.sh | 34 - .../legacy/pytorch-lightning/run_ner.py | 215 ----- .../legacy/pytorch-lightning/run_ner.sh | 44 - .../legacy/pytorch-lightning/run_pos.sh | 39 - .../legacy/question-answering/run_squad.py | 830 ------------------ .../question-answering/run_squad_trainer.py | 185 ---- .../examples/legacy/run_camembert.py | 47 - .../examples/legacy/run_chinese_ref.py | 148 ---- .../examples/legacy/run_language_modeling.py | 364 -------- .../examples/legacy/run_openai_gpt.py | 320 ------- .../examples/legacy/run_swag.py | 720 --------------- .../examples/legacy/run_transfo_xl.py | 145 --- .../examples/legacy/seq2seq/README.md | 334 ------- .../examples/legacy/seq2seq/__init__.py | 5 - .../legacy/seq2seq/convert_model_to_fp16.py | 36 - .../examples/legacy/seq2seq/download_wmt.py | 67 -- .../examples/legacy/seq2seq/finetune.sh | 24 - .../examples/legacy/seq2seq/finetune_tpu.sh | 26 - .../legacy/seq2seq/finetune_trainer.py | 367 -------- .../examples/legacy/seq2seq/minify_dataset.py | 34 - .../seq2seq/old_test_calculate_rouge.py | 94 -- .../legacy/seq2seq/old_test_datasets.py | 254 ------ .../seq2seq/old_test_fsmt_bleu_score.py | 71 -- .../seq2seq/old_test_seq2seq_examples.py | 131 --- .../old_test_seq2seq_examples_multi_gpu.py | 55 -- .../seq2seq/old_test_tatoeba_conversion.py | 40 - .../examples/legacy/seq2seq/pack_dataset.py | 88 -- .../examples/legacy/seq2seq/requirements.txt | 20 - .../legacy/seq2seq/romanian_postprocessing.md | 65 -- .../examples/legacy/seq2seq/rouge_cli.py | 31 - .../legacy/seq2seq/run_distributed_eval.py | 261 ------ .../examples/legacy/seq2seq/run_eval.py | 182 ---- .../legacy/seq2seq/run_eval_search.py | 151 ---- .../examples/legacy/seq2seq/save_len_file.py | 56 -- .../save_randomly_initialized_model.py | 39 - .../legacy/seq2seq/sentence_splitter.py | 35 - .../legacy/seq2seq/seq2seq_trainer.py | 258 ------ .../legacy/seq2seq/seq2seq_training_args.py | 59 -- .../seq2seq/test_data/fsmt/build-eval-data.py | 33 - .../seq2seq/test_data/fsmt/fsmt_val_data.json | 90 -- .../legacy/seq2seq/test_data/test_data | 1 - .../seq2seq/test_data/wmt_en_ro/test.source | 20 - .../seq2seq/test_data/wmt_en_ro/test.target | 20 - .../seq2seq/test_data/wmt_en_ro/train.len | Bin 26 -> 0 bytes .../seq2seq/test_data/wmt_en_ro/train.source | 11 - .../seq2seq/test_data/wmt_en_ro/train.target | 11 - .../seq2seq/test_data/wmt_en_ro/val.len | Bin 40 -> 0 bytes .../seq2seq/test_data/wmt_en_ro/val.source | 16 - .../seq2seq/test_data/wmt_en_ro/val.target | 16 - .../seq2seq/train_distil_marian_enro.sh | 38 - .../seq2seq/train_distil_marian_enro_tpu.sh | 39 - .../legacy/seq2seq/train_distilbart_cnn.sh | 39 - .../legacy/seq2seq/train_mbart_cc25_enro.sh | 35 - .../examples/legacy/seq2seq/utils.py | 664 -------------- .../examples/legacy/seq2seq/xla_spawn.py | 85 -- .../legacy/token-classification/README.md | 294 ------- .../legacy/token-classification/run.sh | 36 - .../legacy/token-classification/run_chunk.sh | 37 - .../legacy/token-classification/run_ner.py | 321 ------- .../legacy/token-classification/run_pos.sh | 37 - .../legacy/token-classification/run_tf_ner.py | 307 ------- .../scripts/preprocess.py | 41 - .../legacy/token-classification/tasks.py | 163 ---- .../legacy/token-classification/utils_ner.py | 372 -------- 70 files changed, 10056 deletions(-) delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/README.md delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/multiple_choice/run_multiple_choice.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/multiple_choice/utils_multiple_choice.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/lightning_base.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/requirements.txt delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_glue.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_glue.sh delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_ner.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_ner.sh delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_pos.sh delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/question-answering/run_squad.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/question-answering/run_squad_trainer.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/run_camembert.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/run_chinese_ref.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/run_language_modeling.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/run_openai_gpt.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/run_swag.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/run_transfo_xl.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/README.md delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/__init__.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/seq2seq/convert_model_to_fp16.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/seq2seq/download_wmt.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/finetune.sh delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/finetune_tpu.sh delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/seq2seq/finetune_trainer.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/seq2seq/minify_dataset.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_calculate_rouge.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_datasets.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_fsmt_bleu_score.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_seq2seq_examples.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_seq2seq_examples_multi_gpu.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_tatoeba_conversion.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/seq2seq/pack_dataset.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/requirements.txt delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/romanian_postprocessing.md delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/rouge_cli.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/seq2seq/run_distributed_eval.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/seq2seq/run_eval.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/seq2seq/run_eval_search.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/seq2seq/save_len_file.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/seq2seq/save_randomly_initialized_model.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/sentence_splitter.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/seq2seq_trainer.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/seq2seq_training_args.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/fsmt/build-eval-data.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/fsmt/fsmt_val_data.json delete mode 120000 examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/test_data delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/test.source delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/test.target delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/train.len delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/train.source delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/train.target delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/val.len delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/val.source delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/val.target delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/train_distil_marian_enro.sh delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/train_distil_marian_enro_tpu.sh delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/train_distilbart_cnn.sh delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/train_mbart_cc25_enro.sh delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/utils.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/seq2seq/xla_spawn.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/token-classification/README.md delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/token-classification/run.sh delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/token-classification/run_chunk.sh delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/token-classification/run_ner.py delete mode 100755 examples/pytorch/huggingface_models/examples/legacy/token-classification/run_pos.sh delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/token-classification/run_tf_ner.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/token-classification/scripts/preprocess.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/token-classification/tasks.py delete mode 100644 examples/pytorch/huggingface_models/examples/legacy/token-classification/utils_ner.py diff --git a/examples/pytorch/huggingface_models/examples/legacy/README.md b/examples/pytorch/huggingface_models/examples/legacy/README.md deleted file mode 100644 index eaf64f62463..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/README.md +++ /dev/null @@ -1,21 +0,0 @@ - - -# Legacy examples - -This folder contains examples which are not actively maintained (mostly contributed by the community). - -Using these examples together with a recent version of the library usually requires to make small (sometimes big) adaptations to get the scripts working. diff --git a/examples/pytorch/huggingface_models/examples/legacy/multiple_choice/run_multiple_choice.py b/examples/pytorch/huggingface_models/examples/legacy/multiple_choice/run_multiple_choice.py deleted file mode 100644 index bf79f2ac7a8..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/multiple_choice/run_multiple_choice.py +++ /dev/null @@ -1,242 +0,0 @@ -# coding=utf-8 -# Copyright 2018 The Google AI Language Team Authors and The HuggingFace Inc. team. -# Copyright (c) 2018, NVIDIA CORPORATION. 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. -""" Finetuning the library models for multiple choice (Bert, Roberta, XLNet).""" - - -import logging -import os -from dataclasses import dataclass, field -from typing import Dict, Optional - -import numpy as np - -import transformers -from transformers import ( - AutoConfig, - AutoModelForMultipleChoice, - AutoTokenizer, - DataCollatorWithPadding, - EvalPrediction, - HfArgumentParser, - Trainer, - TrainingArguments, - set_seed, -) -from transformers.trainer_utils import is_main_process -from utils_multiple_choice import MultipleChoiceDataset, Split, processors - - -logger = logging.getLogger(__name__) - - -def simple_accuracy(preds, labels): - return (preds == labels).mean() - - -@dataclass -class ModelArguments: - """ - Arguments pertaining to which model/config/tokenizer we are going to fine-tune from. - """ - - model_name_or_path: str = field( - metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"} - ) - config_name: Optional[str] = field( - default=None, metadata={"help": "Pretrained config name or path if not the same as model_name"} - ) - tokenizer_name: Optional[str] = field( - default=None, metadata={"help": "Pretrained tokenizer name or path if not the same as model_name"} - ) - cache_dir: Optional[str] = field( - default=None, - metadata={"help": "Where do you want to store the pretrained models downloaded from huggingface.co"}, - ) - - -@dataclass -class DataTrainingArguments: - """ - Arguments pertaining to what data we are going to input our model for training and eval. - """ - - task_name: str = field(metadata={"help": "The name of the task to train on: " + ", ".join(processors.keys())}) - data_dir: str = field(metadata={"help": "Should contain the data files for the task."}) - max_seq_length: int = field( - default=128, - metadata={ - "help": "The maximum total input sequence length after tokenization. Sequences longer " - "than this will be truncated, sequences shorter will be padded." - }, - ) - overwrite_cache: bool = field( - default=False, metadata={"help": "Overwrite the cached training and evaluation sets"} - ) - - -def main(): - # See all possible arguments in src/transformers/training_args.py - # or by passing the --help flag to this script. - # We now keep distinct sets of args, for a cleaner separation of concerns. - - parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments)) - model_args, data_args, training_args = parser.parse_args_into_dataclasses() - - if ( - os.path.exists(training_args.output_dir) - and os.listdir(training_args.output_dir) - and training_args.do_train - and not training_args.overwrite_output_dir - ): - raise ValueError( - f"Output directory ({training_args.output_dir}) already exists and is not empty. Use --overwrite_output_dir to overcome." - ) - - # Setup logging - logging.basicConfig( - format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", - datefmt="%m/%d/%Y %H:%M:%S", - level=logging.INFO if training_args.local_rank in [-1, 0] else logging.WARN, - ) - logger.warning( - "Process rank: %s, device: %s, n_gpu: %s, distributed training: %s, 16-bits training: %s", - training_args.local_rank, - training_args.device, - training_args.n_gpu, - bool(training_args.local_rank != -1), - training_args.fp16, - ) - # Set the verbosity to info of the Transformers logger (on main process only): - if is_main_process(training_args.local_rank): - transformers.utils.logging.set_verbosity_info() - transformers.utils.logging.enable_default_handler() - transformers.utils.logging.enable_explicit_format() - logger.info("Training/evaluation parameters %s", training_args) - - # Set seed - set_seed(training_args.seed) - - try: - processor = processors[data_args.task_name]() - label_list = processor.get_labels() - num_labels = len(label_list) - except KeyError: - raise ValueError("Task not found: %s" % (data_args.task_name)) - - # Load pretrained model and tokenizer - # - # Distributed training: - # The .from_pretrained methods guarantee that only one local process can concurrently - # download model & vocab. - - config = AutoConfig.from_pretrained( - model_args.config_name if model_args.config_name else model_args.model_name_or_path, - num_labels=num_labels, - finetuning_task=data_args.task_name, - cache_dir=model_args.cache_dir, - ) - tokenizer = AutoTokenizer.from_pretrained( - model_args.tokenizer_name if model_args.tokenizer_name else model_args.model_name_or_path, - cache_dir=model_args.cache_dir, - ) - model = AutoModelForMultipleChoice.from_pretrained( - model_args.model_name_or_path, - from_tf=bool(".ckpt" in model_args.model_name_or_path), - config=config, - cache_dir=model_args.cache_dir, - ) - - # Get datasets - train_dataset = ( - MultipleChoiceDataset( - data_dir=data_args.data_dir, - tokenizer=tokenizer, - task=data_args.task_name, - max_seq_length=data_args.max_seq_length, - overwrite_cache=data_args.overwrite_cache, - mode=Split.train, - ) - if training_args.do_train - else None - ) - eval_dataset = ( - MultipleChoiceDataset( - data_dir=data_args.data_dir, - tokenizer=tokenizer, - task=data_args.task_name, - max_seq_length=data_args.max_seq_length, - overwrite_cache=data_args.overwrite_cache, - mode=Split.dev, - ) - if training_args.do_eval - else None - ) - - def compute_metrics(p: EvalPrediction) -> Dict: - preds = np.argmax(p.predictions, axis=1) - return {"acc": simple_accuracy(preds, p.label_ids)} - - # Data collator - data_collator = DataCollatorWithPadding(tokenizer, pad_to_multiple_of=8) if training_args.fp16 else None - - # Initialize our Trainer - trainer = Trainer( - model=model, - args=training_args, - train_dataset=train_dataset, - eval_dataset=eval_dataset, - compute_metrics=compute_metrics, - data_collator=data_collator, - ) - - # Training - if training_args.do_train: - trainer.train( - model_path=model_args.model_name_or_path if os.path.isdir(model_args.model_name_or_path) else None - ) - trainer.save_model() - # For convenience, we also re-save the tokenizer to the same directory, - # so that you can share your model easily on huggingface.co/models =) - if trainer.is_world_master(): - tokenizer.save_pretrained(training_args.output_dir) - - # Evaluation - results = {} - if training_args.do_eval: - logger.info("*** Evaluate ***") - - result = trainer.evaluate() - - output_eval_file = os.path.join(training_args.output_dir, "eval_results.txt") - if trainer.is_world_master(): - with open(output_eval_file, "w") as writer: - logger.info("***** Eval results *****") - for key, value in result.items(): - logger.info(" %s = %s", key, value) - writer.write("%s = %s\n" % (key, value)) - - results.update(result) - - return results - - -def _mp_fn(index): - # For xla_spawn (TPUs) - main() - - -if __name__ == "__main__": - main() diff --git a/examples/pytorch/huggingface_models/examples/legacy/multiple_choice/utils_multiple_choice.py b/examples/pytorch/huggingface_models/examples/legacy/multiple_choice/utils_multiple_choice.py deleted file mode 100644 index 784a7578d35..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/multiple_choice/utils_multiple_choice.py +++ /dev/null @@ -1,579 +0,0 @@ -# coding=utf-8 -# Copyright 2018 The Google AI Language Team Authors and The HuggingFace Inc. team. -# Copyright (c) 2018, NVIDIA CORPORATION. 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. -""" Multiple choice fine-tuning: utilities to work with multiple choice tasks of reading comprehension """ - - -import csv -import glob -import json -import logging -import os -from dataclasses import dataclass -from enum import Enum -from typing import List, Optional - -import tqdm - -from filelock import FileLock -from transformers import PreTrainedTokenizer, is_tf_available, is_torch_available - - -logger = logging.getLogger(__name__) - - -@dataclass(frozen=True) -class InputExample: - """ - A single training/test example for multiple choice - - Args: - example_id: Unique id for the example. - question: string. The untokenized text of the second sequence (question). - contexts: list of str. The untokenized text of the first sequence (context of corresponding question). - endings: list of str. multiple choice's options. Its length must be equal to contexts' length. - label: (Optional) string. The label of the example. This should be - specified for train and dev examples, but not for test examples. - """ - - example_id: str - question: str - contexts: List[str] - endings: List[str] - label: Optional[str] - - -@dataclass(frozen=True) -class InputFeatures: - """ - A single set of features of data. - Property names are the same names as the corresponding inputs to a model. - """ - - example_id: str - input_ids: List[List[int]] - attention_mask: Optional[List[List[int]]] - token_type_ids: Optional[List[List[int]]] - label: Optional[int] - - -class Split(Enum): - train = "train" - dev = "dev" - test = "test" - - -if is_torch_available(): - import torch - from torch.utils.data.dataset import Dataset - - class MultipleChoiceDataset(Dataset): - """ - This will be superseded by a framework-agnostic approach - soon. - """ - - features: List[InputFeatures] - - def __init__( - self, - data_dir: str, - tokenizer: PreTrainedTokenizer, - task: str, - max_seq_length: Optional[int] = None, - overwrite_cache=False, - mode: Split = Split.train, - ): - processor = processors[task]() - - cached_features_file = os.path.join( - data_dir, - "cached_{}_{}_{}_{}".format( - mode.value, - tokenizer.__class__.__name__, - str(max_seq_length), - task, - ), - ) - - # Make sure only the first process in distributed training processes the dataset, - # and the others will use the cache. - lock_path = cached_features_file + ".lock" - with FileLock(lock_path): - - if os.path.exists(cached_features_file) and not overwrite_cache: - logger.info(f"Loading features from cached file {cached_features_file}") - self.features = torch.load(cached_features_file) - else: - logger.info(f"Creating features from dataset file at {data_dir}") - label_list = processor.get_labels() - if mode == Split.dev: - examples = processor.get_dev_examples(data_dir) - elif mode == Split.test: - examples = processor.get_test_examples(data_dir) - else: - examples = processor.get_train_examples(data_dir) - logger.info("Training examples: %s", len(examples)) - self.features = convert_examples_to_features( - examples, - label_list, - max_seq_length, - tokenizer, - ) - logger.info("Saving features into cached file %s", cached_features_file) - torch.save(self.features, cached_features_file) - - def __len__(self): - return len(self.features) - - def __getitem__(self, i) -> InputFeatures: - return self.features[i] - - -if is_tf_available(): - import tensorflow as tf - - class TFMultipleChoiceDataset: - """ - This will be superseded by a framework-agnostic approach - soon. - """ - - features: List[InputFeatures] - - def __init__( - self, - data_dir: str, - tokenizer: PreTrainedTokenizer, - task: str, - max_seq_length: Optional[int] = 128, - overwrite_cache=False, - mode: Split = Split.train, - ): - processor = processors[task]() - - logger.info(f"Creating features from dataset file at {data_dir}") - label_list = processor.get_labels() - if mode == Split.dev: - examples = processor.get_dev_examples(data_dir) - elif mode == Split.test: - examples = processor.get_test_examples(data_dir) - else: - examples = processor.get_train_examples(data_dir) - logger.info("Training examples: %s", len(examples)) - - self.features = convert_examples_to_features( - examples, - label_list, - max_seq_length, - tokenizer, - ) - - def gen(): - for (ex_index, ex) in tqdm.tqdm(enumerate(self.features), desc="convert examples to features"): - if ex_index % 10000 == 0: - logger.info("Writing example %d of %d" % (ex_index, len(examples))) - - yield ( - { - "example_id": 0, - "input_ids": ex.input_ids, - "attention_mask": ex.attention_mask, - "token_type_ids": ex.token_type_ids, - }, - ex.label, - ) - - self.dataset = tf.data.Dataset.from_generator( - gen, - ( - { - "example_id": tf.int32, - "input_ids": tf.int32, - "attention_mask": tf.int32, - "token_type_ids": tf.int32, - }, - tf.int64, - ), - ( - { - "example_id": tf.TensorShape([]), - "input_ids": tf.TensorShape([None, None]), - "attention_mask": tf.TensorShape([None, None]), - "token_type_ids": tf.TensorShape([None, None]), - }, - tf.TensorShape([]), - ), - ) - - def get_dataset(self): - self.dataset = self.dataset.apply(tf.data.experimental.assert_cardinality(len(self.features))) - - return self.dataset - - def __len__(self): - return len(self.features) - - def __getitem__(self, i) -> InputFeatures: - return self.features[i] - - -class DataProcessor: - """Base class for data converters for multiple choice data sets.""" - - def get_train_examples(self, data_dir): - """Gets a collection of `InputExample`s for the train set.""" - raise NotImplementedError() - - def get_dev_examples(self, data_dir): - """Gets a collection of `InputExample`s for the dev set.""" - raise NotImplementedError() - - def get_test_examples(self, data_dir): - """Gets a collection of `InputExample`s for the test set.""" - raise NotImplementedError() - - def get_labels(self): - """Gets the list of labels for this data set.""" - raise NotImplementedError() - - -class RaceProcessor(DataProcessor): - """Processor for the RACE data set.""" - - def get_train_examples(self, data_dir): - """See base class.""" - logger.info("LOOKING AT {} train".format(data_dir)) - high = os.path.join(data_dir, "train/high") - middle = os.path.join(data_dir, "train/middle") - high = self._read_txt(high) - middle = self._read_txt(middle) - return self._create_examples(high + middle, "train") - - def get_dev_examples(self, data_dir): - """See base class.""" - logger.info("LOOKING AT {} dev".format(data_dir)) - high = os.path.join(data_dir, "dev/high") - middle = os.path.join(data_dir, "dev/middle") - high = self._read_txt(high) - middle = self._read_txt(middle) - return self._create_examples(high + middle, "dev") - - def get_test_examples(self, data_dir): - """See base class.""" - logger.info("LOOKING AT {} test".format(data_dir)) - high = os.path.join(data_dir, "test/high") - middle = os.path.join(data_dir, "test/middle") - high = self._read_txt(high) - middle = self._read_txt(middle) - return self._create_examples(high + middle, "test") - - def get_labels(self): - """See base class.""" - return ["0", "1", "2", "3"] - - def _read_txt(self, input_dir): - lines = [] - files = glob.glob(input_dir + "/*txt") - for file in tqdm.tqdm(files, desc="read files"): - with open(file, "r", encoding="utf-8") as fin: - data_raw = json.load(fin) - data_raw["race_id"] = file - lines.append(data_raw) - return lines - - def _create_examples(self, lines, set_type): - """Creates examples for the training and dev sets.""" - examples = [] - for (_, data_raw) in enumerate(lines): - race_id = "%s-%s" % (set_type, data_raw["race_id"]) - article = data_raw["article"] - for i in range(len(data_raw["answers"])): - truth = str(ord(data_raw["answers"][i]) - ord("A")) - question = data_raw["questions"][i] - options = data_raw["options"][i] - - examples.append( - InputExample( - example_id=race_id, - question=question, - contexts=[article, article, article, article], # this is not efficient but convenient - endings=[options[0], options[1], options[2], options[3]], - label=truth, - ) - ) - return examples - - -class SynonymProcessor(DataProcessor): - """Processor for the Synonym data set.""" - - def get_train_examples(self, data_dir): - """See base class.""" - logger.info("LOOKING AT {} train".format(data_dir)) - return self._create_examples(self._read_csv(os.path.join(data_dir, "mctrain.csv")), "train") - - def get_dev_examples(self, data_dir): - """See base class.""" - logger.info("LOOKING AT {} dev".format(data_dir)) - return self._create_examples(self._read_csv(os.path.join(data_dir, "mchp.csv")), "dev") - - def get_test_examples(self, data_dir): - """See base class.""" - logger.info("LOOKING AT {} dev".format(data_dir)) - - return self._create_examples(self._read_csv(os.path.join(data_dir, "mctest.csv")), "test") - - def get_labels(self): - """See base class.""" - return ["0", "1", "2", "3", "4"] - - def _read_csv(self, input_file): - with open(input_file, "r", encoding="utf-8") as f: - return list(csv.reader(f)) - - def _create_examples(self, lines: List[List[str]], type: str): - """Creates examples for the training and dev sets.""" - - examples = [ - InputExample( - example_id=line[0], - question="", # in the swag dataset, the - # common beginning of each - # choice is stored in "sent2". - contexts=[line[1], line[1], line[1], line[1], line[1]], - endings=[line[2], line[3], line[4], line[5], line[6]], - label=line[7], - ) - for line in lines # we skip the line with the column names - ] - - return examples - - -class SwagProcessor(DataProcessor): - """Processor for the SWAG data set.""" - - def get_train_examples(self, data_dir): - """See base class.""" - logger.info("LOOKING AT {} train".format(data_dir)) - return self._create_examples(self._read_csv(os.path.join(data_dir, "train.csv")), "train") - - def get_dev_examples(self, data_dir): - """See base class.""" - logger.info("LOOKING AT {} dev".format(data_dir)) - return self._create_examples(self._read_csv(os.path.join(data_dir, "val.csv")), "dev") - - def get_test_examples(self, data_dir): - """See base class.""" - logger.info("LOOKING AT {} dev".format(data_dir)) - raise ValueError( - "For swag testing, the input file does not contain a label column. It can not be tested in current code" - "setting!" - ) - return self._create_examples(self._read_csv(os.path.join(data_dir, "test.csv")), "test") - - def get_labels(self): - """See base class.""" - return ["0", "1", "2", "3"] - - def _read_csv(self, input_file): - with open(input_file, "r", encoding="utf-8") as f: - return list(csv.reader(f)) - - def _create_examples(self, lines: List[List[str]], type: str): - """Creates examples for the training and dev sets.""" - if type == "train" and lines[0][-1] != "label": - raise ValueError("For training, the input file must contain a label column.") - - examples = [ - InputExample( - example_id=line[2], - question=line[5], # in the swag dataset, the - # common beginning of each - # choice is stored in "sent2". - contexts=[line[4], line[4], line[4], line[4]], - endings=[line[7], line[8], line[9], line[10]], - label=line[11], - ) - for line in lines[1:] # we skip the line with the column names - ] - - return examples - - -class ArcProcessor(DataProcessor): - """Processor for the ARC data set (request from allennlp).""" - - def get_train_examples(self, data_dir): - """See base class.""" - logger.info("LOOKING AT {} train".format(data_dir)) - return self._create_examples(self._read_json(os.path.join(data_dir, "train.jsonl")), "train") - - def get_dev_examples(self, data_dir): - """See base class.""" - logger.info("LOOKING AT {} dev".format(data_dir)) - return self._create_examples(self._read_json(os.path.join(data_dir, "dev.jsonl")), "dev") - - def get_test_examples(self, data_dir): - logger.info("LOOKING AT {} test".format(data_dir)) - return self._create_examples(self._read_json(os.path.join(data_dir, "test.jsonl")), "test") - - def get_labels(self): - """See base class.""" - return ["0", "1", "2", "3"] - - def _read_json(self, input_file): - with open(input_file, "r", encoding="utf-8") as fin: - lines = fin.readlines() - return lines - - def _create_examples(self, lines, type): - """Creates examples for the training and dev sets.""" - - # There are two types of labels. They should be normalized - def normalize(truth): - if truth in "ABCD": - return ord(truth) - ord("A") - elif truth in "1234": - return int(truth) - 1 - else: - logger.info("truth ERROR! %s", str(truth)) - return None - - examples = [] - three_choice = 0 - four_choice = 0 - five_choice = 0 - other_choices = 0 - # we deleted example which has more than or less than four choices - for line in tqdm.tqdm(lines, desc="read arc data"): - data_raw = json.loads(line.strip("\n")) - if len(data_raw["question"]["choices"]) == 3: - three_choice += 1 - continue - elif len(data_raw["question"]["choices"]) == 5: - five_choice += 1 - continue - elif len(data_raw["question"]["choices"]) != 4: - other_choices += 1 - continue - four_choice += 1 - truth = str(normalize(data_raw["answerKey"])) - assert truth != "None" - question_choices = data_raw["question"] - question = question_choices["stem"] - id = data_raw["id"] - options = question_choices["choices"] - if len(options) == 4: - examples.append( - InputExample( - example_id=id, - question=question, - contexts=[ - options[0]["para"].replace("_", ""), - options[1]["para"].replace("_", ""), - options[2]["para"].replace("_", ""), - options[3]["para"].replace("_", ""), - ], - endings=[options[0]["text"], options[1]["text"], options[2]["text"], options[3]["text"]], - label=truth, - ) - ) - - if type == "train": - assert len(examples) > 1 - assert examples[0].label is not None - logger.info("len examples: %s}", str(len(examples))) - logger.info("Three choices: %s", str(three_choice)) - logger.info("Five choices: %s", str(five_choice)) - logger.info("Other choices: %s", str(other_choices)) - logger.info("four choices: %s", str(four_choice)) - - return examples - - -def convert_examples_to_features( - examples: List[InputExample], - label_list: List[str], - max_length: int, - tokenizer: PreTrainedTokenizer, -) -> List[InputFeatures]: - """ - Loads a data file into a list of `InputFeatures` - """ - - label_map = {label: i for i, label in enumerate(label_list)} - - features = [] - for (ex_index, example) in tqdm.tqdm(enumerate(examples), desc="convert examples to features"): - if ex_index % 10000 == 0: - logger.info("Writing example %d of %d" % (ex_index, len(examples))) - choices_inputs = [] - for ending_idx, (context, ending) in enumerate(zip(example.contexts, example.endings)): - text_a = context - if example.question.find("_") != -1: - # this is for cloze question - text_b = example.question.replace("_", ending) - else: - text_b = example.question + " " + ending - - inputs = tokenizer( - text_a, - text_b, - add_special_tokens=True, - max_length=max_length, - padding="max_length", - truncation=True, - return_overflowing_tokens=True, - ) - if "num_truncated_tokens" in inputs and inputs["num_truncated_tokens"] > 0: - logger.info( - "Attention! you are cropping tokens (swag task is ok). " - "If you are training ARC and RACE and you are poping question + options," - "you need to try to use a bigger max seq length!" - ) - - choices_inputs.append(inputs) - - label = label_map[example.label] - - input_ids = [x["input_ids"] for x in choices_inputs] - attention_mask = ( - [x["attention_mask"] for x in choices_inputs] if "attention_mask" in choices_inputs[0] else None - ) - token_type_ids = ( - [x["token_type_ids"] for x in choices_inputs] if "token_type_ids" in choices_inputs[0] else None - ) - - features.append( - InputFeatures( - example_id=example.example_id, - input_ids=input_ids, - attention_mask=attention_mask, - token_type_ids=token_type_ids, - label=label, - ) - ) - - for f in features[:2]: - logger.info("*** Example ***") - logger.info("feature: %s" % f) - - return features - - -processors = {"race": RaceProcessor, "swag": SwagProcessor, "arc": ArcProcessor, "syn": SynonymProcessor} -MULTIPLE_CHOICE_TASKS_NUM_LABELS = {"race", 4, "swag", 4, "arc", 4, "syn", 5} diff --git a/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/lightning_base.py b/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/lightning_base.py deleted file mode 100644 index a9a05fbf960..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/lightning_base.py +++ /dev/null @@ -1,391 +0,0 @@ -import argparse -import logging -import os -from pathlib import Path -from typing import Any, Dict - -import pytorch_lightning as pl -from pytorch_lightning.utilities import rank_zero_info - -from transformers import ( - AdamW, - AutoConfig, - AutoModel, - AutoModelForPreTraining, - AutoModelForQuestionAnswering, - AutoModelForSeq2SeqLM, - AutoModelForSequenceClassification, - AutoModelForTokenClassification, - AutoModelWithLMHead, - AutoTokenizer, - PretrainedConfig, - PreTrainedTokenizer, -) -from transformers.optimization import ( - Adafactor, - get_cosine_schedule_with_warmup, - get_cosine_with_hard_restarts_schedule_with_warmup, - get_linear_schedule_with_warmup, - get_polynomial_decay_schedule_with_warmup, -) -from transformers.utils.versions import require_version_examples - - -logger = logging.getLogger(__name__) - -require_version_examples("pytorch_lightning>=1.0.4") - -MODEL_MODES = { - "base": AutoModel, - "sequence-classification": AutoModelForSequenceClassification, - "question-answering": AutoModelForQuestionAnswering, - "pretraining": AutoModelForPreTraining, - "token-classification": AutoModelForTokenClassification, - "language-modeling": AutoModelWithLMHead, - "summarization": AutoModelForSeq2SeqLM, - "translation": AutoModelForSeq2SeqLM, -} - - -# update this and the import above to support new schedulers from transformers.optimization -arg_to_scheduler = { - "linear": get_linear_schedule_with_warmup, - "cosine": get_cosine_schedule_with_warmup, - "cosine_w_restarts": get_cosine_with_hard_restarts_schedule_with_warmup, - "polynomial": get_polynomial_decay_schedule_with_warmup, - # '': get_constant_schedule, # not supported for now - # '': get_constant_schedule_with_warmup, # not supported for now -} -arg_to_scheduler_choices = sorted(arg_to_scheduler.keys()) -arg_to_scheduler_metavar = "{" + ", ".join(arg_to_scheduler_choices) + "}" - - -class BaseTransformer(pl.LightningModule): - def __init__( - self, - hparams: argparse.Namespace, - num_labels=None, - mode="base", - config=None, - tokenizer=None, - model=None, - **config_kwargs - ): - """Initialize a model, tokenizer and config.""" - super().__init__() - # TODO: move to self.save_hyperparameters() - # self.save_hyperparameters() - # can also expand arguments into trainer signature for easier reading - - self.save_hyperparameters(hparams) - self.step_count = 0 - self.output_dir = Path(self.hparams.output_dir) - cache_dir = self.hparams.cache_dir if self.hparams.cache_dir else None - if config is None: - self.config = AutoConfig.from_pretrained( - self.hparams.config_name if self.hparams.config_name else self.hparams.model_name_or_path, - **({"num_labels": num_labels} if num_labels is not None else {}), - cache_dir=cache_dir, - **config_kwargs, - ) - else: - self.config: PretrainedConfig = config - - extra_model_params = ("encoder_layerdrop", "decoder_layerdrop", "dropout", "attention_dropout") - for p in extra_model_params: - if getattr(self.hparams, p, None): - assert hasattr(self.config, p), f"model config doesn't have a `{p}` attribute" - setattr(self.config, p, getattr(self.hparams, p)) - - if tokenizer is None: - self.tokenizer = AutoTokenizer.from_pretrained( - self.hparams.tokenizer_name if self.hparams.tokenizer_name else self.hparams.model_name_or_path, - cache_dir=cache_dir, - ) - else: - self.tokenizer: PreTrainedTokenizer = tokenizer - self.model_type = MODEL_MODES[mode] - if model is None: - self.model = self.model_type.from_pretrained( - self.hparams.model_name_or_path, - from_tf=bool(".ckpt" in self.hparams.model_name_or_path), - config=self.config, - cache_dir=cache_dir, - ) - else: - self.model = model - - def load_hf_checkpoint(self, *args, **kwargs): - self.model = self.model_type.from_pretrained(*args, **kwargs) - - def get_lr_scheduler(self): - get_schedule_func = arg_to_scheduler[self.hparams.lr_scheduler] - scheduler = get_schedule_func( - self.opt, num_warmup_steps=self.hparams.warmup_steps, num_training_steps=self.total_steps() - ) - scheduler = {"scheduler": scheduler, "interval": "step", "frequency": 1} - return scheduler - - def configure_optimizers(self): - """Prepare optimizer and schedule (linear warmup and decay)""" - model = self.model - no_decay = ["bias", "LayerNorm.weight"] - optimizer_grouped_parameters = [ - { - "params": [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay)], - "weight_decay": self.hparams.weight_decay, - }, - { - "params": [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay)], - "weight_decay": 0.0, - }, - ] - if self.hparams.adafactor: - optimizer = Adafactor( - optimizer_grouped_parameters, lr=self.hparams.learning_rate, scale_parameter=False, relative_step=False - ) - - else: - optimizer = AdamW( - optimizer_grouped_parameters, lr=self.hparams.learning_rate, eps=self.hparams.adam_epsilon - ) - self.opt = optimizer - - scheduler = self.get_lr_scheduler() - - return [optimizer], [scheduler] - - def test_step(self, batch, batch_nb): - return self.validation_step(batch, batch_nb) - - def test_epoch_end(self, outputs): - return self.validation_end(outputs) - - def total_steps(self) -> int: - """The number of total training steps that will be run. Used for lr scheduler purposes.""" - num_devices = max(1, self.hparams.gpus) # TODO: consider num_tpu_cores - effective_batch_size = self.hparams.train_batch_size * self.hparams.accumulate_grad_batches * num_devices - return (self.dataset_size / effective_batch_size) * self.hparams.max_epochs - - def setup(self, mode): - if mode == "test": - self.dataset_size = len(self.test_dataloader().dataset) - else: - self.train_loader = self.get_dataloader("train", self.hparams.train_batch_size, shuffle=True) - self.dataset_size = len(self.train_dataloader().dataset) - - def get_dataloader(self, type_path: str, batch_size: int, shuffle: bool = False): - raise NotImplementedError("You must implement this for your task") - - def train_dataloader(self): - return self.train_loader - - def val_dataloader(self): - return self.get_dataloader("dev", self.hparams.eval_batch_size, shuffle=False) - - def test_dataloader(self): - return self.get_dataloader("test", self.hparams.eval_batch_size, shuffle=False) - - def _feature_file(self, mode): - return os.path.join( - self.hparams.data_dir, - "cached_{}_{}_{}".format( - mode, - list(filter(None, self.hparams.model_name_or_path.split("/"))).pop(), - str(self.hparams.max_seq_length), - ), - ) - - @pl.utilities.rank_zero_only - def on_save_checkpoint(self, checkpoint: Dict[str, Any]) -> None: - save_path = self.output_dir.joinpath("best_tfmr") - self.model.config.save_step = self.step_count - self.model.save_pretrained(save_path) - self.tokenizer.save_pretrained(save_path) - - @staticmethod - def add_model_specific_args(parser, root_dir): - parser.add_argument( - "--model_name_or_path", - default=None, - type=str, - required=True, - help="Path to pretrained model or model identifier from huggingface.co/models", - ) - parser.add_argument( - "--config_name", default="", type=str, help="Pretrained config name or path if not the same as model_name" - ) - parser.add_argument( - "--tokenizer_name", - default=None, - type=str, - help="Pretrained tokenizer name or path if not the same as model_name", - ) - parser.add_argument( - "--cache_dir", - default="", - type=str, - help="Where do you want to store the pre-trained models downloaded from huggingface.co", - ) - parser.add_argument( - "--encoder_layerdrop", - type=float, - help="Encoder layer dropout probability (Optional). Goes into model.config", - ) - parser.add_argument( - "--decoder_layerdrop", - type=float, - help="Decoder layer dropout probability (Optional). Goes into model.config", - ) - parser.add_argument( - "--dropout", - type=float, - help="Dropout probability (Optional). Goes into model.config", - ) - parser.add_argument( - "--attention_dropout", - type=float, - help="Attention dropout probability (Optional). Goes into model.config", - ) - parser.add_argument("--learning_rate", default=5e-5, type=float, help="The initial learning rate for Adam.") - parser.add_argument( - "--lr_scheduler", - default="linear", - choices=arg_to_scheduler_choices, - metavar=arg_to_scheduler_metavar, - type=str, - help="Learning rate scheduler", - ) - parser.add_argument("--weight_decay", default=0.0, type=float, help="Weight decay if we apply some.") - parser.add_argument("--adam_epsilon", default=1e-8, type=float, help="Epsilon for Adam optimizer.") - parser.add_argument("--warmup_steps", default=0, type=int, help="Linear warmup over warmup_steps.") - parser.add_argument("--num_workers", default=4, type=int, help="kwarg passed to DataLoader") - parser.add_argument("--num_train_epochs", dest="max_epochs", default=3, type=int) - parser.add_argument("--train_batch_size", default=32, type=int) - parser.add_argument("--eval_batch_size", default=32, type=int) - parser.add_argument("--adafactor", action="store_true") - - -class LoggingCallback(pl.Callback): - def on_batch_end(self, trainer, pl_module): - lr_scheduler = trainer.lr_schedulers[0]["scheduler"] - lrs = {f"lr_group_{i}": lr for i, lr in enumerate(lr_scheduler.get_lr())} - pl_module.logger.log_metrics(lrs) - - def on_validation_end(self, trainer: pl.Trainer, pl_module: pl.LightningModule): - rank_zero_info("***** Validation results *****") - metrics = trainer.callback_metrics - # Log results - for key in sorted(metrics): - if key not in ["log", "progress_bar"]: - rank_zero_info("{} = {}\n".format(key, str(metrics[key]))) - - def on_test_end(self, trainer: pl.Trainer, pl_module: pl.LightningModule): - rank_zero_info("***** Test results *****") - metrics = trainer.callback_metrics - # Log and save results to file - output_test_results_file = os.path.join(pl_module.hparams.output_dir, "test_results.txt") - with open(output_test_results_file, "w") as writer: - for key in sorted(metrics): - if key not in ["log", "progress_bar"]: - rank_zero_info("{} = {}\n".format(key, str(metrics[key]))) - writer.write("{} = {}\n".format(key, str(metrics[key]))) - - -def add_generic_args(parser, root_dir) -> None: - # To allow all pl args uncomment the following line - # parser = pl.Trainer.add_argparse_args(parser) - parser.add_argument( - "--output_dir", - default=None, - type=str, - required=True, - help="The output directory where the model predictions and checkpoints will be written.", - ) - parser.add_argument( - "--fp16", - action="store_true", - help="Whether to use 16-bit (mixed) precision (through NVIDIA apex) instead of 32-bit", - ) - - parser.add_argument( - "--fp16_opt_level", - type=str, - default="O2", - help="For fp16: Apex AMP optimization level selected in ['O0', 'O1', 'O2', and 'O3']." - "See details at https://nvidia.github.io/apex/amp.html", - ) - parser.add_argument("--n_tpu_cores", dest="tpu_cores", type=int) - parser.add_argument("--max_grad_norm", dest="gradient_clip_val", default=1.0, type=float, help="Max gradient norm") - parser.add_argument("--do_train", action="store_true", help="Whether to run training.") - parser.add_argument("--do_predict", action="store_true", help="Whether to run predictions on the test set.") - parser.add_argument( - "--gradient_accumulation_steps", - dest="accumulate_grad_batches", - type=int, - default=1, - help="Number of updates steps to accumulate before performing a backward/update pass.", - ) - parser.add_argument("--seed", type=int, default=42, help="random seed for initialization") - parser.add_argument( - "--data_dir", - default=None, - type=str, - required=True, - help="The input data dir. Should contain the training files for the CoNLL-2003 NER task.", - ) - - -def generic_train( - model: BaseTransformer, - args: argparse.Namespace, - early_stopping_callback=None, - logger=True, # can pass WandbLogger() here - extra_callbacks=[], - checkpoint_callback=None, - logging_callback=None, - **extra_train_kwargs -): - pl.seed_everything(args.seed) - - # init model - odir = Path(model.hparams.output_dir) - odir.mkdir(exist_ok=True) - - # add custom checkpoints - if checkpoint_callback is None: - checkpoint_callback = pl.callbacks.ModelCheckpoint( - filepath=args.output_dir, prefix="checkpoint", monitor="val_loss", mode="min", save_top_k=1 - ) - if early_stopping_callback: - extra_callbacks.append(early_stopping_callback) - if logging_callback is None: - logging_callback = LoggingCallback() - - train_params = {} - - # TODO: remove with PyTorch 1.6 since pl uses native amp - if args.fp16: - train_params["precision"] = 16 - train_params["amp_level"] = args.fp16_opt_level - - if args.gpus > 1: - train_params["distributed_backend"] = "ddp" - - train_params["accumulate_grad_batches"] = args.accumulate_grad_batches - train_params["accelerator"] = extra_train_kwargs.get("accelerator", None) - train_params["profiler"] = extra_train_kwargs.get("profiler", None) - - trainer = pl.Trainer.from_argparse_args( - args, - weights_summary=None, - callbacks=[logging_callback] + extra_callbacks, - logger=logger, - checkpoint_callback=checkpoint_callback, - **train_params, - ) - - if args.do_train: - trainer.fit(model) - - return trainer diff --git a/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/requirements.txt b/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/requirements.txt deleted file mode 100644 index 7a303019774..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/requirements.txt +++ /dev/null @@ -1,22 +0,0 @@ -tensorboard -scikit-learn -seqeval -psutil -sacrebleu -rouge-score -tensorflow_datasets -pytorch-lightning==1.0.4 -matplotlib -git-python==1.0.3 -faiss-cpu -streamlit -elasticsearch -nltk -pandas -datasets >= 1.1.3 -fire -pytest -conllu -sentencepiece != 0.1.92 -protobuf -ray diff --git a/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_glue.py b/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_glue.py deleted file mode 100644 index abb06bf526b..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_glue.py +++ /dev/null @@ -1,201 +0,0 @@ -import argparse -import glob -import logging -import os -import time -from argparse import Namespace - -import numpy as np -import torch -from torch.utils.data import DataLoader, TensorDataset - -from lightning_base import BaseTransformer, add_generic_args, generic_train -from transformers import glue_compute_metrics as compute_metrics -from transformers import glue_convert_examples_to_features as convert_examples_to_features -from transformers import glue_output_modes -from transformers import glue_processors as processors -from transformers import glue_tasks_num_labels - - -logger = logging.getLogger(__name__) - - -class GLUETransformer(BaseTransformer): - - mode = "sequence-classification" - - def __init__(self, hparams): - if type(hparams) == dict: - hparams = Namespace(**hparams) - hparams.glue_output_mode = glue_output_modes[hparams.task] - num_labels = glue_tasks_num_labels[hparams.task] - - super().__init__(hparams, num_labels, self.mode) - - def forward(self, **inputs): - return self.model(**inputs) - - def training_step(self, batch, batch_idx): - inputs = {"input_ids": batch[0], "attention_mask": batch[1], "labels": batch[3]} - - if self.config.model_type not in ["distilbert", "bart"]: - inputs["token_type_ids"] = batch[2] if self.config.model_type in ["bert", "xlnet", "albert"] else None - - outputs = self(**inputs) - loss = outputs[0] - - lr_scheduler = self.trainer.lr_schedulers[0]["scheduler"] - tensorboard_logs = {"loss": loss, "rate": lr_scheduler.get_last_lr()[-1]} - return {"loss": loss, "log": tensorboard_logs} - - def prepare_data(self): - "Called to initialize data. Use the call to construct features" - args = self.hparams - processor = processors[args.task]() - self.labels = processor.get_labels() - - for mode in ["train", "dev"]: - cached_features_file = self._feature_file(mode) - if os.path.exists(cached_features_file) and not args.overwrite_cache: - logger.info("Loading features from cached file %s", cached_features_file) - else: - logger.info("Creating features from dataset file at %s", args.data_dir) - examples = ( - processor.get_dev_examples(args.data_dir) - if mode == "dev" - else processor.get_train_examples(args.data_dir) - ) - features = convert_examples_to_features( - examples, - self.tokenizer, - max_length=args.max_seq_length, - label_list=self.labels, - output_mode=args.glue_output_mode, - ) - logger.info("Saving features into cached file %s", cached_features_file) - torch.save(features, cached_features_file) - - def get_dataloader(self, mode: str, batch_size: int, shuffle: bool = False) -> DataLoader: - "Load datasets. Called after prepare data." - - # We test on dev set to compare to benchmarks without having to submit to GLUE server - mode = "dev" if mode == "test" else mode - - cached_features_file = self._feature_file(mode) - logger.info("Loading features from cached file %s", cached_features_file) - features = torch.load(cached_features_file) - all_input_ids = torch.tensor([f.input_ids for f in features], dtype=torch.long) - all_attention_mask = torch.tensor([f.attention_mask for f in features], dtype=torch.long) - all_token_type_ids = torch.tensor([f.token_type_ids for f in features], dtype=torch.long) - if self.hparams.glue_output_mode == "classification": - all_labels = torch.tensor([f.label for f in features], dtype=torch.long) - elif self.hparams.glue_output_mode == "regression": - all_labels = torch.tensor([f.label for f in features], dtype=torch.float) - - return DataLoader( - TensorDataset(all_input_ids, all_attention_mask, all_token_type_ids, all_labels), - batch_size=batch_size, - shuffle=shuffle, - ) - - def validation_step(self, batch, batch_idx): - inputs = {"input_ids": batch[0], "attention_mask": batch[1], "labels": batch[3]} - - if self.config.model_type not in ["distilbert", "bart"]: - inputs["token_type_ids"] = batch[2] if self.config.model_type in ["bert", "xlnet", "albert"] else None - - outputs = self(**inputs) - tmp_eval_loss, logits = outputs[:2] - preds = logits.detach().cpu().numpy() - out_label_ids = inputs["labels"].detach().cpu().numpy() - - return {"val_loss": tmp_eval_loss.detach().cpu(), "pred": preds, "target": out_label_ids} - - def _eval_end(self, outputs) -> tuple: - val_loss_mean = torch.stack([x["val_loss"] for x in outputs]).mean().detach().cpu().item() - preds = np.concatenate([x["pred"] for x in outputs], axis=0) - - if self.hparams.glue_output_mode == "classification": - preds = np.argmax(preds, axis=1) - elif self.hparams.glue_output_mode == "regression": - preds = np.squeeze(preds) - - out_label_ids = np.concatenate([x["target"] for x in outputs], axis=0) - out_label_list = [[] for _ in range(out_label_ids.shape[0])] - preds_list = [[] for _ in range(out_label_ids.shape[0])] - - results = {**{"val_loss": val_loss_mean}, **compute_metrics(self.hparams.task, preds, out_label_ids)} - - ret = {k: v for k, v in results.items()} - ret["log"] = results - return ret, preds_list, out_label_list - - def validation_epoch_end(self, outputs: list) -> dict: - ret, preds, targets = self._eval_end(outputs) - logs = ret["log"] - return {"val_loss": logs["val_loss"], "log": logs, "progress_bar": logs} - - def test_epoch_end(self, outputs) -> dict: - ret, predictions, targets = self._eval_end(outputs) - logs = ret["log"] - # `val_loss` is the key returned by `self._eval_end()` but actually refers to `test_loss` - return {"avg_test_loss": logs["val_loss"], "log": logs, "progress_bar": logs} - - @staticmethod - def add_model_specific_args(parser, root_dir): - BaseTransformer.add_model_specific_args(parser, root_dir) - parser.add_argument( - "--max_seq_length", - default=128, - type=int, - help="The maximum total input sequence length after tokenization. Sequences longer " - "than this will be truncated, sequences shorter will be padded.", - ) - - parser.add_argument( - "--task", - default="", - type=str, - required=True, - help="The GLUE task to run", - ) - parser.add_argument( - "--gpus", - default=0, - type=int, - help="The number of GPUs allocated for this, it is by default 0 meaning none", - ) - - parser.add_argument( - "--overwrite_cache", action="store_true", help="Overwrite the cached training and evaluation sets" - ) - - return parser - - -def main(): - parser = argparse.ArgumentParser() - add_generic_args(parser, os.getcwd()) - parser = GLUETransformer.add_model_specific_args(parser, os.getcwd()) - args = parser.parse_args() - - # If output_dir not provided, a folder will be generated in pwd - if args.output_dir is None: - args.output_dir = os.path.join( - "./results", - f"{args.task}_{time.strftime('%Y%m%d_%H%M%S')}", - ) - os.makedirs(args.output_dir) - - model = GLUETransformer(args) - trainer = generic_train(model, args) - - # Optionally, predict on dev set and write to output_dir - if args.do_predict: - checkpoints = list(sorted(glob.glob(os.path.join(args.output_dir, "checkpoint-epoch=*.ckpt"), recursive=True))) - model = model.load_from_checkpoint(checkpoints[-1]) - return trainer.test(model) - - -if __name__ == "__main__": - main() diff --git a/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_glue.sh b/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_glue.sh deleted file mode 100755 index 7cd57306d4e..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_glue.sh +++ /dev/null @@ -1,34 +0,0 @@ -# Install example requirements -pip install -r ../requirements.txt - -# Download glue data -python3 ../../utils/download_glue_data.py - -export TASK=mrpc -export DATA_DIR=./glue_data/MRPC/ -export MAX_LENGTH=128 -export LEARNING_RATE=2e-5 -export BERT_MODEL=bert-base-cased -export BATCH_SIZE=32 -export NUM_EPOCHS=3 -export SEED=2 -export OUTPUT_DIR_NAME=mrpc-pl-bert -export CURRENT_DIR=${PWD} -export OUTPUT_DIR=${CURRENT_DIR}/${OUTPUT_DIR_NAME} - -# Make output directory if it doesn't exist -mkdir -p $OUTPUT_DIR -# Add parent directory to python path to access lightning_base.py -export PYTHONPATH="../":"${PYTHONPATH}" - -python3 run_glue.py --gpus 1 --data_dir $DATA_DIR \ ---task $TASK \ ---model_name_or_path $BERT_MODEL \ ---output_dir $OUTPUT_DIR \ ---max_seq_length $MAX_LENGTH \ ---learning_rate $LEARNING_RATE \ ---num_train_epochs $NUM_EPOCHS \ ---train_batch_size $BATCH_SIZE \ ---seed $SEED \ ---do_train \ ---do_predict diff --git a/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_ner.py b/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_ner.py deleted file mode 100644 index 1066c6fed48..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_ner.py +++ /dev/null @@ -1,215 +0,0 @@ -import argparse -import glob -import logging -import os -from argparse import Namespace -from importlib import import_module - -import numpy as np -import torch -from seqeval.metrics import accuracy_score, f1_score, precision_score, recall_score -from torch.nn import CrossEntropyLoss -from torch.utils.data import DataLoader, TensorDataset - -from lightning_base import BaseTransformer, add_generic_args, generic_train -from utils_ner import TokenClassificationTask - - -logger = logging.getLogger(__name__) - - -class NERTransformer(BaseTransformer): - """ - A training module for NER. See BaseTransformer for the core options. - """ - - mode = "token-classification" - - def __init__(self, hparams): - if type(hparams) == dict: - hparams = Namespace(**hparams) - module = import_module("tasks") - try: - token_classification_task_clazz = getattr(module, hparams.task_type) - self.token_classification_task: TokenClassificationTask = token_classification_task_clazz() - except AttributeError: - raise ValueError( - f"Task {hparams.task_type} needs to be defined as a TokenClassificationTask subclass in {module}. " - f"Available tasks classes are: {TokenClassificationTask.__subclasses__()}" - ) - self.labels = self.token_classification_task.get_labels(hparams.labels) - self.pad_token_label_id = CrossEntropyLoss().ignore_index - super().__init__(hparams, len(self.labels), self.mode) - - def forward(self, **inputs): - return self.model(**inputs) - - def training_step(self, batch, batch_num): - "Compute loss and log." - inputs = {"input_ids": batch[0], "attention_mask": batch[1], "labels": batch[3]} - if self.config.model_type != "distilbert": - inputs["token_type_ids"] = ( - batch[2] if self.config.model_type in ["bert", "xlnet"] else None - ) # XLM and RoBERTa don"t use token_type_ids - - outputs = self(**inputs) - loss = outputs[0] - # tensorboard_logs = {"loss": loss, "rate": self.lr_scheduler.get_last_lr()[-1]} - return {"loss": loss} - - def prepare_data(self): - "Called to initialize data. Use the call to construct features" - args = self.hparams - for mode in ["train", "dev", "test"]: - cached_features_file = self._feature_file(mode) - if os.path.exists(cached_features_file) and not args.overwrite_cache: - logger.info("Loading features from cached file %s", cached_features_file) - features = torch.load(cached_features_file) - else: - logger.info("Creating features from dataset file at %s", args.data_dir) - examples = self.token_classification_task.read_examples_from_file(args.data_dir, mode) - features = self.token_classification_task.convert_examples_to_features( - examples, - self.labels, - args.max_seq_length, - self.tokenizer, - cls_token_at_end=bool(self.config.model_type in ["xlnet"]), - cls_token=self.tokenizer.cls_token, - cls_token_segment_id=2 if self.config.model_type in ["xlnet"] else 0, - sep_token=self.tokenizer.sep_token, - sep_token_extra=False, - pad_on_left=bool(self.config.model_type in ["xlnet"]), - pad_token=self.tokenizer.pad_token_id, - pad_token_segment_id=self.tokenizer.pad_token_type_id, - pad_token_label_id=self.pad_token_label_id, - ) - logger.info("Saving features into cached file %s", cached_features_file) - torch.save(features, cached_features_file) - - def get_dataloader(self, mode: int, batch_size: int, shuffle: bool = False) -> DataLoader: - "Load datasets. Called after prepare data." - cached_features_file = self._feature_file(mode) - logger.info("Loading features from cached file %s", cached_features_file) - features = torch.load(cached_features_file) - all_input_ids = torch.tensor([f.input_ids for f in features], dtype=torch.long) - all_attention_mask = torch.tensor([f.attention_mask for f in features], dtype=torch.long) - if features[0].token_type_ids is not None: - all_token_type_ids = torch.tensor([f.token_type_ids for f in features], dtype=torch.long) - else: - all_token_type_ids = torch.tensor([0 for f in features], dtype=torch.long) - # HACK(we will not use this anymore soon) - all_label_ids = torch.tensor([f.label_ids for f in features], dtype=torch.long) - return DataLoader( - TensorDataset(all_input_ids, all_attention_mask, all_token_type_ids, all_label_ids), batch_size=batch_size - ) - - def validation_step(self, batch, batch_nb): - """Compute validation""" "" - inputs = {"input_ids": batch[0], "attention_mask": batch[1], "labels": batch[3]} - if self.config.model_type != "distilbert": - inputs["token_type_ids"] = ( - batch[2] if self.config.model_type in ["bert", "xlnet"] else None - ) # XLM and RoBERTa don"t use token_type_ids - outputs = self(**inputs) - tmp_eval_loss, logits = outputs[:2] - preds = logits.detach().cpu().numpy() - out_label_ids = inputs["labels"].detach().cpu().numpy() - return {"val_loss": tmp_eval_loss.detach().cpu(), "pred": preds, "target": out_label_ids} - - def _eval_end(self, outputs): - "Evaluation called for both Val and Test" - val_loss_mean = torch.stack([x["val_loss"] for x in outputs]).mean() - preds = np.concatenate([x["pred"] for x in outputs], axis=0) - preds = np.argmax(preds, axis=2) - out_label_ids = np.concatenate([x["target"] for x in outputs], axis=0) - - label_map = {i: label for i, label in enumerate(self.labels)} - out_label_list = [[] for _ in range(out_label_ids.shape[0])] - preds_list = [[] for _ in range(out_label_ids.shape[0])] - - for i in range(out_label_ids.shape[0]): - for j in range(out_label_ids.shape[1]): - if out_label_ids[i, j] != self.pad_token_label_id: - out_label_list[i].append(label_map[out_label_ids[i][j]]) - preds_list[i].append(label_map[preds[i][j]]) - - results = { - "val_loss": val_loss_mean, - "accuracy_score": accuracy_score(out_label_list, preds_list), - "precision": precision_score(out_label_list, preds_list), - "recall": recall_score(out_label_list, preds_list), - "f1": f1_score(out_label_list, preds_list), - } - - ret = {k: v for k, v in results.items()} - ret["log"] = results - return ret, preds_list, out_label_list - - def validation_epoch_end(self, outputs): - # when stable - ret, preds, targets = self._eval_end(outputs) - logs = ret["log"] - return {"val_loss": logs["val_loss"], "log": logs, "progress_bar": logs} - - def test_epoch_end(self, outputs): - # updating to test_epoch_end instead of deprecated test_end - ret, predictions, targets = self._eval_end(outputs) - - # Converting to the dict required by pl - # https://github.com/PyTorchLightning/pytorch-lightning/blob/master/\ - # pytorch_lightning/trainer/logging.py#L139 - logs = ret["log"] - # `val_loss` is the key returned by `self._eval_end()` but actually refers to `test_loss` - return {"avg_test_loss": logs["val_loss"], "log": logs, "progress_bar": logs} - - @staticmethod - def add_model_specific_args(parser, root_dir): - # Add NER specific options - BaseTransformer.add_model_specific_args(parser, root_dir) - parser.add_argument( - "--task_type", default="NER", type=str, help="Task type to fine tune in training (e.g. NER, POS, etc)" - ) - parser.add_argument( - "--max_seq_length", - default=128, - type=int, - help="The maximum total input sequence length after tokenization. Sequences longer " - "than this will be truncated, sequences shorter will be padded.", - ) - - parser.add_argument( - "--labels", - default="", - type=str, - help="Path to a file containing all labels. If not specified, CoNLL-2003 labels are used.", - ) - parser.add_argument( - "--gpus", - default=0, - type=int, - help="The number of GPUs allocated for this, it is by default 0 meaning none", - ) - - parser.add_argument( - "--overwrite_cache", action="store_true", help="Overwrite the cached training and evaluation sets" - ) - - return parser - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - add_generic_args(parser, os.getcwd()) - parser = NERTransformer.add_model_specific_args(parser, os.getcwd()) - args = parser.parse_args() - model = NERTransformer(args) - trainer = generic_train(model, args) - - if args.do_predict: - # See https://github.com/huggingface/transformers/issues/3159 - # pl use this default format to create a checkpoint: - # https://github.com/PyTorchLightning/pytorch-lightning/blob/master\ - # /pytorch_lightning/callbacks/model_checkpoint.py#L322 - checkpoints = list(sorted(glob.glob(os.path.join(args.output_dir, "checkpoint-epoch=*.ckpt"), recursive=True))) - model = model.load_from_checkpoint(checkpoints[-1]) - trainer.test(model) diff --git a/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_ner.sh b/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_ner.sh deleted file mode 100755 index 2913473eb8c..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_ner.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash - -# for seqeval metrics import -pip install -r ../requirements.txt - -## The relevant files are currently on a shared Google -## drive at https://drive.google.com/drive/folders/1kC0I2UGl2ltrluI9NqDjaQJGw5iliw_J -## Monitor for changes and eventually migrate to nlp dataset -curl -L 'https://drive.google.com/uc?export=download&id=1Jjhbal535VVz2ap4v4r_rN1UEHTdLK5P' \ -| grep -v "^#" | cut -f 2,3 | tr '\t' ' ' > train.txt.tmp -curl -L 'https://drive.google.com/uc?export=download&id=1ZfRcQThdtAR5PPRjIDtrVP7BtXSCUBbm' \ -| grep -v "^#" | cut -f 2,3 | tr '\t' ' ' > dev.txt.tmp -curl -L 'https://drive.google.com/uc?export=download&id=1u9mb7kNJHWQCWyweMDRMuTFoOHOfeBTH' \ -| grep -v "^#" | cut -f 2,3 | tr '\t' ' ' > test.txt.tmp - -export MAX_LENGTH=128 -export BERT_MODEL=bert-base-multilingual-cased -python3 scripts/preprocess.py train.txt.tmp $BERT_MODEL $MAX_LENGTH > train.txt -python3 scripts/preprocess.py dev.txt.tmp $BERT_MODEL $MAX_LENGTH > dev.txt -python3 scripts/preprocess.py test.txt.tmp $BERT_MODEL $MAX_LENGTH > test.txt -cat train.txt dev.txt test.txt | cut -d " " -f 2 | grep -v "^$"| sort | uniq > labels.txt -export BATCH_SIZE=32 -export NUM_EPOCHS=3 -export SEED=1 - -export OUTPUT_DIR_NAME=germeval-model -export CURRENT_DIR=${PWD} -export OUTPUT_DIR=${CURRENT_DIR}/${OUTPUT_DIR_NAME} -mkdir -p $OUTPUT_DIR - -# Add parent directory to python path to access lightning_base.py -export PYTHONPATH="../":"${PYTHONPATH}" - -python3 run_ner.py --data_dir ./ \ ---labels ./labels.txt \ ---model_name_or_path $BERT_MODEL \ ---output_dir $OUTPUT_DIR \ ---max_seq_length $MAX_LENGTH \ ---num_train_epochs $NUM_EPOCHS \ ---train_batch_size $BATCH_SIZE \ ---seed $SEED \ ---gpus 1 \ ---do_train \ ---do_predict diff --git a/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_pos.sh b/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_pos.sh deleted file mode 100755 index 93765366cf3..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/pytorch-lightning/run_pos.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash -if ! [ -f ./dev.txt ]; then - echo "Download dev dataset...." - curl -L -o ./dev.txt 'https://github.com/UniversalDependencies/UD_English-EWT/raw/master/en_ewt-ud-dev.conllu' -fi - -if ! [ -f ./test.txt ]; then - echo "Download test dataset...." - curl -L -o ./test.txt 'https://github.com/UniversalDependencies/UD_English-EWT/raw/master/en_ewt-ud-test.conllu' -fi - -if ! [ -f ./train.txt ]; then - echo "Download train dataset...." - curl -L -o ./train.txt 'https://github.com/UniversalDependencies/UD_English-EWT/raw/master/en_ewt-ud-train.conllu' -fi - -export MAX_LENGTH=200 -export BERT_MODEL=bert-base-uncased -export OUTPUT_DIR=postagger-model -export BATCH_SIZE=32 -export NUM_EPOCHS=3 -export SAVE_STEPS=750 -export SEED=1 - - -# Add parent directory to python path to access lightning_base.py -export PYTHONPATH="../":"${PYTHONPATH}" - -python3 run_ner.py --data_dir ./ \ ---task_type POS \ ---model_name_or_path $BERT_MODEL \ ---output_dir $OUTPUT_DIR \ ---max_seq_length $MAX_LENGTH \ ---num_train_epochs $NUM_EPOCHS \ ---train_batch_size $BATCH_SIZE \ ---seed $SEED \ ---gpus 1 \ ---do_train \ ---do_predict diff --git a/examples/pytorch/huggingface_models/examples/legacy/question-answering/run_squad.py b/examples/pytorch/huggingface_models/examples/legacy/question-answering/run_squad.py deleted file mode 100644 index ff693ad24dd..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/question-answering/run_squad.py +++ /dev/null @@ -1,830 +0,0 @@ -# coding=utf-8 -# Copyright 2018 The Google AI Language Team Authors and The HuggingFace Inc. team. -# Copyright (c) 2018, NVIDIA CORPORATION. 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. -""" Finetuning the library models for question-answering on SQuAD (DistilBERT, Bert, XLM, XLNet).""" - - -import argparse -import glob -import logging -import os -import random -import timeit - -import numpy as np -import torch -from torch.utils.data import DataLoader, RandomSampler, SequentialSampler -from torch.utils.data.distributed import DistributedSampler -from tqdm import tqdm, trange - -import transformers -from transformers import ( - MODEL_FOR_QUESTION_ANSWERING_MAPPING, - WEIGHTS_NAME, - AdamW, - AutoConfig, - AutoModelForQuestionAnswering, - AutoTokenizer, - get_linear_schedule_with_warmup, - squad_convert_examples_to_features, -) -from transformers.data.metrics.squad_metrics import ( - compute_predictions_log_probs, - compute_predictions_logits, - squad_evaluate, -) -from transformers.data.processors.squad import SquadResult, SquadV1Processor, SquadV2Processor -from transformers.trainer_utils import is_main_process - - -try: - from torch.utils.tensorboard import SummaryWriter -except ImportError: - from tensorboardX import SummaryWriter - - -logger = logging.getLogger(__name__) - -MODEL_CONFIG_CLASSES = list(MODEL_FOR_QUESTION_ANSWERING_MAPPING.keys()) -MODEL_TYPES = tuple(conf.model_type for conf in MODEL_CONFIG_CLASSES) - - -def set_seed(args): - random.seed(args.seed) - np.random.seed(args.seed) - torch.manual_seed(args.seed) - if args.n_gpu > 0: - torch.cuda.manual_seed_all(args.seed) - - -def to_list(tensor): - return tensor.detach().cpu().tolist() - - -def train(args, train_dataset, model, tokenizer): - """ Train the model """ - if args.local_rank in [-1, 0]: - tb_writer = SummaryWriter() - - args.train_batch_size = args.per_gpu_train_batch_size * max(1, args.n_gpu) - train_sampler = RandomSampler(train_dataset) if args.local_rank == -1 else DistributedSampler(train_dataset) - train_dataloader = DataLoader(train_dataset, sampler=train_sampler, batch_size=args.train_batch_size) - - if args.max_steps > 0: - t_total = args.max_steps - args.num_train_epochs = args.max_steps // (len(train_dataloader) // args.gradient_accumulation_steps) + 1 - else: - t_total = len(train_dataloader) // args.gradient_accumulation_steps * args.num_train_epochs - - # Prepare optimizer and schedule (linear warmup and decay) - no_decay = ["bias", "LayerNorm.weight"] - optimizer_grouped_parameters = [ - { - "params": [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay)], - "weight_decay": args.weight_decay, - }, - {"params": [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay)], "weight_decay": 0.0}, - ] - optimizer = AdamW(optimizer_grouped_parameters, lr=args.learning_rate, eps=args.adam_epsilon) - scheduler = get_linear_schedule_with_warmup( - optimizer, num_warmup_steps=args.warmup_steps, num_training_steps=t_total - ) - - # Check if saved optimizer or scheduler states exist - if os.path.isfile(os.path.join(args.model_name_or_path, "optimizer.pt")) and os.path.isfile( - os.path.join(args.model_name_or_path, "scheduler.pt") - ): - # Load in optimizer and scheduler states - optimizer.load_state_dict(torch.load(os.path.join(args.model_name_or_path, "optimizer.pt"))) - scheduler.load_state_dict(torch.load(os.path.join(args.model_name_or_path, "scheduler.pt"))) - - if args.fp16: - try: - from apex import amp - except ImportError: - raise ImportError("Please install apex from https://www.github.com/nvidia/apex to use fp16 training.") - - model, optimizer = amp.initialize(model, optimizer, opt_level=args.fp16_opt_level) - - # multi-gpu training (should be after apex fp16 initialization) - if args.n_gpu > 1: - model = torch.nn.DataParallel(model) - - # Distributed training (should be after apex fp16 initialization) - if args.local_rank != -1: - model = torch.nn.parallel.DistributedDataParallel( - model, device_ids=[args.local_rank], output_device=args.local_rank, find_unused_parameters=True - ) - - # Train! - logger.info("***** Running training *****") - logger.info(" Num examples = %d", len(train_dataset)) - logger.info(" Num Epochs = %d", args.num_train_epochs) - logger.info(" Instantaneous batch size per GPU = %d", args.per_gpu_train_batch_size) - logger.info( - " Total train batch size (w. parallel, distributed & accumulation) = %d", - args.train_batch_size - * args.gradient_accumulation_steps - * (torch.distributed.get_world_size() if args.local_rank != -1 else 1), - ) - logger.info(" Gradient Accumulation steps = %d", args.gradient_accumulation_steps) - logger.info(" Total optimization steps = %d", t_total) - - global_step = 1 - epochs_trained = 0 - steps_trained_in_current_epoch = 0 - # Check if continuing training from a checkpoint - if os.path.exists(args.model_name_or_path): - try: - # set global_step to gobal_step of last saved checkpoint from model path - checkpoint_suffix = args.model_name_or_path.split("-")[-1].split("/")[0] - global_step = int(checkpoint_suffix) - epochs_trained = global_step // (len(train_dataloader) // args.gradient_accumulation_steps) - steps_trained_in_current_epoch = global_step % (len(train_dataloader) // args.gradient_accumulation_steps) - - logger.info(" Continuing training from checkpoint, will skip to saved global_step") - logger.info(" Continuing training from epoch %d", epochs_trained) - logger.info(" Continuing training from global step %d", global_step) - logger.info(" Will skip the first %d steps in the first epoch", steps_trained_in_current_epoch) - except ValueError: - logger.info(" Starting fine-tuning.") - - tr_loss, logging_loss = 0.0, 0.0 - model.zero_grad() - train_iterator = trange( - epochs_trained, int(args.num_train_epochs), desc="Epoch", disable=args.local_rank not in [-1, 0] - ) - # Added here for reproductibility - set_seed(args) - - for _ in train_iterator: - epoch_iterator = tqdm(train_dataloader, desc="Iteration", disable=args.local_rank not in [-1, 0]) - for step, batch in enumerate(epoch_iterator): - - # Skip past any already trained steps if resuming training - if steps_trained_in_current_epoch > 0: - steps_trained_in_current_epoch -= 1 - continue - - model.train() - batch = tuple(t.to(args.device) for t in batch) - - inputs = { - "input_ids": batch[0], - "attention_mask": batch[1], - "token_type_ids": batch[2], - "start_positions": batch[3], - "end_positions": batch[4], - } - - if args.model_type in ["xlm", "roberta", "distilbert", "camembert", "bart", "longformer"]: - del inputs["token_type_ids"] - - if args.model_type in ["xlnet", "xlm"]: - inputs.update({"cls_index": batch[5], "p_mask": batch[6]}) - if args.version_2_with_negative: - inputs.update({"is_impossible": batch[7]}) - if hasattr(model, "config") and hasattr(model.config, "lang2id"): - inputs.update( - {"langs": (torch.ones(batch[0].shape, dtype=torch.int64) * args.lang_id).to(args.device)} - ) - - outputs = model(**inputs) - # model outputs are always tuple in transformers (see doc) - loss = outputs[0] - - if args.n_gpu > 1: - loss = loss.mean() # mean() to average on multi-gpu parallel (not distributed) training - if args.gradient_accumulation_steps > 1: - loss = loss / args.gradient_accumulation_steps - - if args.fp16: - with amp.scale_loss(loss, optimizer) as scaled_loss: - scaled_loss.backward() - else: - loss.backward() - - tr_loss += loss.item() - if (step + 1) % args.gradient_accumulation_steps == 0: - if args.fp16: - torch.nn.utils.clip_grad_norm_(amp.master_params(optimizer), args.max_grad_norm) - else: - torch.nn.utils.clip_grad_norm_(model.parameters(), args.max_grad_norm) - - optimizer.step() - scheduler.step() # Update learning rate schedule - model.zero_grad() - global_step += 1 - - # Log metrics - if args.local_rank in [-1, 0] and args.logging_steps > 0 and global_step % args.logging_steps == 0: - # Only evaluate when single GPU otherwise metrics may not average well - if args.local_rank == -1 and args.evaluate_during_training: - results = evaluate(args, model, tokenizer) - for key, value in results.items(): - tb_writer.add_scalar("eval_{}".format(key), value, global_step) - tb_writer.add_scalar("lr", scheduler.get_lr()[0], global_step) - tb_writer.add_scalar("loss", (tr_loss - logging_loss) / args.logging_steps, global_step) - logging_loss = tr_loss - - # Save model checkpoint - if args.local_rank in [-1, 0] and args.save_steps > 0 and global_step % args.save_steps == 0: - output_dir = os.path.join(args.output_dir, "checkpoint-{}".format(global_step)) - # Take care of distributed/parallel training - model_to_save = model.module if hasattr(model, "module") else model - model_to_save.save_pretrained(output_dir) - tokenizer.save_pretrained(output_dir) - - torch.save(args, os.path.join(output_dir, "training_args.bin")) - logger.info("Saving model checkpoint to %s", output_dir) - - torch.save(optimizer.state_dict(), os.path.join(output_dir, "optimizer.pt")) - torch.save(scheduler.state_dict(), os.path.join(output_dir, "scheduler.pt")) - logger.info("Saving optimizer and scheduler states to %s", output_dir) - - if args.max_steps > 0 and global_step > args.max_steps: - epoch_iterator.close() - break - if args.max_steps > 0 and global_step > args.max_steps: - train_iterator.close() - break - - if args.local_rank in [-1, 0]: - tb_writer.close() - - return global_step, tr_loss / global_step - - -def evaluate(args, model, tokenizer, prefix=""): - dataset, examples, features = load_and_cache_examples(args, tokenizer, evaluate=True, output_examples=True) - - if not os.path.exists(args.output_dir) and args.local_rank in [-1, 0]: - os.makedirs(args.output_dir) - - args.eval_batch_size = args.per_gpu_eval_batch_size * max(1, args.n_gpu) - - # Note that DistributedSampler samples randomly - eval_sampler = SequentialSampler(dataset) - eval_dataloader = DataLoader(dataset, sampler=eval_sampler, batch_size=args.eval_batch_size) - - # multi-gpu evaluate - if args.n_gpu > 1 and not isinstance(model, torch.nn.DataParallel): - model = torch.nn.DataParallel(model) - - # Eval! - logger.info("***** Running evaluation {} *****".format(prefix)) - logger.info(" Num examples = %d", len(dataset)) - logger.info(" Batch size = %d", args.eval_batch_size) - - all_results = [] - start_time = timeit.default_timer() - - for batch in tqdm(eval_dataloader, desc="Evaluating"): - model.eval() - batch = tuple(t.to(args.device) for t in batch) - - with torch.no_grad(): - inputs = { - "input_ids": batch[0], - "attention_mask": batch[1], - "token_type_ids": batch[2], - } - - if args.model_type in ["xlm", "roberta", "distilbert", "camembert", "bart", "longformer"]: - del inputs["token_type_ids"] - - feature_indices = batch[3] - - # XLNet and XLM use more arguments for their predictions - if args.model_type in ["xlnet", "xlm"]: - inputs.update({"cls_index": batch[4], "p_mask": batch[5]}) - # for lang_id-sensitive xlm models - if hasattr(model, "config") and hasattr(model.config, "lang2id"): - inputs.update( - {"langs": (torch.ones(batch[0].shape, dtype=torch.int64) * args.lang_id).to(args.device)} - ) - outputs = model(**inputs) - - for i, feature_index in enumerate(feature_indices): - eval_feature = features[feature_index.item()] - unique_id = int(eval_feature.unique_id) - - output = [to_list(output[i]) for output in outputs.to_tuple()] - - # Some models (XLNet, XLM) use 5 arguments for their predictions, while the other "simpler" - # models only use two. - if len(output) >= 5: - start_logits = output[0] - start_top_index = output[1] - end_logits = output[2] - end_top_index = output[3] - cls_logits = output[4] - - result = SquadResult( - unique_id, - start_logits, - end_logits, - start_top_index=start_top_index, - end_top_index=end_top_index, - cls_logits=cls_logits, - ) - - else: - start_logits, end_logits = output - result = SquadResult(unique_id, start_logits, end_logits) - - all_results.append(result) - - evalTime = timeit.default_timer() - start_time - logger.info(" Evaluation done in total %f secs (%f sec per example)", evalTime, evalTime / len(dataset)) - - # Compute predictions - output_prediction_file = os.path.join(args.output_dir, "predictions_{}.json".format(prefix)) - output_nbest_file = os.path.join(args.output_dir, "nbest_predictions_{}.json".format(prefix)) - - if args.version_2_with_negative: - output_null_log_odds_file = os.path.join(args.output_dir, "null_odds_{}.json".format(prefix)) - else: - output_null_log_odds_file = None - - # XLNet and XLM use a more complex post-processing procedure - if args.model_type in ["xlnet", "xlm"]: - start_n_top = model.config.start_n_top if hasattr(model, "config") else model.module.config.start_n_top - end_n_top = model.config.end_n_top if hasattr(model, "config") else model.module.config.end_n_top - - predictions = compute_predictions_log_probs( - examples, - features, - all_results, - args.n_best_size, - args.max_answer_length, - output_prediction_file, - output_nbest_file, - output_null_log_odds_file, - start_n_top, - end_n_top, - args.version_2_with_negative, - tokenizer, - args.verbose_logging, - ) - else: - predictions = compute_predictions_logits( - examples, - features, - all_results, - args.n_best_size, - args.max_answer_length, - args.do_lower_case, - output_prediction_file, - output_nbest_file, - output_null_log_odds_file, - args.verbose_logging, - args.version_2_with_negative, - args.null_score_diff_threshold, - tokenizer, - ) - - # Compute the F1 and exact scores. - results = squad_evaluate(examples, predictions) - return results - - -def load_and_cache_examples(args, tokenizer, evaluate=False, output_examples=False): - if args.local_rank not in [-1, 0] and not evaluate: - # Make sure only the first process in distributed training process the dataset, and the others will use the cache - torch.distributed.barrier() - - # Load data features from cache or dataset file - input_dir = args.data_dir if args.data_dir else "." - cached_features_file = os.path.join( - input_dir, - "cached_{}_{}_{}".format( - "dev" if evaluate else "train", - list(filter(None, args.model_name_or_path.split("/"))).pop(), - str(args.max_seq_length), - ), - ) - - # Init features and dataset from cache if it exists - if os.path.exists(cached_features_file) and not args.overwrite_cache: - logger.info("Loading features from cached file %s", cached_features_file) - features_and_dataset = torch.load(cached_features_file) - features, dataset, examples = ( - features_and_dataset["features"], - features_and_dataset["dataset"], - features_and_dataset["examples"], - ) - else: - logger.info("Creating features from dataset file at %s", input_dir) - - if not args.data_dir and ((evaluate and not args.predict_file) or (not evaluate and not args.train_file)): - try: - import tensorflow_datasets as tfds - except ImportError: - raise ImportError("If not data_dir is specified, tensorflow_datasets needs to be installed.") - - if args.version_2_with_negative: - logger.warn("tensorflow_datasets does not handle version 2 of SQuAD.") - - tfds_examples = tfds.load("squad") - examples = SquadV1Processor().get_examples_from_dataset(tfds_examples, evaluate=evaluate) - else: - processor = SquadV2Processor() if args.version_2_with_negative else SquadV1Processor() - if evaluate: - examples = processor.get_dev_examples(args.data_dir, filename=args.predict_file) - else: - examples = processor.get_train_examples(args.data_dir, filename=args.train_file) - - features, dataset = squad_convert_examples_to_features( - examples=examples, - tokenizer=tokenizer, - max_seq_length=args.max_seq_length, - doc_stride=args.doc_stride, - max_query_length=args.max_query_length, - is_training=not evaluate, - return_dataset="pt", - threads=args.threads, - ) - - if args.local_rank in [-1, 0]: - logger.info("Saving features into cached file %s", cached_features_file) - torch.save({"features": features, "dataset": dataset, "examples": examples}, cached_features_file) - - if args.local_rank == 0 and not evaluate: - # Make sure only the first process in distributed training process the dataset, and the others will use the cache - torch.distributed.barrier() - - if output_examples: - return dataset, examples, features - return dataset - - -def main(): - parser = argparse.ArgumentParser() - - # Required parameters - parser.add_argument( - "--model_type", - default=None, - type=str, - required=True, - help="Model type selected in the list: " + ", ".join(MODEL_TYPES), - ) - parser.add_argument( - "--model_name_or_path", - default=None, - type=str, - required=True, - help="Path to pretrained model or model identifier from huggingface.co/models", - ) - parser.add_argument( - "--output_dir", - default=None, - type=str, - required=True, - help="The output directory where the model checkpoints and predictions will be written.", - ) - - # Other parameters - parser.add_argument( - "--data_dir", - default=None, - type=str, - help="The input data dir. Should contain the .json files for the task." - + "If no data dir or train/predict files are specified, will run with tensorflow_datasets.", - ) - parser.add_argument( - "--train_file", - default=None, - type=str, - help="The input training file. If a data dir is specified, will look for the file there" - + "If no data dir or train/predict files are specified, will run with tensorflow_datasets.", - ) - parser.add_argument( - "--predict_file", - default=None, - type=str, - help="The input evaluation file. If a data dir is specified, will look for the file there" - + "If no data dir or train/predict files are specified, will run with tensorflow_datasets.", - ) - parser.add_argument( - "--config_name", default="", type=str, help="Pretrained config name or path if not the same as model_name" - ) - parser.add_argument( - "--tokenizer_name", - default="", - type=str, - help="Pretrained tokenizer name or path if not the same as model_name", - ) - parser.add_argument( - "--cache_dir", - default="", - type=str, - help="Where do you want to store the pre-trained models downloaded from huggingface.co", - ) - - parser.add_argument( - "--version_2_with_negative", - action="store_true", - help="If true, the SQuAD examples contain some that do not have an answer.", - ) - parser.add_argument( - "--null_score_diff_threshold", - type=float, - default=0.0, - help="If null_score - best_non_null is greater than the threshold predict null.", - ) - - parser.add_argument( - "--max_seq_length", - default=384, - type=int, - help="The maximum total input sequence length after WordPiece tokenization. Sequences " - "longer than this will be truncated, and sequences shorter than this will be padded.", - ) - parser.add_argument( - "--doc_stride", - default=128, - type=int, - help="When splitting up a long document into chunks, how much stride to take between chunks.", - ) - parser.add_argument( - "--max_query_length", - default=64, - type=int, - help="The maximum number of tokens for the question. Questions longer than this will " - "be truncated to this length.", - ) - parser.add_argument("--do_train", action="store_true", help="Whether to run training.") - parser.add_argument("--do_eval", action="store_true", help="Whether to run eval on the dev set.") - parser.add_argument( - "--evaluate_during_training", action="store_true", help="Run evaluation during training at each logging step." - ) - parser.add_argument( - "--do_lower_case", action="store_true", help="Set this flag if you are using an uncased model." - ) - - parser.add_argument("--per_gpu_train_batch_size", default=8, type=int, help="Batch size per GPU/CPU for training.") - parser.add_argument( - "--per_gpu_eval_batch_size", default=8, type=int, help="Batch size per GPU/CPU for evaluation." - ) - parser.add_argument("--learning_rate", default=5e-5, type=float, help="The initial learning rate for Adam.") - parser.add_argument( - "--gradient_accumulation_steps", - type=int, - default=1, - help="Number of updates steps to accumulate before performing a backward/update pass.", - ) - parser.add_argument("--weight_decay", default=0.0, type=float, help="Weight decay if we apply some.") - parser.add_argument("--adam_epsilon", default=1e-8, type=float, help="Epsilon for Adam optimizer.") - parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.") - parser.add_argument( - "--num_train_epochs", default=3.0, type=float, help="Total number of training epochs to perform." - ) - parser.add_argument( - "--max_steps", - default=-1, - type=int, - help="If > 0: set total number of training steps to perform. Override num_train_epochs.", - ) - parser.add_argument("--warmup_steps", default=0, type=int, help="Linear warmup over warmup_steps.") - parser.add_argument( - "--n_best_size", - default=20, - type=int, - help="The total number of n-best predictions to generate in the nbest_predictions.json output file.", - ) - parser.add_argument( - "--max_answer_length", - default=30, - type=int, - help="The maximum length of an answer that can be generated. This is needed because the start " - "and end predictions are not conditioned on one another.", - ) - parser.add_argument( - "--verbose_logging", - action="store_true", - help="If true, all of the warnings related to data processing will be printed. " - "A number of warnings are expected for a normal SQuAD evaluation.", - ) - parser.add_argument( - "--lang_id", - default=0, - type=int, - help="language id of input for language-specific xlm models (see tokenization_xlm.PRETRAINED_INIT_CONFIGURATION)", - ) - - parser.add_argument("--logging_steps", type=int, default=500, help="Log every X updates steps.") - parser.add_argument("--save_steps", type=int, default=500, help="Save checkpoint every X updates steps.") - parser.add_argument( - "--eval_all_checkpoints", - action="store_true", - help="Evaluate all checkpoints starting with the same prefix as model_name ending and ending with step number", - ) - parser.add_argument("--no_cuda", action="store_true", help="Whether not to use CUDA when available") - parser.add_argument( - "--overwrite_output_dir", action="store_true", help="Overwrite the content of the output directory" - ) - parser.add_argument( - "--overwrite_cache", action="store_true", help="Overwrite the cached training and evaluation sets" - ) - parser.add_argument("--seed", type=int, default=42, help="random seed for initialization") - - parser.add_argument("--local_rank", type=int, default=-1, help="local_rank for distributed training on gpus") - parser.add_argument( - "--fp16", - action="store_true", - help="Whether to use 16-bit (mixed) precision (through NVIDIA apex) instead of 32-bit", - ) - parser.add_argument( - "--fp16_opt_level", - type=str, - default="O1", - help="For fp16: Apex AMP optimization level selected in ['O0', 'O1', 'O2', and 'O3']." - "See details at https://nvidia.github.io/apex/amp.html", - ) - parser.add_argument("--server_ip", type=str, default="", help="Can be used for distant debugging.") - parser.add_argument("--server_port", type=str, default="", help="Can be used for distant debugging.") - - parser.add_argument("--threads", type=int, default=1, help="multiple threads for converting example to features") - args = parser.parse_args() - - if args.doc_stride >= args.max_seq_length - args.max_query_length: - logger.warning( - "WARNING - You've set a doc stride which may be superior to the document length in some " - "examples. This could result in errors when building features from the examples. Please reduce the doc " - "stride or increase the maximum length to ensure the features are correctly built." - ) - - if ( - os.path.exists(args.output_dir) - and os.listdir(args.output_dir) - and args.do_train - and not args.overwrite_output_dir - ): - raise ValueError( - "Output directory ({}) already exists and is not empty. Use --overwrite_output_dir to overcome.".format( - args.output_dir - ) - ) - - # Setup distant debugging if needed - if args.server_ip and args.server_port: - # Distant debugging - see https://code.visualstudio.com/docs/python/debugging#_attach-to-a-local-script - import ptvsd - - print("Waiting for debugger attach") - ptvsd.enable_attach(address=(args.server_ip, args.server_port), redirect_output=True) - ptvsd.wait_for_attach() - - # Setup CUDA, GPU & distributed training - if args.local_rank == -1 or args.no_cuda: - device = torch.device("cuda" if torch.cuda.is_available() and not args.no_cuda else "cpu") - args.n_gpu = 0 if args.no_cuda else torch.cuda.device_count() - else: # Initializes the distributed backend which will take care of sychronizing nodes/GPUs - torch.cuda.set_device(args.local_rank) - device = torch.device("cuda", args.local_rank) - torch.distributed.init_process_group(backend="nccl") - args.n_gpu = 1 - args.device = device - - # Setup logging - logging.basicConfig( - format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", - datefmt="%m/%d/%Y %H:%M:%S", - level=logging.INFO if args.local_rank in [-1, 0] else logging.WARN, - ) - logger.warning( - "Process rank: %s, device: %s, n_gpu: %s, distributed training: %s, 16-bits training: %s", - args.local_rank, - device, - args.n_gpu, - bool(args.local_rank != -1), - args.fp16, - ) - # Set the verbosity to info of the Transformers logger (on main process only): - if is_main_process(args.local_rank): - transformers.utils.logging.set_verbosity_info() - transformers.utils.logging.enable_default_handler() - transformers.utils.logging.enable_explicit_format() - # Set seed - set_seed(args) - - # Load pretrained model and tokenizer - if args.local_rank not in [-1, 0]: - # Make sure only the first process in distributed training will download model & vocab - torch.distributed.barrier() - - args.model_type = args.model_type.lower() - config = AutoConfig.from_pretrained( - args.config_name if args.config_name else args.model_name_or_path, - cache_dir=args.cache_dir if args.cache_dir else None, - ) - tokenizer = AutoTokenizer.from_pretrained( - args.tokenizer_name if args.tokenizer_name else args.model_name_or_path, - do_lower_case=args.do_lower_case, - cache_dir=args.cache_dir if args.cache_dir else None, - use_fast=False, # SquadDataset is not compatible with Fast tokenizers which have a smarter overflow handeling - ) - model = AutoModelForQuestionAnswering.from_pretrained( - args.model_name_or_path, - from_tf=bool(".ckpt" in args.model_name_or_path), - config=config, - cache_dir=args.cache_dir if args.cache_dir else None, - ) - - if args.local_rank == 0: - # Make sure only the first process in distributed training will download model & vocab - torch.distributed.barrier() - - model.to(args.device) - - logger.info("Training/evaluation parameters %s", args) - - # Before we do anything with models, we want to ensure that we get fp16 execution of torch.einsum if args.fp16 is set. - # Otherwise it'll default to "promote" mode, and we'll get fp32 operations. Note that running `--fp16_opt_level="O2"` will - # remove the need for this code, but it is still valid. - if args.fp16: - try: - import apex - - apex.amp.register_half_function(torch, "einsum") - except ImportError: - raise ImportError("Please install apex from https://www.github.com/nvidia/apex to use fp16 training.") - - # Training - if args.do_train: - train_dataset = load_and_cache_examples(args, tokenizer, evaluate=False, output_examples=False) - global_step, tr_loss = train(args, train_dataset, model, tokenizer) - logger.info(" global_step = %s, average loss = %s", global_step, tr_loss) - - # Save the trained model and the tokenizer - if args.do_train and (args.local_rank == -1 or torch.distributed.get_rank() == 0): - logger.info("Saving model checkpoint to %s", args.output_dir) - # Save a trained model, configuration and tokenizer using `save_pretrained()`. - # They can then be reloaded using `from_pretrained()` - # Take care of distributed/parallel training - model_to_save = model.module if hasattr(model, "module") else model - model_to_save.save_pretrained(args.output_dir) - tokenizer.save_pretrained(args.output_dir) - - # Good practice: save your training arguments together with the trained model - torch.save(args, os.path.join(args.output_dir, "training_args.bin")) - - # Load a trained model and vocabulary that you have fine-tuned - model = AutoModelForQuestionAnswering.from_pretrained(args.output_dir) # , force_download=True) - - # SquadDataset is not compatible with Fast tokenizers which have a smarter overflow handeling - # So we use use_fast=False here for now until Fast-tokenizer-compatible-examples are out - tokenizer = AutoTokenizer.from_pretrained(args.output_dir, do_lower_case=args.do_lower_case, use_fast=False) - model.to(args.device) - - # Evaluation - we can ask to evaluate all the checkpoints (sub-directories) in a directory - results = {} - if args.do_eval and args.local_rank in [-1, 0]: - if args.do_train: - logger.info("Loading checkpoints saved during training for evaluation") - checkpoints = [args.output_dir] - if args.eval_all_checkpoints: - checkpoints = list( - os.path.dirname(c) - for c in sorted(glob.glob(args.output_dir + "/**/" + WEIGHTS_NAME, recursive=True)) - ) - - else: - logger.info("Loading checkpoint %s for evaluation", args.model_name_or_path) - checkpoints = [args.model_name_or_path] - - logger.info("Evaluate the following checkpoints: %s", checkpoints) - - for checkpoint in checkpoints: - # Reload the model - global_step = checkpoint.split("-")[-1] if len(checkpoints) > 1 else "" - model = AutoModelForQuestionAnswering.from_pretrained(checkpoint) # , force_download=True) - model.to(args.device) - - # Evaluate - result = evaluate(args, model, tokenizer, prefix=global_step) - - result = dict((k + ("_{}".format(global_step) if global_step else ""), v) for k, v in result.items()) - results.update(result) - - logger.info("Results: {}".format(results)) - - return results - - -if __name__ == "__main__": - main() diff --git a/examples/pytorch/huggingface_models/examples/legacy/question-answering/run_squad_trainer.py b/examples/pytorch/huggingface_models/examples/legacy/question-answering/run_squad_trainer.py deleted file mode 100644 index 1b1d6e6fed4..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/question-answering/run_squad_trainer.py +++ /dev/null @@ -1,185 +0,0 @@ -# coding=utf-8 -# Copyright 2018 The Google AI Language Team Authors and The HuggingFace Inc. team. -# Copyright (c) 2018, NVIDIA CORPORATION. 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. -""" Fine-tuning the library models for question-answering.""" - - -import logging -import os -import sys -from dataclasses import dataclass, field -from typing import Optional - -import transformers -from transformers import ( - AutoConfig, - AutoModelForQuestionAnswering, - AutoTokenizer, - DataCollatorWithPadding, - HfArgumentParser, - SquadDataset, -) -from transformers import SquadDataTrainingArguments as DataTrainingArguments -from transformers import Trainer, TrainingArguments -from transformers.trainer_utils import is_main_process - - -logger = logging.getLogger(__name__) - - -@dataclass -class ModelArguments: - """ - Arguments pertaining to which model/config/tokenizer we are going to fine-tune from. - """ - - model_name_or_path: str = field( - metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"} - ) - config_name: Optional[str] = field( - default=None, metadata={"help": "Pretrained config name or path if not the same as model_name"} - ) - tokenizer_name: Optional[str] = field( - default=None, metadata={"help": "Pretrained tokenizer name or path if not the same as model_name"} - ) - use_fast: bool = field(default=False, metadata={"help": "Set this flag to use fast tokenization."}) - # If you want to tweak more attributes on your tokenizer, you should do it in a distinct script, - # or just modify its tokenizer_config.json. - cache_dir: Optional[str] = field( - default=None, - metadata={"help": "Where do you want to store the pretrained models downloaded from huggingface.co"}, - ) - - -def main(): - # See all possible arguments in src/transformers/training_args.py - # or by passing the --help flag to this script. - # We now keep distinct sets of args, for a cleaner separation of concerns. - - parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments)) - - if len(sys.argv) == 2 and sys.argv[1].endswith(".json"): - # If we pass only one argument to the script and it's the path to a json file, - # let's parse it to get our arguments. - model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1])) - else: - model_args, data_args, training_args = parser.parse_args_into_dataclasses() - - if ( - os.path.exists(training_args.output_dir) - and os.listdir(training_args.output_dir) - and training_args.do_train - and not training_args.overwrite_output_dir - ): - raise ValueError( - f"Output directory ({training_args.output_dir}) already exists and is not empty. Use --overwrite_output_dir to overcome." - ) - - # Setup logging - logging.basicConfig( - format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", - datefmt="%m/%d/%Y %H:%M:%S", - level=logging.INFO if training_args.local_rank in [-1, 0] else logging.WARN, - ) - logger.warning( - "Process rank: %s, device: %s, n_gpu: %s, distributed training: %s, 16-bits training: %s", - training_args.local_rank, - training_args.device, - training_args.n_gpu, - bool(training_args.local_rank != -1), - training_args.fp16, - ) - # Set the verbosity to info of the Transformers logger (on main process only): - if is_main_process(training_args.local_rank): - transformers.utils.logging.set_verbosity_info() - transformers.utils.logging.enable_default_handler() - transformers.utils.logging.enable_explicit_format() - logger.info("Training/evaluation parameters %s", training_args) - - # Prepare Question-Answering task - # Load pretrained model and tokenizer - # - # Distributed training: - # The .from_pretrained methods guarantee that only one local process can concurrently - # download model & vocab. - - config = AutoConfig.from_pretrained( - model_args.config_name if model_args.config_name else model_args.model_name_or_path, - cache_dir=model_args.cache_dir, - ) - tokenizer = AutoTokenizer.from_pretrained( - model_args.tokenizer_name if model_args.tokenizer_name else model_args.model_name_or_path, - cache_dir=model_args.cache_dir, - use_fast=False, # SquadDataset is not compatible with Fast tokenizers which have a smarter overflow handeling - ) - model = AutoModelForQuestionAnswering.from_pretrained( - model_args.model_name_or_path, - from_tf=bool(".ckpt" in model_args.model_name_or_path), - config=config, - cache_dir=model_args.cache_dir, - ) - - # Get datasets - is_language_sensitive = hasattr(model.config, "lang2id") - train_dataset = ( - SquadDataset( - data_args, tokenizer=tokenizer, is_language_sensitive=is_language_sensitive, cache_dir=model_args.cache_dir - ) - if training_args.do_train - else None - ) - eval_dataset = ( - SquadDataset( - data_args, - tokenizer=tokenizer, - mode="dev", - is_language_sensitive=is_language_sensitive, - cache_dir=model_args.cache_dir, - ) - if training_args.do_eval - else None - ) - - # Data collator - data_collator = DataCollatorWithPadding(tokenizer, pad_to_multiple_of=8) if training_args.fp16 else None - - # Initialize our Trainer - trainer = Trainer( - model=model, - args=training_args, - train_dataset=train_dataset, - eval_dataset=eval_dataset, - data_collator=data_collator, - ) - - # Training - if training_args.do_train: - trainer.train( - model_path=model_args.model_name_or_path if os.path.isdir(model_args.model_name_or_path) else None - ) - trainer.save_model() - # For convenience, we also re-save the tokenizer to the same directory, - # so that you can share your model easily on huggingface.co/models =) - if trainer.is_world_master(): - tokenizer.save_pretrained(training_args.output_dir) - - -def _mp_fn(index): - # For xla_spawn (TPUs) - main() - - -if __name__ == "__main__": - main() diff --git a/examples/pytorch/huggingface_models/examples/legacy/run_camembert.py b/examples/pytorch/huggingface_models/examples/legacy/run_camembert.py deleted file mode 100755 index 9651570b39e..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/run_camembert.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python -import torch - -from transformers import CamembertForMaskedLM, CamembertTokenizer - - -def fill_mask(masked_input, model, tokenizer, topk=5): - # Adapted from https://github.com/pytorch/fairseq/blob/master/fairseq/models/roberta/hub_interface.py - assert masked_input.count("") == 1 - input_ids = torch.tensor(tokenizer.encode(masked_input, add_special_tokens=True)).unsqueeze(0) # Batch size 1 - logits = model(input_ids)[0] # The last hidden-state is the first element of the output tuple - masked_index = (input_ids.squeeze() == tokenizer.mask_token_id).nonzero().item() - logits = logits[0, masked_index, :] - prob = logits.softmax(dim=0) - values, indices = prob.topk(k=topk, dim=0) - topk_predicted_token_bpe = " ".join( - [tokenizer.convert_ids_to_tokens(indices[i].item()) for i in range(len(indices))] - ) - masked_token = tokenizer.mask_token - topk_filled_outputs = [] - for index, predicted_token_bpe in enumerate(topk_predicted_token_bpe.split(" ")): - predicted_token = predicted_token_bpe.replace("\u2581", " ") - if " {0}".format(masked_token) in masked_input: - topk_filled_outputs.append( - ( - masked_input.replace(" {0}".format(masked_token), predicted_token), - values[index].item(), - predicted_token, - ) - ) - else: - topk_filled_outputs.append( - ( - masked_input.replace(masked_token, predicted_token), - values[index].item(), - predicted_token, - ) - ) - return topk_filled_outputs - - -tokenizer = CamembertTokenizer.from_pretrained("camembert-base") -model = CamembertForMaskedLM.from_pretrained("camembert-base") -model.eval() - -masked_input = "Le camembert est :)" -print(fill_mask(masked_input, model, tokenizer, topk=3)) diff --git a/examples/pytorch/huggingface_models/examples/legacy/run_chinese_ref.py b/examples/pytorch/huggingface_models/examples/legacy/run_chinese_ref.py deleted file mode 100755 index f7c09e37ff8..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/run_chinese_ref.py +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/env python -import argparse -import json -from typing import List - -from ltp import LTP -from transformers import BertTokenizer - - -def _is_chinese_char(cp): - """Checks whether CP is the codepoint of a CJK character.""" - # This defines a "chinese character" as anything in the CJK Unicode block: - # https://en.wikipedia.org/wiki/CJK_Unified_Ideographs_(Unicode_block) - # - # Note that the CJK Unicode block is NOT all Japanese and Korean characters, - # despite its name. The modern Korean Hangul alphabet is a different block, - # as is Japanese Hiragana and Katakana. Those alphabets are used to write - # space-separated words, so they are not treated specially and handled - # like the all of the other languages. - if ( - (cp >= 0x4E00 and cp <= 0x9FFF) - or (cp >= 0x3400 and cp <= 0x4DBF) # - or (cp >= 0x20000 and cp <= 0x2A6DF) # - or (cp >= 0x2A700 and cp <= 0x2B73F) # - or (cp >= 0x2B740 and cp <= 0x2B81F) # - or (cp >= 0x2B820 and cp <= 0x2CEAF) # - or (cp >= 0xF900 and cp <= 0xFAFF) - or (cp >= 0x2F800 and cp <= 0x2FA1F) # - ): # - return True - - return False - - -def is_chinese(word: str): - # word like '180' or '身高' or '神' - for char in word: - char = ord(char) - if not _is_chinese_char(char): - return 0 - return 1 - - -def get_chinese_word(tokens: List[str]): - word_set = set() - - for token in tokens: - chinese_word = len(token) > 1 and is_chinese(token) - if chinese_word: - word_set.add(token) - word_list = list(word_set) - return word_list - - -def add_sub_symbol(bert_tokens: List[str], chinese_word_set: set()): - if not chinese_word_set: - return bert_tokens - max_word_len = max([len(w) for w in chinese_word_set]) - - bert_word = bert_tokens - start, end = 0, len(bert_word) - while start < end: - single_word = True - if is_chinese(bert_word[start]): - l = min(end - start, max_word_len) - for i in range(l, 1, -1): - whole_word = "".join(bert_word[start : start + i]) - if whole_word in chinese_word_set: - for j in range(start + 1, start + i): - bert_word[j] = "##" + bert_word[j] - start = start + i - single_word = False - break - if single_word: - start += 1 - return bert_word - - -def prepare_ref(lines: List[str], ltp_tokenizer: LTP, bert_tokenizer: BertTokenizer): - ltp_res = [] - - for i in range(0, len(lines), 100): - res = ltp_tokenizer.seg(lines[i : i + 100])[0] - res = [get_chinese_word(r) for r in res] - ltp_res.extend(res) - assert len(ltp_res) == len(lines) - - bert_res = [] - for i in range(0, len(lines), 100): - res = bert_tokenizer(lines[i : i + 100], add_special_tokens=True, truncation=True, max_length=512) - bert_res.extend(res["input_ids"]) - assert len(bert_res) == len(lines) - - ref_ids = [] - for input_ids, chinese_word in zip(bert_res, ltp_res): - - input_tokens = [] - for id in input_ids: - token = bert_tokenizer._convert_id_to_token(id) - input_tokens.append(token) - input_tokens = add_sub_symbol(input_tokens, chinese_word) - ref_id = [] - # We only save pos of chinese subwords start with ##, which mean is part of a whole word. - for i, token in enumerate(input_tokens): - if token[:2] == "##": - clean_token = token[2:] - # save chinese tokens' pos - if len(clean_token) == 1 and _is_chinese_char(ord(clean_token)): - ref_id.append(i) - ref_ids.append(ref_id) - - assert len(ref_ids) == len(bert_res) - - return ref_ids - - -def main(args): - # For Chinese (Ro)Bert, the best result is from : RoBERTa-wwm-ext (https://github.com/ymcui/Chinese-BERT-wwm) - # If we want to fine-tune these model, we have to use same tokenizer : LTP (https://github.com/HIT-SCIR/ltp) - with open(args.file_name, "r", encoding="utf-8") as f: - data = f.readlines() - data = [line.strip() for line in data if len(line) > 0 and not line.isspace()] # avoid delimiter like '\u2029' - ltp_tokenizer = LTP(args.ltp) # faster in GPU device - bert_tokenizer = BertTokenizer.from_pretrained(args.bert) - - ref_ids = prepare_ref(data, ltp_tokenizer, bert_tokenizer) - - with open(args.save_path, "w", encoding="utf-8") as f: - data = [json.dumps(ref) + "\n" for ref in ref_ids] - f.writelines(data) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="prepare_chinese_ref") - parser.add_argument( - "--file_name", - type=str, - default="./resources/chinese-demo.txt", - help="file need process, same as training data in lm", - ) - parser.add_argument( - "--ltp", type=str, default="./resources/ltp", help="resources for LTP tokenizer, usually a path" - ) - parser.add_argument("--bert", type=str, default="./resources/robert", help="resources for Bert tokenizer") - parser.add_argument("--save_path", type=str, default="./resources/ref.txt", help="path to save res") - - args = parser.parse_args() - main(args) diff --git a/examples/pytorch/huggingface_models/examples/legacy/run_language_modeling.py b/examples/pytorch/huggingface_models/examples/legacy/run_language_modeling.py deleted file mode 100755 index 20995f1bfaa..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/run_language_modeling.py +++ /dev/null @@ -1,364 +0,0 @@ -#!/usr/bin/env python -# coding=utf-8 -# Copyright 2018 The Google AI Language Team Authors and The HuggingFace Inc. team. -# Copyright (c) 2018, NVIDIA CORPORATION. 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. -""" -Fine-tuning the library models for language modeling on a text file (GPT, GPT-2, CTRL, BERT, RoBERTa, XLNet). -GPT, GPT-2 and CTRL are fine-tuned using a causal language modeling (CLM) loss. BERT and RoBERTa are fine-tuned -using a masked language modeling (MLM) loss. XLNet is fine-tuned using a permutation language modeling (PLM) loss. -""" - - -import logging -import math -import os -from dataclasses import dataclass, field -from glob import glob -from typing import Optional - -from torch.utils.data import ConcatDataset - -import transformers -from transformers import ( - CONFIG_MAPPING, - MODEL_WITH_LM_HEAD_MAPPING, - AutoConfig, - AutoModelWithLMHead, - AutoTokenizer, - DataCollatorForLanguageModeling, - DataCollatorForPermutationLanguageModeling, - DataCollatorForWholeWordMask, - HfArgumentParser, - LineByLineTextDataset, - LineByLineWithRefDataset, - PreTrainedTokenizer, - TextDataset, - Trainer, - TrainingArguments, - set_seed, -) -from transformers.trainer_utils import is_main_process - - -logger = logging.getLogger(__name__) - - -MODEL_CONFIG_CLASSES = list(MODEL_WITH_LM_HEAD_MAPPING.keys()) -MODEL_TYPES = tuple(conf.model_type for conf in MODEL_CONFIG_CLASSES) - - -@dataclass -class ModelArguments: - """ - Arguments pertaining to which model/config/tokenizer we are going to fine-tune, or train from scratch. - """ - - model_name_or_path: Optional[str] = field( - default=None, - metadata={ - "help": "The model checkpoint for weights initialization. Leave None if you want to train a model from scratch." - }, - ) - model_type: Optional[str] = field( - default=None, - metadata={"help": "If training from scratch, pass a model type from the list: " + ", ".join(MODEL_TYPES)}, - ) - config_name: Optional[str] = field( - default=None, metadata={"help": "Pretrained config name or path if not the same as model_name"} - ) - tokenizer_name: Optional[str] = field( - default=None, metadata={"help": "Pretrained tokenizer name or path if not the same as model_name"} - ) - cache_dir: Optional[str] = field( - default=None, - metadata={"help": "Where do you want to store the pretrained models downloaded from huggingface.co"}, - ) - - -@dataclass -class DataTrainingArguments: - """ - Arguments pertaining to what data we are going to input our model for training and eval. - """ - - train_data_file: Optional[str] = field( - default=None, metadata={"help": "The input training data file (a text file)."} - ) - train_data_files: Optional[str] = field( - default=None, - metadata={ - "help": "The input training data files (multiple files in glob format). " - "Very often splitting large files to smaller files can prevent tokenizer going out of memory" - }, - ) - eval_data_file: Optional[str] = field( - default=None, - metadata={"help": "An optional input evaluation data file to evaluate the perplexity on (a text file)."}, - ) - train_ref_file: Optional[str] = field( - default=None, - metadata={"help": "An optional input train ref data file for whole word mask in Chinese."}, - ) - eval_ref_file: Optional[str] = field( - default=None, - metadata={"help": "An optional input eval ref data file for whole word mask in Chinese."}, - ) - line_by_line: bool = field( - default=False, - metadata={"help": "Whether distinct lines of text in the dataset are to be handled as distinct sequences."}, - ) - - mlm: bool = field( - default=False, metadata={"help": "Train with masked-language modeling loss instead of language modeling."} - ) - whole_word_mask: bool = field(default=False, metadata={"help": "Whether ot not to use whole word mask."}) - mlm_probability: float = field( - default=0.15, metadata={"help": "Ratio of tokens to mask for masked language modeling loss"} - ) - plm_probability: float = field( - default=1 / 6, - metadata={ - "help": "Ratio of length of a span of masked tokens to surrounding context length for permutation language modeling." - }, - ) - max_span_length: int = field( - default=5, metadata={"help": "Maximum length of a span of masked tokens for permutation language modeling."} - ) - - block_size: int = field( - default=-1, - metadata={ - "help": "Optional input sequence length after tokenization." - "The training dataset will be truncated in block of this size for training." - "Default to the model max input length for single sentence inputs (take into account special tokens)." - }, - ) - overwrite_cache: bool = field( - default=False, metadata={"help": "Overwrite the cached training and evaluation sets"} - ) - - -def get_dataset( - args: DataTrainingArguments, - tokenizer: PreTrainedTokenizer, - evaluate: bool = False, - cache_dir: Optional[str] = None, -): - def _dataset(file_path, ref_path=None): - if args.line_by_line: - if ref_path is not None: - if not args.whole_word_mask or not args.mlm: - raise ValueError("You need to set world whole masking and mlm to True for Chinese Whole Word Mask") - return LineByLineWithRefDataset( - tokenizer=tokenizer, - file_path=file_path, - block_size=args.block_size, - ref_path=ref_path, - ) - - return LineByLineTextDataset(tokenizer=tokenizer, file_path=file_path, block_size=args.block_size) - else: - return TextDataset( - tokenizer=tokenizer, - file_path=file_path, - block_size=args.block_size, - overwrite_cache=args.overwrite_cache, - cache_dir=cache_dir, - ) - - if evaluate: - return _dataset(args.eval_data_file, args.eval_ref_file) - elif args.train_data_files: - return ConcatDataset([_dataset(f) for f in glob(args.train_data_files)]) - else: - return _dataset(args.train_data_file, args.train_ref_file) - - -def main(): - # See all possible arguments in src/transformers/training_args.py - # or by passing the --help flag to this script. - # We now keep distinct sets of args, for a cleaner separation of concerns. - - parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments)) - model_args, data_args, training_args = parser.parse_args_into_dataclasses() - - if data_args.eval_data_file is None and training_args.do_eval: - raise ValueError( - "Cannot do evaluation without an evaluation data file. Either supply a file to --eval_data_file " - "or remove the --do_eval argument." - ) - if ( - os.path.exists(training_args.output_dir) - and os.listdir(training_args.output_dir) - and training_args.do_train - and not training_args.overwrite_output_dir - ): - raise ValueError( - f"Output directory ({training_args.output_dir}) already exists and is not empty. Use --overwrite_output_dir to overcome." - ) - - # Setup logging - logging.basicConfig( - format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", - datefmt="%m/%d/%Y %H:%M:%S", - level=logging.INFO if training_args.local_rank in [-1, 0] else logging.WARN, - ) - logger.warning( - "Process rank: %s, device: %s, n_gpu: %s, distributed training: %s, 16-bits training: %s", - training_args.local_rank, - training_args.device, - training_args.n_gpu, - bool(training_args.local_rank != -1), - training_args.fp16, - ) - # Set the verbosity to info of the Transformers logger (on main process only): - if is_main_process(training_args.local_rank): - transformers.utils.logging.set_verbosity_info() - transformers.utils.logging.enable_default_handler() - transformers.utils.logging.enable_explicit_format() - logger.info("Training/evaluation parameters %s", training_args) - - # Set seed - set_seed(training_args.seed) - - # Load pretrained model and tokenizer - # - # Distributed training: - # The .from_pretrained methods guarantee that only one local process can concurrently - # download model & vocab. - - if model_args.config_name: - config = AutoConfig.from_pretrained(model_args.config_name, cache_dir=model_args.cache_dir) - elif model_args.model_name_or_path: - config = AutoConfig.from_pretrained(model_args.model_name_or_path, cache_dir=model_args.cache_dir) - else: - config = CONFIG_MAPPING[model_args.model_type]() - logger.warning("You are instantiating a new config instance from scratch.") - - if model_args.tokenizer_name: - tokenizer = AutoTokenizer.from_pretrained(model_args.tokenizer_name, cache_dir=model_args.cache_dir) - elif model_args.model_name_or_path: - tokenizer = AutoTokenizer.from_pretrained(model_args.model_name_or_path, cache_dir=model_args.cache_dir) - else: - raise ValueError( - "You are instantiating a new tokenizer from scratch. This is not supported, but you can do it from another script, save it," - "and load it from here, using --tokenizer_name" - ) - - if model_args.model_name_or_path: - model = AutoModelWithLMHead.from_pretrained( - model_args.model_name_or_path, - from_tf=bool(".ckpt" in model_args.model_name_or_path), - config=config, - cache_dir=model_args.cache_dir, - ) - else: - logger.info("Training new model from scratch") - model = AutoModelWithLMHead.from_config(config) - - model.resize_token_embeddings(len(tokenizer)) - - if config.model_type in ["bert", "roberta", "distilbert", "camembert"] and not data_args.mlm: - raise ValueError( - "BERT and RoBERTa-like models do not have LM heads but masked LM heads. They must be run using the" - "--mlm flag (masked language modeling)." - ) - - if data_args.block_size <= 0: - data_args.block_size = tokenizer.max_len - # Our input block size will be the max possible for the model - else: - data_args.block_size = min(data_args.block_size, tokenizer.max_len) - - # Get datasets - - train_dataset = ( - get_dataset(data_args, tokenizer=tokenizer, cache_dir=model_args.cache_dir) if training_args.do_train else None - ) - eval_dataset = ( - get_dataset(data_args, tokenizer=tokenizer, evaluate=True, cache_dir=model_args.cache_dir) - if training_args.do_eval - else None - ) - if config.model_type == "xlnet": - data_collator = DataCollatorForPermutationLanguageModeling( - tokenizer=tokenizer, - plm_probability=data_args.plm_probability, - max_span_length=data_args.max_span_length, - ) - else: - if data_args.mlm and data_args.whole_word_mask: - data_collator = DataCollatorForWholeWordMask( - tokenizer=tokenizer, mlm_probability=data_args.mlm_probability - ) - else: - data_collator = DataCollatorForLanguageModeling( - tokenizer=tokenizer, mlm=data_args.mlm, mlm_probability=data_args.mlm_probability - ) - - # Initialize our Trainer - trainer = Trainer( - model=model, - args=training_args, - data_collator=data_collator, - train_dataset=train_dataset, - eval_dataset=eval_dataset, - prediction_loss_only=True, - ) - - # Training - if training_args.do_train: - model_path = ( - model_args.model_name_or_path - if model_args.model_name_or_path is not None and os.path.isdir(model_args.model_name_or_path) - else None - ) - trainer.train(model_path=model_path) - trainer.save_model() - # For convenience, we also re-save the tokenizer to the same directory, - # so that you can share your model easily on huggingface.co/models =) - if trainer.is_world_master(): - tokenizer.save_pretrained(training_args.output_dir) - - # Evaluation - results = {} - if training_args.do_eval: - logger.info("*** Evaluate ***") - - eval_output = trainer.evaluate() - - perplexity = math.exp(eval_output["eval_loss"]) - result = {"perplexity": perplexity} - - output_eval_file = os.path.join(training_args.output_dir, "eval_results_lm.txt") - if trainer.is_world_master(): - with open(output_eval_file, "w") as writer: - logger.info("***** Eval results *****") - for key in sorted(result.keys()): - logger.info(" %s = %s", key, str(result[key])) - writer.write("%s = %s\n" % (key, str(result[key]))) - - results.update(result) - - return results - - -def _mp_fn(index): - # For xla_spawn (TPUs) - main() - - -if __name__ == "__main__": - main() diff --git a/examples/pytorch/huggingface_models/examples/legacy/run_openai_gpt.py b/examples/pytorch/huggingface_models/examples/legacy/run_openai_gpt.py deleted file mode 100755 index 72314b5edb3..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/run_openai_gpt.py +++ /dev/null @@ -1,320 +0,0 @@ -#!/usr/bin/env python -# coding=utf-8 -# Copyright 2018 Google AI, Google Brain and Carnegie Mellon University Authors and the HuggingFace Inc. team. -# Copyright (c) 2018, NVIDIA CORPORATION. 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. -""" OpenAI GPT model fine-tuning script. - Adapted from https://github.com/huggingface/pytorch-openai-transformer-lm/blob/master/train.py - It self adapted from https://github.com/openai/finetune-transformer-lm/blob/master/train.py - - This script with default values fine-tunes and evaluate a pretrained OpenAI GPT on the RocStories dataset: - python run_openai_gpt.py \ - --model_name openai-gpt \ - --do_train \ - --do_eval \ - --train_dataset "$ROC_STORIES_DIR/cloze_test_val__spring2016 - cloze_test_ALL_val.csv" \ - --eval_dataset "$ROC_STORIES_DIR/cloze_test_test__spring2016 - cloze_test_ALL_test.csv" \ - --output_dir ../log \ - --train_batch_size 16 \ -""" -import argparse -import csv -import logging -import os -import random - -import numpy as np -import torch -from torch.utils.data import DataLoader, RandomSampler, SequentialSampler, TensorDataset -from tqdm import tqdm, trange - -from transformers import ( - CONFIG_NAME, - WEIGHTS_NAME, - AdamW, - OpenAIGPTDoubleHeadsModel, - OpenAIGPTTokenizer, - get_linear_schedule_with_warmup, -) - - -logging.basicConfig( - format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", datefmt="%m/%d/%Y %H:%M:%S", level=logging.INFO -) -logger = logging.getLogger(__name__) - - -def accuracy(out, labels): - outputs = np.argmax(out, axis=1) - return np.sum(outputs == labels) - - -def load_rocstories_dataset(dataset_path): - """ Output a list of tuples(story, 1st continuation, 2nd continuation, label) """ - with open(dataset_path, encoding="utf_8") as f: - f = csv.reader(f) - output = [] - next(f) # skip the first line - for line in tqdm(f): - output.append((" ".join(line[1:5]), line[5], line[6], int(line[-1]) - 1)) - return output - - -def pre_process_datasets(encoded_datasets, input_len, cap_length, start_token, delimiter_token, clf_token): - """Pre-process datasets containing lists of tuples(story, 1st continuation, 2nd continuation, label) - - To Transformer inputs of shape (n_batch, n_alternative, length) comprising for each batch, continuation: - input_ids[batch, alternative, :] = [start_token] + story[:cap_length] + [delimiter_token] + cont1[:cap_length] + [clf_token] - """ - tensor_datasets = [] - for dataset in encoded_datasets: - n_batch = len(dataset) - input_ids = np.zeros((n_batch, 2, input_len), dtype=np.int64) - mc_token_ids = np.zeros((n_batch, 2), dtype=np.int64) - lm_labels = np.full((n_batch, 2, input_len), fill_value=-100, dtype=np.int64) - mc_labels = np.zeros((n_batch,), dtype=np.int64) - for ( - i, - (story, cont1, cont2, mc_label), - ) in enumerate(dataset): - with_cont1 = [start_token] + story[:cap_length] + [delimiter_token] + cont1[:cap_length] + [clf_token] - with_cont2 = [start_token] + story[:cap_length] + [delimiter_token] + cont2[:cap_length] + [clf_token] - input_ids[i, 0, : len(with_cont1)] = with_cont1 - input_ids[i, 1, : len(with_cont2)] = with_cont2 - mc_token_ids[i, 0] = len(with_cont1) - 1 - mc_token_ids[i, 1] = len(with_cont2) - 1 - lm_labels[i, 0, : len(with_cont1)] = with_cont1 - lm_labels[i, 1, : len(with_cont2)] = with_cont2 - mc_labels[i] = mc_label - all_inputs = (input_ids, mc_token_ids, lm_labels, mc_labels) - tensor_datasets.append(tuple(torch.tensor(t) for t in all_inputs)) - return tensor_datasets - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("--model_name", type=str, default="openai-gpt", help="pretrained model name") - parser.add_argument("--do_train", action="store_true", help="Whether to run training.") - parser.add_argument("--do_eval", action="store_true", help="Whether to run eval on the dev set.") - parser.add_argument( - "--output_dir", - default=None, - type=str, - required=True, - help="The output directory where the model predictions and checkpoints will be written.", - ) - parser.add_argument("--train_dataset", type=str, default="") - parser.add_argument("--eval_dataset", type=str, default="") - parser.add_argument("--seed", type=int, default=42) - parser.add_argument("--num_train_epochs", type=int, default=3) - parser.add_argument("--train_batch_size", type=int, default=8) - parser.add_argument("--eval_batch_size", type=int, default=16) - parser.add_argument("--adam_epsilon", default=1e-8, type=float, help="Epsilon for Adam optimizer.") - parser.add_argument("--max_grad_norm", type=int, default=1) - parser.add_argument( - "--max_steps", - default=-1, - type=int, - help="If > 0: set total number of training \ - steps to perform. Override num_train_epochs.", - ) - parser.add_argument( - "--gradient_accumulation_steps", - type=int, - default=1, - help="Number of updates steps to accumulate before\ - performing a backward/update pass.", - ) - parser.add_argument("--learning_rate", type=float, default=6.25e-5) - parser.add_argument("--warmup_steps", default=0, type=int, help="Linear warmup over warmup_steps.") - parser.add_argument("--lr_schedule", type=str, default="warmup_linear") - parser.add_argument("--weight_decay", type=float, default=0.01) - parser.add_argument("--lm_coef", type=float, default=0.9) - parser.add_argument("--n_valid", type=int, default=374) - - parser.add_argument("--server_ip", type=str, default="", help="Can be used for distant debugging.") - parser.add_argument("--server_port", type=str, default="", help="Can be used for distant debugging.") - args = parser.parse_args() - print(args) - - if args.server_ip and args.server_port: - # Distant debugging - see https://code.visualstudio.com/docs/python/debugging#_attach-to-a-local-script - import ptvsd - - print("Waiting for debugger attach") - ptvsd.enable_attach(address=(args.server_ip, args.server_port), redirect_output=True) - ptvsd.wait_for_attach() - - random.seed(args.seed) - np.random.seed(args.seed) - torch.manual_seed(args.seed) - torch.cuda.manual_seed_all(args.seed) - - device = torch.device("cuda" if torch.cuda.is_available() else "cpu") - n_gpu = torch.cuda.device_count() - logger.info("device: {}, n_gpu {}".format(device, n_gpu)) - - if not args.do_train and not args.do_eval: - raise ValueError("At least one of `do_train` or `do_eval` must be True.") - - if not os.path.exists(args.output_dir): - os.makedirs(args.output_dir) - - # Load tokenizer and model - # This loading functions also add new tokens and embeddings called `special tokens` - # These new embeddings will be fine-tuned on the RocStories dataset - special_tokens = ["_start_", "_delimiter_", "_classify_"] - tokenizer = OpenAIGPTTokenizer.from_pretrained(args.model_name) - tokenizer.add_tokens(special_tokens) - special_tokens_ids = tokenizer.convert_tokens_to_ids(special_tokens) - model = OpenAIGPTDoubleHeadsModel.from_pretrained(args.model_name) - model.resize_token_embeddings(len(tokenizer)) - model.to(device) - - # Load and encode the datasets - def tokenize_and_encode(obj): - """ Tokenize and encode a nested object """ - if isinstance(obj, str): - return tokenizer.convert_tokens_to_ids(tokenizer.tokenize(obj)) - elif isinstance(obj, int): - return obj - return list(tokenize_and_encode(o) for o in obj) - - logger.info("Encoding dataset...") - train_dataset = load_rocstories_dataset(args.train_dataset) - eval_dataset = load_rocstories_dataset(args.eval_dataset) - datasets = (train_dataset, eval_dataset) - encoded_datasets = tokenize_and_encode(datasets) - - # Compute the max input length for the Transformer - max_length = model.config.n_positions // 2 - 2 - input_length = max( - len(story[:max_length]) + max(len(cont1[:max_length]), len(cont2[:max_length])) + 3 - for dataset in encoded_datasets - for story, cont1, cont2, _ in dataset - ) - input_length = min(input_length, model.config.n_positions) # Max size of input for the pre-trained model - - # Prepare inputs tensors and dataloaders - tensor_datasets = pre_process_datasets(encoded_datasets, input_length, max_length, *special_tokens_ids) - train_tensor_dataset, eval_tensor_dataset = tensor_datasets[0], tensor_datasets[1] - - train_data = TensorDataset(*train_tensor_dataset) - train_sampler = RandomSampler(train_data) - train_dataloader = DataLoader(train_data, sampler=train_sampler, batch_size=args.train_batch_size) - - eval_data = TensorDataset(*eval_tensor_dataset) - eval_sampler = SequentialSampler(eval_data) - eval_dataloader = DataLoader(eval_data, sampler=eval_sampler, batch_size=args.eval_batch_size) - - # Prepare optimizer - if args.do_train: - if args.max_steps > 0: - t_total = args.max_steps - args.num_train_epochs = args.max_steps // (len(train_dataloader) // args.gradient_accumulation_steps) + 1 - else: - t_total = len(train_dataloader) // args.gradient_accumulation_steps * args.num_train_epochs - - param_optimizer = list(model.named_parameters()) - no_decay = ["bias", "LayerNorm.bias", "LayerNorm.weight"] - optimizer_grouped_parameters = [ - { - "params": [p for n, p in param_optimizer if not any(nd in n for nd in no_decay)], - "weight_decay": args.weight_decay, - }, - {"params": [p for n, p in param_optimizer if any(nd in n for nd in no_decay)], "weight_decay": 0.0}, - ] - optimizer = AdamW(optimizer_grouped_parameters, lr=args.learning_rate, eps=args.adam_epsilon) - scheduler = get_linear_schedule_with_warmup( - optimizer, num_warmup_steps=args.warmup_steps, num_training_steps=t_total - ) - - if args.do_train: - nb_tr_steps, tr_loss, exp_average_loss = 0, 0, None - model.train() - for _ in trange(int(args.num_train_epochs), desc="Epoch"): - tr_loss = 0 - nb_tr_steps = 0 - tqdm_bar = tqdm(train_dataloader, desc="Training") - for step, batch in enumerate(tqdm_bar): - batch = tuple(t.to(device) for t in batch) - input_ids, mc_token_ids, lm_labels, mc_labels = batch - losses = model(input_ids, mc_token_ids=mc_token_ids, lm_labels=lm_labels, mc_labels=mc_labels) - loss = args.lm_coef * losses[0] + losses[1] - loss.backward() - optimizer.step() - scheduler.step() - optimizer.zero_grad() - tr_loss += loss.item() - exp_average_loss = ( - loss.item() if exp_average_loss is None else 0.7 * exp_average_loss + 0.3 * loss.item() - ) - nb_tr_steps += 1 - tqdm_bar.desc = "Training loss: {:.2e} lr: {:.2e}".format(exp_average_loss, scheduler.get_lr()[0]) - - # Save a trained model - if args.do_train: - # Save a trained model, configuration and tokenizer - model_to_save = model.module if hasattr(model, "module") else model # Only save the model itself - - # If we save using the predefined names, we can load using `from_pretrained` - output_model_file = os.path.join(args.output_dir, WEIGHTS_NAME) - output_config_file = os.path.join(args.output_dir, CONFIG_NAME) - - torch.save(model_to_save.state_dict(), output_model_file) - model_to_save.config.to_json_file(output_config_file) - tokenizer.save_vocabulary(args.output_dir) - - # Load a trained model and vocabulary that you have fine-tuned - model = OpenAIGPTDoubleHeadsModel.from_pretrained(args.output_dir) - tokenizer = OpenAIGPTTokenizer.from_pretrained(args.output_dir) - model.to(device) - - if args.do_eval: - model.eval() - eval_loss, eval_accuracy = 0, 0 - nb_eval_steps, nb_eval_examples = 0, 0 - for batch in tqdm(eval_dataloader, desc="Evaluating"): - batch = tuple(t.to(device) for t in batch) - input_ids, mc_token_ids, lm_labels, mc_labels = batch - with torch.no_grad(): - _, mc_loss, _, mc_logits = model( - input_ids, mc_token_ids=mc_token_ids, lm_labels=lm_labels, mc_labels=mc_labels - ) - - mc_logits = mc_logits.detach().cpu().numpy() - mc_labels = mc_labels.to("cpu").numpy() - tmp_eval_accuracy = accuracy(mc_logits, mc_labels) - - eval_loss += mc_loss.mean().item() - eval_accuracy += tmp_eval_accuracy - - nb_eval_examples += input_ids.size(0) - nb_eval_steps += 1 - - eval_loss = eval_loss / nb_eval_steps - eval_accuracy = eval_accuracy / nb_eval_examples - train_loss = tr_loss / nb_tr_steps if args.do_train else None - result = {"eval_loss": eval_loss, "eval_accuracy": eval_accuracy, "train_loss": train_loss} - - output_eval_file = os.path.join(args.output_dir, "eval_results.txt") - with open(output_eval_file, "w") as writer: - logger.info("***** Eval results *****") - for key in sorted(result.keys()): - logger.info(" %s = %s", key, str(result[key])) - writer.write("%s = %s\n" % (key, str(result[key]))) - - -if __name__ == "__main__": - main() diff --git a/examples/pytorch/huggingface_models/examples/legacy/run_swag.py b/examples/pytorch/huggingface_models/examples/legacy/run_swag.py deleted file mode 100755 index ddce4d20e25..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/run_swag.py +++ /dev/null @@ -1,720 +0,0 @@ -#!/usr/bin/env python -# coding=utf-8 -# Copyright 2018 The Google AI Language Team Authors and The HuggingFace Inc. team. -# Copyright (c) 2018, NVIDIA CORPORATION. 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. -"""BERT finetuning runner. - Finetuning the library models for multiple choice on SWAG (Bert). -""" - - -import argparse -import csv -import glob -import logging -import os -import random - -import numpy as np -import torch -from torch.utils.data import DataLoader, RandomSampler, SequentialSampler, TensorDataset -from torch.utils.data.distributed import DistributedSampler -from tqdm import tqdm, trange - -import transformers -from transformers import ( - WEIGHTS_NAME, - AdamW, - AutoConfig, - AutoModelForMultipleChoice, - AutoTokenizer, - get_linear_schedule_with_warmup, -) -from transformers.trainer_utils import is_main_process - - -try: - from torch.utils.tensorboard import SummaryWriter -except ImportError: - from tensorboardX import SummaryWriter - - -logger = logging.getLogger(__name__) - - -class SwagExample(object): - """A single training/test example for the SWAG dataset.""" - - def __init__(self, swag_id, context_sentence, start_ending, ending_0, ending_1, ending_2, ending_3, label=None): - self.swag_id = swag_id - self.context_sentence = context_sentence - self.start_ending = start_ending - self.endings = [ - ending_0, - ending_1, - ending_2, - ending_3, - ] - self.label = label - - def __str__(self): - return self.__repr__() - - def __repr__(self): - attributes = [ - "swag_id: {}".format(self.swag_id), - "context_sentence: {}".format(self.context_sentence), - "start_ending: {}".format(self.start_ending), - "ending_0: {}".format(self.endings[0]), - "ending_1: {}".format(self.endings[1]), - "ending_2: {}".format(self.endings[2]), - "ending_3: {}".format(self.endings[3]), - ] - - if self.label is not None: - attributes.append("label: {}".format(self.label)) - - return ", ".join(attributes) - - -class InputFeatures(object): - def __init__(self, example_id, choices_features, label): - self.example_id = example_id - self.choices_features = [ - {"input_ids": input_ids, "input_mask": input_mask, "segment_ids": segment_ids} - for _, input_ids, input_mask, segment_ids in choices_features - ] - self.label = label - - -def read_swag_examples(input_file, is_training=True): - with open(input_file, "r", encoding="utf-8") as f: - lines = list(csv.reader(f)) - - if is_training and lines[0][-1] != "label": - raise ValueError("For training, the input file must contain a label column.") - - examples = [ - SwagExample( - swag_id=line[2], - context_sentence=line[4], - start_ending=line[5], # in the swag dataset, the - # common beginning of each - # choice is stored in "sent2". - ending_0=line[7], - ending_1=line[8], - ending_2=line[9], - ending_3=line[10], - label=int(line[11]) if is_training else None, - ) - for line in lines[1:] # we skip the line with the column names - ] - - return examples - - -def convert_examples_to_features(examples, tokenizer, max_seq_length, is_training): - """Loads a data file into a list of `InputBatch`s.""" - - # Swag is a multiple choice task. To perform this task using Bert, - # we will use the formatting proposed in "Improving Language - # Understanding by Generative Pre-Training" and suggested by - # @jacobdevlin-google in this issue - # https://github.com/google-research/bert/issues/38. - # - # Each choice will correspond to a sample on which we run the - # inference. For a given Swag example, we will create the 4 - # following inputs: - # - [CLS] context [SEP] choice_1 [SEP] - # - [CLS] context [SEP] choice_2 [SEP] - # - [CLS] context [SEP] choice_3 [SEP] - # - [CLS] context [SEP] choice_4 [SEP] - # The model will output a single value for each input. To get the - # final decision of the model, we will run a softmax over these 4 - # outputs. - features = [] - for example_index, example in tqdm(enumerate(examples)): - context_tokens = tokenizer.tokenize(example.context_sentence) - start_ending_tokens = tokenizer.tokenize(example.start_ending) - - choices_features = [] - for ending_index, ending in enumerate(example.endings): - # We create a copy of the context tokens in order to be - # able to shrink it according to ending_tokens - context_tokens_choice = context_tokens[:] - ending_tokens = start_ending_tokens + tokenizer.tokenize(ending) - # Modifies `context_tokens_choice` and `ending_tokens` in - # place so that the total length is less than the - # specified length. Account for [CLS], [SEP], [SEP] with - # "- 3" - _truncate_seq_pair(context_tokens_choice, ending_tokens, max_seq_length - 3) - - tokens = ["[CLS]"] + context_tokens_choice + ["[SEP]"] + ending_tokens + ["[SEP]"] - segment_ids = [0] * (len(context_tokens_choice) + 2) + [1] * (len(ending_tokens) + 1) - - input_ids = tokenizer.convert_tokens_to_ids(tokens) - input_mask = [1] * len(input_ids) - - # Zero-pad up to the sequence length. - padding = [0] * (max_seq_length - len(input_ids)) - input_ids += padding - input_mask += padding - segment_ids += padding - - assert len(input_ids) == max_seq_length - assert len(input_mask) == max_seq_length - assert len(segment_ids) == max_seq_length - - choices_features.append((tokens, input_ids, input_mask, segment_ids)) - - label = example.label - if example_index < 5: - logger.info("*** Example ***") - logger.info("swag_id: {}".format(example.swag_id)) - for choice_idx, (tokens, input_ids, input_mask, segment_ids) in enumerate(choices_features): - logger.info("choice: {}".format(choice_idx)) - logger.info("tokens: {}".format(" ".join(tokens))) - logger.info("input_ids: {}".format(" ".join(map(str, input_ids)))) - logger.info("input_mask: {}".format(" ".join(map(str, input_mask)))) - logger.info("segment_ids: {}".format(" ".join(map(str, segment_ids)))) - if is_training: - logger.info("label: {}".format(label)) - - features.append(InputFeatures(example_id=example.swag_id, choices_features=choices_features, label=label)) - - return features - - -def _truncate_seq_pair(tokens_a, tokens_b, max_length): - """Truncates a sequence pair in place to the maximum length.""" - - # This is a simple heuristic which will always truncate the longer sequence - # one token at a time. This makes more sense than truncating an equal percent - # of tokens from each, since if one sequence is very short then each token - # that's truncated likely contains more information than a longer sequence. - while True: - total_length = len(tokens_a) + len(tokens_b) - if total_length <= max_length: - break - if len(tokens_a) > len(tokens_b): - tokens_a.pop() - else: - tokens_b.pop() - - -def accuracy(out, labels): - outputs = np.argmax(out, axis=1) - return np.sum(outputs == labels) - - -def select_field(features, field): - return [[choice[field] for choice in feature.choices_features] for feature in features] - - -def set_seed(args): - random.seed(args.seed) - np.random.seed(args.seed) - torch.manual_seed(args.seed) - if args.n_gpu > 0: - torch.cuda.manual_seed_all(args.seed) - - -def load_and_cache_examples(args, tokenizer, evaluate=False, output_examples=False): - if args.local_rank not in [-1, 0]: - torch.distributed.barrier() # Make sure only the first process in distributed training process the dataset, and the others will use the cache - - # Load data features from cache or dataset file - input_file = args.predict_file if evaluate else args.train_file - cached_features_file = os.path.join( - os.path.dirname(input_file), - "cached_{}_{}_{}".format( - "dev" if evaluate else "train", - list(filter(None, args.model_name_or_path.split("/"))).pop(), - str(args.max_seq_length), - ), - ) - if os.path.exists(cached_features_file) and not args.overwrite_cache and not output_examples: - logger.info("Loading features from cached file %s", cached_features_file) - features = torch.load(cached_features_file) - else: - logger.info("Creating features from dataset file at %s", input_file) - examples = read_swag_examples(input_file) - features = convert_examples_to_features(examples, tokenizer, args.max_seq_length, not evaluate) - - if args.local_rank in [-1, 0]: - logger.info("Saving features into cached file %s", cached_features_file) - torch.save(features, cached_features_file) - - if args.local_rank == 0: - torch.distributed.barrier() # Make sure only the first process in distributed training process the dataset, and the others will use the cache - - # Convert to Tensors and build dataset - all_input_ids = torch.tensor(select_field(features, "input_ids"), dtype=torch.long) - all_input_mask = torch.tensor(select_field(features, "input_mask"), dtype=torch.long) - all_segment_ids = torch.tensor(select_field(features, "segment_ids"), dtype=torch.long) - all_label = torch.tensor([f.label for f in features], dtype=torch.long) - - if evaluate: - dataset = TensorDataset(all_input_ids, all_input_mask, all_segment_ids, all_label) - else: - dataset = TensorDataset(all_input_ids, all_input_mask, all_segment_ids, all_label) - - if output_examples: - return dataset, examples, features - return dataset - - -def train(args, train_dataset, model, tokenizer): - """ Train the model """ - if args.local_rank in [-1, 0]: - tb_writer = SummaryWriter() - - args.train_batch_size = args.per_gpu_train_batch_size * max(1, args.n_gpu) - train_sampler = RandomSampler(train_dataset) if args.local_rank == -1 else DistributedSampler(train_dataset) - train_dataloader = DataLoader(train_dataset, sampler=train_sampler, batch_size=args.train_batch_size) - - if args.max_steps > 0: - t_total = args.max_steps - args.num_train_epochs = args.max_steps // (len(train_dataloader) // args.gradient_accumulation_steps) + 1 - else: - t_total = len(train_dataloader) // args.gradient_accumulation_steps * args.num_train_epochs - - # Prepare optimizer and schedule (linear warmup and decay) - no_decay = ["bias", "LayerNorm.weight"] - optimizer_grouped_parameters = [ - { - "params": [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay)], - "weight_decay": args.weight_decay, - }, - {"params": [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay)], "weight_decay": 0.0}, - ] - optimizer = AdamW(optimizer_grouped_parameters, lr=args.learning_rate, eps=args.adam_epsilon) - scheduler = get_linear_schedule_with_warmup( - optimizer, num_warmup_steps=args.warmup_steps, num_training_steps=t_total - ) - if args.fp16: - try: - from apex import amp - except ImportError: - raise ImportError("Please install apex from https://www.github.com/nvidia/apex to use fp16 training.") - model, optimizer = amp.initialize(model, optimizer, opt_level=args.fp16_opt_level) - - # multi-gpu training (should be after apex fp16 initialization) - if args.n_gpu > 1: - model = torch.nn.DataParallel(model) - - # Distributed training (should be after apex fp16 initialization) - if args.local_rank != -1: - model = torch.nn.parallel.DistributedDataParallel( - model, device_ids=[args.local_rank], output_device=args.local_rank, find_unused_parameters=True - ) - - # Train! - logger.info("***** Running training *****") - logger.info(" Num examples = %d", len(train_dataset)) - logger.info(" Num Epochs = %d", args.num_train_epochs) - logger.info(" Instantaneous batch size per GPU = %d", args.per_gpu_train_batch_size) - logger.info( - " Total train batch size (w. parallel, distributed & accumulation) = %d", - args.train_batch_size - * args.gradient_accumulation_steps - * (torch.distributed.get_world_size() if args.local_rank != -1 else 1), - ) - logger.info(" Gradient Accumulation steps = %d", args.gradient_accumulation_steps) - logger.info(" Total optimization steps = %d", t_total) - - global_step = 0 - tr_loss, logging_loss = 0.0, 0.0 - model.zero_grad() - train_iterator = trange(int(args.num_train_epochs), desc="Epoch", disable=args.local_rank not in [-1, 0]) - set_seed(args) # Added here for reproductibility - for _ in train_iterator: - epoch_iterator = tqdm(train_dataloader, desc="Iteration", disable=args.local_rank not in [-1, 0]) - for step, batch in enumerate(epoch_iterator): - model.train() - batch = tuple(t.to(args.device) for t in batch) - inputs = { - "input_ids": batch[0], - "attention_mask": batch[1], - # 'token_type_ids': None if args.model_type == 'xlm' else batch[2], - "token_type_ids": batch[2], - "labels": batch[3], - } - # if args.model_type in ['xlnet', 'xlm']: - # inputs.update({'cls_index': batch[5], - # 'p_mask': batch[6]}) - outputs = model(**inputs) - loss = outputs[0] # model outputs are always tuple in transformers (see doc) - - if args.n_gpu > 1: - loss = loss.mean() # mean() to average on multi-gpu parallel (not distributed) training - if args.gradient_accumulation_steps > 1: - loss = loss / args.gradient_accumulation_steps - - if args.fp16: - with amp.scale_loss(loss, optimizer) as scaled_loss: - scaled_loss.backward() - torch.nn.utils.clip_grad_norm_(amp.master_params(optimizer), args.max_grad_norm) - else: - loss.backward() - torch.nn.utils.clip_grad_norm_(model.parameters(), args.max_grad_norm) - - tr_loss += loss.item() - if (step + 1) % args.gradient_accumulation_steps == 0: - optimizer.step() - scheduler.step() # Update learning rate schedule - model.zero_grad() - global_step += 1 - - if args.local_rank in [-1, 0] and args.logging_steps > 0 and global_step % args.logging_steps == 0: - # Log metrics - if ( - args.local_rank == -1 and args.evaluate_during_training - ): # Only evaluate when single GPU otherwise metrics may not average well - results = evaluate(args, model, tokenizer) - for key, value in results.items(): - tb_writer.add_scalar("eval_{}".format(key), value, global_step) - tb_writer.add_scalar("lr", scheduler.get_lr()[0], global_step) - tb_writer.add_scalar("loss", (tr_loss - logging_loss) / args.logging_steps, global_step) - logging_loss = tr_loss - - if args.local_rank in [-1, 0] and args.save_steps > 0 and global_step % args.save_steps == 0: - # Save model checkpoint - output_dir = os.path.join(args.output_dir, "checkpoint-{}".format(global_step)) - model_to_save = ( - model.module if hasattr(model, "module") else model - ) # Take care of distributed/parallel training - model_to_save.save_pretrained(output_dir) - tokenizer.save_vocabulary(output_dir) - torch.save(args, os.path.join(output_dir, "training_args.bin")) - logger.info("Saving model checkpoint to %s", output_dir) - - if args.max_steps > 0 and global_step > args.max_steps: - epoch_iterator.close() - break - if args.max_steps > 0 and global_step > args.max_steps: - train_iterator.close() - break - - if args.local_rank in [-1, 0]: - tb_writer.close() - - return global_step, tr_loss / global_step - - -def evaluate(args, model, tokenizer, prefix=""): - dataset, examples, features = load_and_cache_examples(args, tokenizer, evaluate=True, output_examples=True) - - if not os.path.exists(args.output_dir) and args.local_rank in [-1, 0]: - os.makedirs(args.output_dir) - - args.eval_batch_size = args.per_gpu_eval_batch_size * max(1, args.n_gpu) - # Note that DistributedSampler samples randomly - eval_sampler = SequentialSampler(dataset) if args.local_rank == -1 else DistributedSampler(dataset) - eval_dataloader = DataLoader(dataset, sampler=eval_sampler, batch_size=args.eval_batch_size) - - # Eval! - logger.info("***** Running evaluation {} *****".format(prefix)) - logger.info(" Num examples = %d", len(dataset)) - logger.info(" Batch size = %d", args.eval_batch_size) - - eval_loss, eval_accuracy = 0, 0 - nb_eval_steps, nb_eval_examples = 0, 0 - - for batch in tqdm(eval_dataloader, desc="Evaluating"): - model.eval() - batch = tuple(t.to(args.device) for t in batch) - with torch.no_grad(): - inputs = { - "input_ids": batch[0], - "attention_mask": batch[1], - # 'token_type_ids': None if args.model_type == 'xlm' else batch[2] # XLM don't use segment_ids - "token_type_ids": batch[2], - "labels": batch[3], - } - - # if args.model_type in ['xlnet', 'xlm']: - # inputs.update({'cls_index': batch[4], - # 'p_mask': batch[5]}) - outputs = model(**inputs) - tmp_eval_loss, logits = outputs[:2] - eval_loss += tmp_eval_loss.mean().item() - - logits = logits.detach().cpu().numpy() - label_ids = inputs["labels"].to("cpu").numpy() - tmp_eval_accuracy = accuracy(logits, label_ids) - eval_accuracy += tmp_eval_accuracy - - nb_eval_steps += 1 - nb_eval_examples += inputs["input_ids"].size(0) - - eval_loss = eval_loss / nb_eval_steps - eval_accuracy = eval_accuracy / nb_eval_examples - result = {"eval_loss": eval_loss, "eval_accuracy": eval_accuracy} - - output_eval_file = os.path.join(args.output_dir, "eval_results.txt") - with open(output_eval_file, "w") as writer: - logger.info("***** Eval results *****") - for key in sorted(result.keys()): - logger.info("%s = %s", key, str(result[key])) - writer.write("%s = %s\n" % (key, str(result[key]))) - - return result - - -def main(): - parser = argparse.ArgumentParser() - - # Required parameters - parser.add_argument( - "--train_file", default=None, type=str, required=True, help="SWAG csv for training. E.g., train.csv" - ) - parser.add_argument( - "--predict_file", - default=None, - type=str, - required=True, - help="SWAG csv for predictions. E.g., val.csv or test.csv", - ) - parser.add_argument( - "--model_name_or_path", - default=None, - type=str, - required=True, - help="Path to pretrained model or model identifier from huggingface.co/models", - ) - parser.add_argument( - "--output_dir", - default=None, - type=str, - required=True, - help="The output directory where the model checkpoints and predictions will be written.", - ) - - # Other parameters - parser.add_argument( - "--config_name", default="", type=str, help="Pretrained config name or path if not the same as model_name" - ) - parser.add_argument( - "--tokenizer_name", - default="", - type=str, - help="Pretrained tokenizer name or path if not the same as model_name", - ) - parser.add_argument( - "--max_seq_length", - default=384, - type=int, - help="The maximum total input sequence length after tokenization. Sequences " - "longer than this will be truncated, and sequences shorter than this will be padded.", - ) - parser.add_argument("--do_train", action="store_true", help="Whether to run training.") - parser.add_argument("--do_eval", action="store_true", help="Whether to run eval on the dev set.") - parser.add_argument( - "--evaluate_during_training", action="store_true", help="Rul evaluation during training at each logging step." - ) - - parser.add_argument("--per_gpu_train_batch_size", default=8, type=int, help="Batch size per GPU/CPU for training.") - parser.add_argument( - "--per_gpu_eval_batch_size", default=8, type=int, help="Batch size per GPU/CPU for evaluation." - ) - parser.add_argument("--learning_rate", default=5e-5, type=float, help="The initial learning rate for Adam.") - parser.add_argument( - "--gradient_accumulation_steps", - type=int, - default=1, - help="Number of updates steps to accumulate before performing a backward/update pass.", - ) - parser.add_argument("--weight_decay", default=0.0, type=float, help="Weight deay if we apply some.") - parser.add_argument("--adam_epsilon", default=1e-8, type=float, help="Epsilon for Adam optimizer.") - parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.") - parser.add_argument( - "--num_train_epochs", default=3.0, type=float, help="Total number of training epochs to perform." - ) - parser.add_argument( - "--max_steps", - default=-1, - type=int, - help="If > 0: set total number of training steps to perform. Override num_train_epochs.", - ) - parser.add_argument("--warmup_steps", default=0, type=int, help="Linear warmup over warmup_steps.") - - parser.add_argument("--logging_steps", type=int, default=50, help="Log every X updates steps.") - parser.add_argument("--save_steps", type=int, default=50, help="Save checkpoint every X updates steps.") - parser.add_argument( - "--eval_all_checkpoints", - action="store_true", - help="Evaluate all checkpoints starting with the same prefix as model_name ending and ending with step number", - ) - parser.add_argument("--no_cuda", action="store_true", help="Whether not to use CUDA when available") - parser.add_argument( - "--overwrite_output_dir", action="store_true", help="Overwrite the content of the output directory" - ) - parser.add_argument( - "--overwrite_cache", action="store_true", help="Overwrite the cached training and evaluation sets" - ) - parser.add_argument("--seed", type=int, default=42, help="random seed for initialization") - - parser.add_argument("--local_rank", type=int, default=-1, help="local_rank for distributed training on gpus") - parser.add_argument( - "--fp16", - action="store_true", - help="Whether to use 16-bit (mixed) precision (through NVIDIA apex) instead of 32-bit", - ) - parser.add_argument( - "--fp16_opt_level", - type=str, - default="O1", - help="For fp16: Apex AMP optimization level selected in ['O0', 'O1', 'O2', and 'O3']." - "See details at https://nvidia.github.io/apex/amp.html", - ) - parser.add_argument("--server_ip", type=str, default="", help="Can be used for distant debugging.") - parser.add_argument("--server_port", type=str, default="", help="Can be used for distant debugging.") - args = parser.parse_args() - - if ( - os.path.exists(args.output_dir) - and os.listdir(args.output_dir) - and args.do_train - and not args.overwrite_output_dir - ): - raise ValueError( - "Output directory ({}) already exists and is not empty. Use --overwrite_output_dir to overcome.".format( - args.output_dir - ) - ) - - # Setup distant debugging if needed - if args.server_ip and args.server_port: - # Distant debugging - see https://code.visualstudio.com/docs/python/debugging#_attach-to-a-local-script - import ptvsd - - print("Waiting for debugger attach") - ptvsd.enable_attach(address=(args.server_ip, args.server_port), redirect_output=True) - ptvsd.wait_for_attach() - - # Setup CUDA, GPU & distributed training - if args.local_rank == -1 or args.no_cuda: - device = torch.device("cuda" if torch.cuda.is_available() and not args.no_cuda else "cpu") - args.n_gpu = 0 if args.no_cuda else torch.cuda.device_count() - else: # Initializes the distributed backend which will take care of sychronizing nodes/GPUs - torch.cuda.set_device(args.local_rank) - device = torch.device("cuda", args.local_rank) - torch.distributed.init_process_group(backend="nccl") - args.n_gpu = 1 - args.device = device - - # Setup logging - logging.basicConfig( - format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", - datefmt="%m/%d/%Y %H:%M:%S", - level=logging.INFO if args.local_rank in [-1, 0] else logging.WARN, - ) - logger.warning( - "Process rank: %s, device: %s, n_gpu: %s, distributed training: %s, 16-bits training: %s", - args.local_rank, - device, - args.n_gpu, - bool(args.local_rank != -1), - args.fp16, - ) - # Set the verbosity to info of the Transformers logger (on main process only): - if is_main_process(args.local_rank): - transformers.utils.logging.set_verbosity_info() - transformers.utils.logging.enable_default_handler() - transformers.utils.logging.enable_explicit_format() - - # Set seed - set_seed(args) - - # Load pretrained model and tokenizer - if args.local_rank not in [-1, 0]: - torch.distributed.barrier() # Make sure only the first process in distributed training will download model & vocab - - config = AutoConfig.from_pretrained(args.config_name if args.config_name else args.model_name_or_path) - tokenizer = AutoTokenizer.from_pretrained( - args.tokenizer_name if args.tokenizer_name else args.model_name_or_path, - ) - model = AutoModelForMultipleChoice.from_pretrained( - args.model_name_or_path, from_tf=bool(".ckpt" in args.model_name_or_path), config=config - ) - - if args.local_rank == 0: - torch.distributed.barrier() # Make sure only the first process in distributed training will download model & vocab - - model.to(args.device) - - logger.info("Training/evaluation parameters %s", args) - - # Training - if args.do_train: - train_dataset = load_and_cache_examples(args, tokenizer, evaluate=False, output_examples=False) - global_step, tr_loss = train(args, train_dataset, model, tokenizer) - logger.info(" global_step = %s, average loss = %s", global_step, tr_loss) - - # Save the trained model and the tokenizer - if args.local_rank == -1 or torch.distributed.get_rank() == 0: - logger.info("Saving model checkpoint to %s", args.output_dir) - # Save a trained model, configuration and tokenizer using `save_pretrained()`. - # They can then be reloaded using `from_pretrained()` - model_to_save = ( - model.module if hasattr(model, "module") else model - ) # Take care of distributed/parallel training - model_to_save.save_pretrained(args.output_dir) - tokenizer.save_pretrained(args.output_dir) - - # Good practice: save your training arguments together with the trained model - torch.save(args, os.path.join(args.output_dir, "training_args.bin")) - - # Load a trained model and vocabulary that you have fine-tuned - model = AutoModelForMultipleChoice.from_pretrained(args.output_dir) - tokenizer = AutoTokenizer.from_pretrained(args.output_dir) - model.to(args.device) - - # Evaluation - we can ask to evaluate all the checkpoints (sub-directories) in a directory - results = {} - if args.do_eval and args.local_rank in [-1, 0]: - if args.do_train: - checkpoints = [args.output_dir] - else: - # if do_train is False and do_eval is true, load model directly from pretrained. - checkpoints = [args.model_name_or_path] - - if args.eval_all_checkpoints: - checkpoints = list( - os.path.dirname(c) for c in sorted(glob.glob(args.output_dir + "/**/" + WEIGHTS_NAME, recursive=True)) - ) - - logger.info("Evaluate the following checkpoints: %s", checkpoints) - - for checkpoint in checkpoints: - # Reload the model - global_step = checkpoint.split("-")[-1] if len(checkpoints) > 1 else "" - model = AutoModelForMultipleChoice.from_pretrained(checkpoint) - tokenizer = AutoTokenizer.from_pretrained(checkpoint) - model.to(args.device) - - # Evaluate - result = evaluate(args, model, tokenizer, prefix=global_step) - - result = dict((k + ("_{}".format(global_step) if global_step else ""), v) for k, v in result.items()) - results.update(result) - - logger.info("Results: {}".format(results)) - - return results - - -if __name__ == "__main__": - main() diff --git a/examples/pytorch/huggingface_models/examples/legacy/run_transfo_xl.py b/examples/pytorch/huggingface_models/examples/legacy/run_transfo_xl.py deleted file mode 100755 index 71f3efa2a88..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/run_transfo_xl.py +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/env python -# coding=utf-8 -# Copyright 2018 Google AI, Google Brain and Carnegie Mellon University Authors and the HuggingFace Inc. team. -# Copyright (c) 2018, NVIDIA CORPORATION. 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. -""" PyTorch Transformer XL model evaluation script. - Adapted from https://github.com/kimiyoung/transformer-xl. - In particular https://github.com/kimiyoung/transformer-xl/blob/master/pytorch/eval.py - - This script with default values evaluates a pretrained Transformer-XL on WikiText 103 -""" - - -import argparse -import logging -import math -import time - -import torch - -from transformers import TransfoXLCorpus, TransfoXLLMHeadModel - - -logging.basicConfig( - format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", datefmt="%m/%d/%Y %H:%M:%S", level=logging.INFO -) -logger = logging.getLogger(__name__) - - -def main(): - parser = argparse.ArgumentParser(description="PyTorch Transformer Language Model") - parser.add_argument("--model_name", type=str, default="transfo-xl-wt103", help="pretrained model name") - parser.add_argument( - "--split", type=str, default="test", choices=["all", "valid", "test"], help="which split to evaluate" - ) - parser.add_argument("--batch_size", type=int, default=10, help="batch size") - parser.add_argument("--tgt_len", type=int, default=128, help="number of tokens to predict") - parser.add_argument("--ext_len", type=int, default=0, help="length of the extended context") - parser.add_argument("--mem_len", type=int, default=1600, help="length of the retained previous heads") - parser.add_argument("--clamp_len", type=int, default=1000, help="max positional embedding index") - parser.add_argument("--no_cuda", action="store_true", help="Do not use CUDA even though CUA is available") - parser.add_argument("--work_dir", type=str, required=True, help="path to the work_dir") - parser.add_argument("--no_log", action="store_true", help="do not log the eval result") - parser.add_argument("--same_length", action="store_true", help="set same length attention with masking") - parser.add_argument("--server_ip", type=str, default="", help="Can be used for distant debugging.") - parser.add_argument("--server_port", type=str, default="", help="Can be used for distant debugging.") - args = parser.parse_args() - assert args.ext_len >= 0, "extended context length must be non-negative" - - if args.server_ip and args.server_port: - # Distant debugging - see https://code.visualstudio.com/docs/python/debugging#_attach-to-a-local-script - import ptvsd - - print("Waiting for debugger attach") - ptvsd.enable_attach(address=(args.server_ip, args.server_port), redirect_output=True) - ptvsd.wait_for_attach() - - device = torch.device("cuda" if torch.cuda.is_available() and not args.no_cuda else "cpu") - logger.info("device: {}".format(device)) - - # Load a pre-processed dataset - # You can also build the corpus yourself using TransfoXLCorpus methods - # The pre-processing involve computing word frequencies to prepare the Adaptive input and SoftMax - # and tokenizing the dataset - # The pre-processed corpus is a convertion (using the conversion script ) - corpus = TransfoXLCorpus.from_pretrained(args.model_name) - - va_iter = corpus.get_iterator("valid", args.batch_size, args.tgt_len, device=device, ext_len=args.ext_len) - te_iter = corpus.get_iterator("test", args.batch_size, args.tgt_len, device=device, ext_len=args.ext_len) - - # Load a pre-trained model - model = TransfoXLLMHeadModel.from_pretrained(args.model_name) - model.to(device) - - logger.info( - "Evaluating with bsz {} tgt_len {} ext_len {} mem_len {} clamp_len {}".format( - args.batch_size, args.tgt_len, args.ext_len, args.mem_len, args.clamp_len - ) - ) - - model.reset_memory_length(args.mem_len) - if args.clamp_len > 0: - model.clamp_len = args.clamp_len - if args.same_length: - model.same_length = True - - ############################################################################### - # Evaluation code - ############################################################################### - def evaluate(eval_iter): - # Turn on evaluation mode which disables dropout. - model.eval() - total_len, total_loss = 0, 0.0 - start_time = time.time() - with torch.no_grad(): - mems = None - for idx, (data, target, seq_len) in enumerate(eval_iter): - ret = model(data, lm_labels=target, mems=mems) - loss, _, mems = ret - loss = loss.mean() - total_loss += seq_len * loss.item() - total_len += seq_len - total_time = time.time() - start_time - logger.info("Time : {:.2f}s, {:.2f}ms/segment".format(total_time, 1000 * total_time / (idx + 1))) - return total_loss / total_len - - # Run on test data. - if args.split == "all": - test_loss = evaluate(te_iter) - valid_loss = evaluate(va_iter) - elif args.split == "valid": - valid_loss = evaluate(va_iter) - test_loss = None - elif args.split == "test": - test_loss = evaluate(te_iter) - valid_loss = None - - def format_log(loss, split): - log_str = "| {0} loss {1:5.2f} | {0} ppl {2:9.3f} ".format(split, loss, math.exp(loss)) - return log_str - - log_str = "" - if valid_loss is not None: - log_str += format_log(valid_loss, "valid") - if test_loss is not None: - log_str += format_log(test_loss, "test") - - logger.info("=" * 100) - logger.info(log_str) - logger.info("=" * 100) - - -if __name__ == "__main__": - main() diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/README.md b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/README.md deleted file mode 100644 index 623b731d0d9..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/README.md +++ /dev/null @@ -1,334 +0,0 @@ - - -# Sequence-to-Sequence Training and Evaluation - -This directory contains examples for finetuning and evaluating transformers on summarization and translation tasks. -For deprecated `bertabs` instructions, see [`bertabs/README.md`](https://github.com/huggingface/transformers/blob/master/examples/research_projects/bertabs/README.md). - -### Supported Architectures - -- `BartForConditionalGeneration` -- `MarianMTModel` -- `PegasusForConditionalGeneration` -- `MBartForConditionalGeneration` -- `FSMTForConditionalGeneration` -- `T5ForConditionalGeneration` - -### Downlowd the Datasets - -#### XSUM - -```bash -cd examples/legacy/seq2seq -wget https://cdn-datasets.huggingface.co/summarization/xsum.tar.gz -tar -xzvf xsum.tar.gz -export XSUM_DIR=${PWD}/xsum -``` -this should make a directory called `xsum/` with files like `test.source`. -To use your own data, copy that files format. Each article to be summarized is on its own line. - -#### CNN/DailyMail - -```bash -cd examples/legacy/seq2seq -wget https://cdn-datasets.huggingface.co/summarization/cnn_dm_v2.tgz -tar -xzvf cnn_dm_v2.tgz # empty lines removed -mv cnn_cln cnn_dm -export CNN_DIR=${PWD}/cnn_dm -``` -this should make a directory called `cnn_dm/` with 6 files. - -#### WMT16 English-Romanian Translation Data - -download with this command: -```bash -wget https://cdn-datasets.huggingface.co/translation/wmt_en_ro.tar.gz -tar -xzvf wmt_en_ro.tar.gz -export ENRO_DIR=${PWD}/wmt_en_ro -``` -this should make a directory called `wmt_en_ro/` with 6 files. - -#### WMT English-German - -```bash -wget https://cdn-datasets.huggingface.co/translation/wmt_en_de.tgz -tar -xzvf wmt_en_de.tgz -export DATA_DIR=${PWD}/wmt_en_de -``` - -#### FSMT datasets (wmt) - -Refer to the scripts starting with `eval_` under: -https://github.com/huggingface/transformers/tree/master/scripts/fsmt - -#### Pegasus (multiple datasets) - -Multiple eval datasets are available for download from: -https://github.com/stas00/porting/tree/master/datasets/pegasus - - -#### Your Data - -If you are using your own data, it must be formatted as one directory with 6 files: -``` -train.source -train.target -val.source -val.target -test.source -test.target -``` -The `.source` files are the input, the `.target` files are the desired output. - -### Potential issues - -- native AMP (`--fp16` and no apex) may lead to a huge memory leak and require 10x gpu memory. This has been fixed in pytorch-nightly and the minimal official version to have this fix will be pytorch-1.7.1. Until then if you have to use mixed precision please use AMP only with pytorch-nightly or NVIDIA's apex. Reference: https://github.com/huggingface/transformers/issues/8403 - - -### Tips and Tricks - -General Tips: -- since you need to run from `examples/legacy/seq2seq`, and likely need to modify code, the easiest workflow is fork transformers, clone your fork, and run `pip install -e .` before you get started. -- try `--freeze_encoder` or `--freeze_embeds` for faster training/larger batch size. (3hr per epoch with bs=8, see the "xsum_shared_task" command below) -- `fp16_opt_level=O1` (the default works best). -- In addition to the pytorch-lightning .ckpt checkpoint, a transformers checkpoint will be saved. -Load it with `BartForConditionalGeneration.from_pretrained(f'{output_dir}/best_tfmr)`. -- At the moment, `--do_predict` does not work in a multi-gpu setting. You need to use `evaluate_checkpoint` or the `run_eval.py` code. -- This warning can be safely ignored: - > "Some weights of BartForConditionalGeneration were not initialized from the model checkpoint at facebook/bart-large-xsum and are newly initialized: ['final_logits_bias']" -- Both finetuning and eval are 30% faster with `--fp16`. For that you need to [install apex](https://github.com/NVIDIA/apex#quick-start). -- Read scripts before you run them! - -Summarization Tips: -- (summ) 1 epoch at batch size 1 for bart-large takes 24 hours and requires 13GB GPU RAM with fp16 on an NVIDIA-V100. -- If you want to run experiments on improving the summarization finetuning process, try the XSUM Shared Task (below). It's faster to train than CNNDM because the summaries are shorter. -- For CNN/DailyMail, the default `val_max_target_length` and `test_max_target_length` will truncate the ground truth labels, resulting in slightly higher rouge scores. To get accurate rouge scores, you should rerun calculate_rouge on the `{output_dir}/test_generations.txt` file saved by `trainer.test()` -- `--max_target_length=60 --val_max_target_length=60 --test_max_target_length=100 ` is a reasonable setting for XSUM. -- `wandb` can be used by specifying `--logger_name wandb`. It is useful for reproducibility. Specify the environment variable `WANDB_PROJECT='hf_xsum'` to do the XSUM shared task. -- If you are finetuning on your own dataset, start from `distilbart-cnn-12-6` if you want long summaries and `distilbart-xsum-12-6` if you want short summaries. -(It rarely makes sense to start from `bart-large` unless you are a researching finetuning methods). - -**Update 2018-07-18** -Datasets: `LegacySeq2SeqDataset` will be used for all tokenizers without a `prepare_seq2seq_batch` method. Otherwise, `Seq2SeqDataset` will be used. -Future work/help wanted: A new dataset to support multilingual tasks. - - -### Fine-tuning using Seq2SeqTrainer -To use `Seq2SeqTrainer` for fine-tuning you should use the `finetune_trainer.py` script. It subclasses `Trainer` to extend it for seq2seq training. Except the `Trainer`-related `TrainingArguments`, it shares the same argument names as that of `finetune.py` file. One notable difference is that calculating generative metrics (BLEU, ROUGE) is optional and is controlled using the `--predict_with_generate` argument. - -With PyTorch 1.6+ it'll automatically use `native AMP` when `--fp16` is set. - -To see all the possible command line options, run: - -```bash -python finetune_trainer.py --help -``` - -For multi-gpu training use `torch.distributed.launch`, e.g. with 2 gpus: -```bash -python -m torch.distributed.launch --nproc_per_node=2 finetune_trainer.py ... -``` - -**At the moment, `Seq2SeqTrainer` does not support *with teacher* distillation.** - -All `Seq2SeqTrainer`-based fine-tuning scripts are included in the `builtin_trainer` directory. - -#### TPU Training -`Seq2SeqTrainer` supports TPU training with few caveats -1. As `generate` method does not work on TPU at the moment, `predict_with_generate` cannot be used. You should use `--prediction_loss_only` to only calculate loss, and do not set `--do_predict` and `--predict_with_generate`. -2. All sequences should be padded to be of equal length to avoid extremely slow training. (`finetune_trainer.py` does this automatically when running on TPU.) - -We provide a very simple launcher script named `xla_spawn.py` that lets you run our example scripts on multiple TPU cores without any boilerplate. Just pass a `--num_cores` flag to this script, then your regular training script with its arguments (this is similar to the `torch.distributed.launch` helper for `torch.distributed`). - -`builtin_trainer/finetune_tpu.sh` script provides minimal arguments needed for TPU training. - -The following command fine-tunes `sshleifer/student_marian_en_ro_6_3` on TPU V3-8 and should complete one epoch in ~5-6 mins. - -```bash -./builtin_trainer/train_distil_marian_enro_tpu.sh -``` - -## Evaluation Commands - -To create summaries for each article in dataset, we use `run_eval.py`, here are a few commands that run eval for different tasks and models. -If 'translation' is in your task name, the computed metric will be BLEU. Otherwise, ROUGE will be used. - -For t5, you need to specify --task translation_{src}_to_{tgt} as follows: -```bash -export DATA_DIR=wmt_en_ro -./run_eval.py t5-base \ - $DATA_DIR/val.source t5_val_generations.txt \ - --reference_path $DATA_DIR/val.target \ - --score_path enro_bleu.json \ - --task translation_en_to_ro \ - --n_obs 100 \ - --device cuda \ - --fp16 \ - --bs 32 -``` - -This command works for MBART, although the BLEU score is suspiciously low. -```bash -export DATA_DIR=wmt_en_ro -./run_eval.py facebook/mbart-large-en-ro $DATA_DIR/val.source mbart_val_generations.txt \ - --reference_path $DATA_DIR/val.target \ - --score_path enro_bleu.json \ - --task translation \ - --n_obs 100 \ - --device cuda \ - --fp16 \ - --bs 32 -``` - -Summarization (xsum will be very similar): -```bash -export DATA_DIR=cnn_dm -./run_eval.py sshleifer/distilbart-cnn-12-6 $DATA_DIR/val.source dbart_val_generations.txt \ - --reference_path $DATA_DIR/val.target \ - --score_path cnn_rouge.json \ - --task summarization \ - --n_obs 100 \ - -th 56 \ - --fp16 \ - --bs 32 -``` - -### Multi-GPU Evaluation -here is a command to run xsum evaluation on 8 GPUS. It is more than linearly faster than run_eval.py in some cases -because it uses SortishSampler to minimize padding. You can also use it on 1 GPU. `data_dir` must have -`{type_path}.source` and `{type_path}.target`. Run `./run_distributed_eval.py --help` for all clargs. - -```bash -python -m torch.distributed.launch --nproc_per_node=8 run_distributed_eval.py \ - --model_name sshleifer/distilbart-large-xsum-12-3 \ - --save_dir xsum_generations \ - --data_dir xsum \ - --fp16 # you can pass generate kwargs like num_beams here, just like run_eval.py -``` - -Contributions that implement this command for other distributed hardware setups are welcome! - -#### Single-GPU Eval: Tips and Tricks - -When using `run_eval.py`, the following features can be useful: - -* if you running the script multiple times and want to make it easier to track what arguments produced that output, use `--dump-args`. Along with the results it will also dump any custom params that were passed to the script. For example if you used: `--num_beams 8 --early_stopping true`, the output will be: - ``` - {'bleu': 26.887, 'n_obs': 10, 'runtime': 1, 'seconds_per_sample': 0.1, 'num_beams': 8, 'early_stopping': True} - ``` - - `--info` is an additional argument available for the same purpose of tracking the conditions of the experiment. It's useful to pass things that weren't in the argument list, e.g. a language pair `--info "lang:en-ru"`. But also if you pass `--info` without a value it will fallback to the current date/time string, e.g. `2020-09-13 18:44:43`. - - If using `--dump-args --info`, the output will be: - - ``` - {'bleu': 26.887, 'n_obs': 10, 'runtime': 1, 'seconds_per_sample': 0.1, 'num_beams': 8, 'early_stopping': True, 'info': '2020-09-13 18:44:43'} - ``` - - If using `--dump-args --info "pair:en-ru chkpt=best`, the output will be: - - ``` - {'bleu': 26.887, 'n_obs': 10, 'runtime': 1, 'seconds_per_sample': 0.1, 'num_beams': 8, 'early_stopping': True, 'info': 'pair=en-ru chkpt=best'} - ``` - - -* if you need to perform a parametric search in order to find the best ones that lead to the highest BLEU score, let `run_eval_search.py` to do the searching for you. - - The script accepts the exact same arguments as `run_eval.py`, plus an additional argument `--search`. The value of `--search` is parsed, reformatted and fed to ``run_eval.py`` as additional args. - - The format for the `--search` value is a simple string with hparams and colon separated values to try, e.g.: - ``` - --search "num_beams=5:10 length_penalty=0.8:1.0:1.2 early_stopping=true:false" - ``` - which will generate `12` `(2*3*2)` searches for a product of each hparam. For example the example that was just used will invoke `run_eval.py` repeatedly with: - - ``` - --num_beams 5 --length_penalty 0.8 --early_stopping true - --num_beams 5 --length_penalty 0.8 --early_stopping false - [...] - --num_beams 10 --length_penalty 1.2 --early_stopping false - ``` - - On completion, this function prints a markdown table of the results sorted by the best BLEU score and the winning arguments. - -``` -bleu | num_beams | length_penalty | early_stopping ------ | --------- | -------------- | -------------- -26.71 | 5 | 1.1 | 1 -26.66 | 5 | 0.9 | 1 -26.66 | 5 | 0.9 | 0 -26.41 | 5 | 1.1 | 0 -21.94 | 1 | 0.9 | 1 -21.94 | 1 | 0.9 | 0 -21.94 | 1 | 1.1 | 1 -21.94 | 1 | 1.1 | 0 - -Best score args: -stas/wmt19-en-ru data/en-ru/val.source data/en-ru/test_translations.txt --reference_path data/en-ru/val.target --score_path data/en-ru/test_bleu.json --bs 8 --task translation --num_beams 5 --length_penalty 1.1 --early_stopping True -``` - -If you pass `--info "some experiment-specific info"` it will get printed before the results table - this is useful for scripting and multiple runs, so one can tell the different sets of results from each other. - - -### Contributing -- follow the standard contributing guidelines and code of conduct. -- add tests to `test_seq2seq_examples.py` -- To run only the seq2seq tests, you must be in the root of the repository and run: -```bash -pytest examples/seq2seq/ -``` - -### Converting pytorch-lightning checkpoints -pytorch lightning ``-do_predict`` often fails, after you are done training, the best way to evaluate your model is to convert it. - -This should be done for you, with a file called `{save_dir}/best_tfmr`. - -If that file doesn't exist but you have a lightning `.ckpt` file, you can run -```bash -python convert_pl_checkpoint_to_hf.py PATH_TO_CKPT randomly_initialized_hf_model_path save_dir/best_tfmr -``` -Then either `run_eval` or `run_distributed_eval` with `save_dir/best_tfmr` (see previous sections) - - -# Experimental Features -These features are harder to use and not always useful. - -### Dynamic Batch Size for MT -`finetune.py` has a command line arg `--max_tokens_per_batch` that allows batches to be dynamically sized. -This feature can only be used: -- with fairseq installed -- on 1 GPU -- without sortish sampler -- after calling `./save_len_file.py $tok $data_dir` - -For example, -```bash -./save_len_file.py Helsinki-NLP/opus-mt-en-ro wmt_en_ro -./dynamic_bs_example.sh --max_tokens_per_batch=2000 --output_dir benchmark_dynamic_bs -``` -splits `wmt_en_ro/train` into 11,197 uneven lengthed batches and can finish 1 epoch in 8 minutes on a v100. - -For comparison, -```bash -./dynamic_bs_example.sh --sortish_sampler --train_batch_size 48 -``` -uses 12,723 batches of length 48 and takes slightly more time 9.5 minutes. - -The feature is still experimental, because: -+ we can make it much more robust if we have memory mapped/preprocessed datasets. -+ The speedup over sortish sampler is not that large at the moment. diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/__init__.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/__init__.py deleted file mode 100644 index 3cee09bb7f5..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -import os -import sys - - -sys.path.insert(1, os.path.dirname(os.path.realpath(__file__))) diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/convert_model_to_fp16.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/convert_model_to_fp16.py deleted file mode 100755 index 7fffbde79df..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/convert_model_to_fp16.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python -# Copyright 2020 The HuggingFace Team. 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 typing import Union - -import fire -import torch -from tqdm import tqdm - - -def convert(src_path: str, map_location: str = "cpu", save_path: Union[str, None] = None) -> None: - """Convert a pytorch_model.bin or model.pt file to torch.float16 for faster downloads, less disk space.""" - state_dict = torch.load(src_path, map_location=map_location) - for k, v in tqdm(state_dict.items()): - if not isinstance(v, torch.Tensor): - raise TypeError("FP16 conversion only works on paths that are saved state dicts, like pytorch_model.bin") - state_dict[k] = v.half() - if save_path is None: # overwrite src_path - save_path = src_path - torch.save(state_dict, save_path) - - -if __name__ == "__main__": - fire.Fire(convert) diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/download_wmt.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/download_wmt.py deleted file mode 100755 index c52c0c7b4fa..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/download_wmt.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python -# Copyright 2020 The HuggingFace Team. 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 pathlib import Path - -import fire -from tqdm import tqdm - - -def download_wmt_dataset(src_lang="ro", tgt_lang="en", dataset="wmt16", save_dir=None) -> None: - """Download a dataset using the datasets package and save it to the format expected by finetune.py - Format of save_dir: train.source, train.target, val.source, val.target, test.source, test.target. - - Args: - src_lang: source language - tgt_lang: target language - dataset: wmt16, wmt17, etc. wmt16 is a good start as it's small. To get the full list run `import datasets; print([d.id for d in datasets.list_datasets() if "wmt" in d.id])` - save_dir: , where to save the datasets, defaults to f'{dataset}-{src_lang}-{tgt_lang}' - - Usage: - >>> download_wmt_dataset('ro', 'en', dataset='wmt16') # saves to wmt16-ro-en - """ - try: - import datasets - except (ModuleNotFoundError, ImportError): - raise ImportError("run pip install datasets") - pair = f"{src_lang}-{tgt_lang}" - print(f"Converting {dataset}-{pair}") - ds = datasets.load_dataset(dataset, pair) - if save_dir is None: - save_dir = f"{dataset}-{pair}" - save_dir = Path(save_dir) - save_dir.mkdir(exist_ok=True) - - for split in ds.keys(): - print(f"Splitting {split} with {ds[split].num_rows} records") - - # to save to val.source, val.target like summary datasets - fn = "val" if split == "validation" else split - src_path = save_dir.joinpath(f"{fn}.source") - tgt_path = save_dir.joinpath(f"{fn}.target") - src_fp = src_path.open("w+") - tgt_fp = tgt_path.open("w+") - - # reader is the bottleneck so writing one record at a time doesn't slow things down - for x in tqdm(ds[split]): - ex = x["translation"] - src_fp.write(ex[src_lang] + "\n") - tgt_fp.write(ex[tgt_lang] + "\n") - - print(f"Saved {dataset} dataset to {save_dir}") - - -if __name__ == "__main__": - fire.Fire(download_wmt_dataset) diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/finetune.sh b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/finetune.sh deleted file mode 100644 index 1f518835d63..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/finetune.sh +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2020 The HuggingFace Team. 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. - -# the proper usage is documented in the README, you need to specify data_dir, output_dir and model_name_or_path -# run ./finetune.sh --help to see all the possible options -python finetune_trainer.py \ - --learning_rate=3e-5 \ - --fp16 \ - --do_train --do_eval --do_predict \ - --evaluation_strategy steps \ - --predict_with_generate \ - --n_val 1000 \ - "$@" diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/finetune_tpu.sh b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/finetune_tpu.sh deleted file mode 100644 index 68cf0d77360..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/finetune_tpu.sh +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2020 The HuggingFace Team. 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. - -export TPU_NUM_CORES=8 - -# the proper usage is documented in the README, you need to specify data_dir, output_dir and model_name_or_path -# run ./finetune_tpu.sh --help to see all the possible options -python xla_spawn.py --num_cores $TPU_NUM_CORES \ - finetune_trainer.py \ - --learning_rate=3e-5 \ - --do_train --do_eval \ - --evaluation_strategy steps \ - --prediction_loss_only \ - --n_val 1000 \ - "$@" diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/finetune_trainer.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/finetune_trainer.py deleted file mode 100755 index 37573e50bad..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/finetune_trainer.py +++ /dev/null @@ -1,367 +0,0 @@ -#!/usr/bin/env python -# Copyright 2020 The HuggingFace Team. 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 -from dataclasses import dataclass, field -from typing import Optional - -import transformers -from seq2seq_trainer import Seq2SeqTrainer -from seq2seq_training_args import Seq2SeqTrainingArguments -from transformers import ( - AutoConfig, - AutoModelForSeq2SeqLM, - AutoTokenizer, - HfArgumentParser, - MBartTokenizer, - MBartTokenizerFast, - set_seed, -) -from transformers.trainer_utils import EvaluationStrategy, is_main_process -from transformers.training_args import ParallelMode -from utils import ( - Seq2SeqDataCollator, - Seq2SeqDataset, - assert_all_frozen, - build_compute_metrics_fn, - check_output_dir, - freeze_embeds, - freeze_params, - lmap, - save_json, - use_task_specific_params, - write_txt_file, -) - - -logger = logging.getLogger(__name__) - - -@dataclass -class ModelArguments: - """ - Arguments pertaining to which model/config/tokenizer we are going to fine-tune from. - """ - - model_name_or_path: str = field( - metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"} - ) - config_name: Optional[str] = field( - default=None, metadata={"help": "Pretrained config name or path if not the same as model_name"} - ) - tokenizer_name: Optional[str] = field( - default=None, metadata={"help": "Pretrained tokenizer name or path if not the same as model_name"} - ) - cache_dir: Optional[str] = field( - default=None, - metadata={"help": "Where do you want to store the pretrained models downloaded from huggingface.co"}, - ) - freeze_encoder: bool = field(default=False, metadata={"help": "Whether tp freeze the encoder."}) - freeze_embeds: bool = field(default=False, metadata={"help": "Whether to freeze the embeddings."}) - - -@dataclass -class DataTrainingArguments: - """ - Arguments pertaining to what data we are going to input our model for training and eval. - """ - - data_dir: str = field( - metadata={"help": "The input data dir. Should contain the .tsv files (or other data files) for the task."} - ) - task: Optional[str] = field( - default="summarization", - metadata={"help": "Task name, summarization (or summarization_{dataset} for pegasus) or translation"}, - ) - max_source_length: Optional[int] = field( - default=1024, - metadata={ - "help": "The maximum total input sequence length after tokenization. Sequences longer " - "than this will be truncated, sequences shorter will be padded." - }, - ) - max_target_length: Optional[int] = field( - default=128, - metadata={ - "help": "The maximum total sequence length for target text after tokenization. Sequences longer " - "than this will be truncated, sequences shorter will be padded." - }, - ) - val_max_target_length: Optional[int] = field( - default=142, - metadata={ - "help": "The maximum total sequence length for validation target text after tokenization. Sequences longer " - "than this will be truncated, sequences shorter will be padded. " - "This argument is also used to override the ``max_length`` param of ``model.generate``, which is used " - "during ``evaluate`` and ``predict``." - }, - ) - test_max_target_length: Optional[int] = field( - default=142, - metadata={ - "help": "The maximum total sequence length for test target text after tokenization. Sequences longer " - "than this will be truncated, sequences shorter will be padded." - }, - ) - n_train: Optional[int] = field(default=-1, metadata={"help": "# training examples. -1 means use all."}) - n_val: Optional[int] = field(default=-1, metadata={"help": "# validation examples. -1 means use all."}) - n_test: Optional[int] = field(default=-1, metadata={"help": "# test examples. -1 means use all."}) - src_lang: Optional[str] = field(default=None, metadata={"help": "Source language id for translation."}) - tgt_lang: Optional[str] = field(default=None, metadata={"help": "Target language id for translation."}) - eval_beams: Optional[int] = field(default=None, metadata={"help": "# num_beams to use for evaluation."}) - ignore_pad_token_for_loss: bool = field( - default=True, - metadata={"help": "If only pad tokens should be ignored. This assumes that `config.pad_token_id` is defined."}, - ) - - -def handle_metrics(split, metrics, output_dir): - """ - Log and save metrics - - Args: - - split: one of train, val, test - - metrics: metrics dict - - output_dir: where to save the metrics - """ - - logger.info(f"***** {split} metrics *****") - for key in sorted(metrics.keys()): - logger.info(f" {key} = {metrics[key]}") - save_json(metrics, os.path.join(output_dir, f"{split}_results.json")) - - -def main(): - # See all possible arguments in src/transformers/training_args.py - # or by passing the --help flag to this script. - # We now keep distinct sets of args, for a cleaner separation of concerns. - - parser = HfArgumentParser((ModelArguments, DataTrainingArguments, Seq2SeqTrainingArguments)) - - if len(sys.argv) == 2 and sys.argv[1].endswith(".json"): - # If we pass only one argument to the script and it's the path to a json file, - # let's parse it to get our arguments. - model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1])) - else: - model_args, data_args, training_args = parser.parse_args_into_dataclasses() - - check_output_dir(training_args) - - # Setup logging - logging.basicConfig( - format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", - datefmt="%m/%d/%Y %H:%M:%S", - level=logging.INFO if training_args.local_rank in [-1, 0] else logging.WARN, - ) - logger.warning( - "Process rank: %s, device: %s, n_gpu: %s, distributed training: %s, 16-bits training: %s", - training_args.local_rank, - training_args.device, - training_args.n_gpu, - bool(training_args.parallel_mode == ParallelMode.DISTRIBUTED), - training_args.fp16, - ) - transformers.utils.logging.enable_default_handler() - transformers.utils.logging.enable_explicit_format() - # Set the verbosity to info of the Transformers logger (on main process only): - if is_main_process(training_args.local_rank): - transformers.utils.logging.set_verbosity_info() - logger.info("Training/evaluation parameters %s", training_args) - - # Set seed - set_seed(training_args.seed) - - # Load pretrained model and tokenizer - # - # Distributed training: - # The .from_pretrained methods guarantee that only one local process can concurrently - # download model & vocab. - - config = AutoConfig.from_pretrained( - model_args.config_name if model_args.config_name else model_args.model_name_or_path, - cache_dir=model_args.cache_dir, - ) - - extra_model_params = ("encoder_layerdrop", "decoder_layerdrop", "dropout", "attention_dropout") - for p in extra_model_params: - if getattr(training_args, p, None): - assert hasattr(config, p), f"({config.__class__.__name__}) doesn't have a `{p}` attribute" - setattr(config, p, getattr(training_args, p)) - - tokenizer = AutoTokenizer.from_pretrained( - model_args.tokenizer_name if model_args.tokenizer_name else model_args.model_name_or_path, - cache_dir=model_args.cache_dir, - ) - model = AutoModelForSeq2SeqLM.from_pretrained( - model_args.model_name_or_path, - from_tf=".ckpt" in model_args.model_name_or_path, - config=config, - cache_dir=model_args.cache_dir, - ) - - # use task specific params - use_task_specific_params(model, data_args.task) - - # set num_beams for evaluation - if data_args.eval_beams is None: - data_args.eval_beams = model.config.num_beams - - # set decoder_start_token_id for MBart - if model.config.decoder_start_token_id is None and isinstance(tokenizer, (MBartTokenizer, MBartTokenizerFast)): - assert ( - data_args.tgt_lang is not None and data_args.src_lang is not None - ), "mBart requires --tgt_lang and --src_lang" - if isinstance(tokenizer, MBartTokenizer): - model.config.decoder_start_token_id = tokenizer.lang_code_to_id[data_args.tgt_lang] - else: - model.config.decoder_start_token_id = tokenizer.convert_tokens_to_ids(data_args.tgt_lang) - - if model_args.freeze_embeds: - freeze_embeds(model) - if model_args.freeze_encoder: - freeze_params(model.get_encoder()) - assert_all_frozen(model.get_encoder()) - - dataset_class = Seq2SeqDataset - - # Get datasets - train_dataset = ( - dataset_class( - tokenizer, - type_path="train", - data_dir=data_args.data_dir, - n_obs=data_args.n_train, - max_target_length=data_args.max_target_length, - max_source_length=data_args.max_source_length, - prefix=model.config.prefix or "", - ) - if training_args.do_train - else None - ) - eval_dataset = ( - dataset_class( - tokenizer, - type_path="val", - data_dir=data_args.data_dir, - n_obs=data_args.n_val, - max_target_length=data_args.val_max_target_length, - max_source_length=data_args.max_source_length, - prefix=model.config.prefix or "", - ) - if training_args.do_eval or training_args.evaluation_strategy != EvaluationStrategy.NO - else None - ) - test_dataset = ( - dataset_class( - tokenizer, - type_path="test", - data_dir=data_args.data_dir, - n_obs=data_args.n_test, - max_target_length=data_args.test_max_target_length, - max_source_length=data_args.max_source_length, - prefix=model.config.prefix or "", - ) - if training_args.do_predict - else None - ) - - # Initialize our Trainer - compute_metrics_fn = ( - build_compute_metrics_fn(data_args.task, tokenizer) if training_args.predict_with_generate else None - ) - trainer = Seq2SeqTrainer( - model=model, - args=training_args, - data_args=data_args, - train_dataset=train_dataset, - eval_dataset=eval_dataset, - data_collator=Seq2SeqDataCollator( - tokenizer, data_args, model.config.decoder_start_token_id, training_args.tpu_num_cores - ), - compute_metrics=compute_metrics_fn, - tokenizer=tokenizer, - ) - - all_metrics = {} - # Training - if training_args.do_train: - logger.info("*** Train ***") - - train_result = trainer.train( - model_path=model_args.model_name_or_path if os.path.isdir(model_args.model_name_or_path) else None - ) - metrics = train_result.metrics - metrics["train_n_objs"] = data_args.n_train - - trainer.save_model() # this also saves the tokenizer - - if trainer.is_world_process_zero(): - handle_metrics("train", metrics, training_args.output_dir) - all_metrics.update(metrics) - - # Need to save the state, since Trainer.save_model saves only the tokenizer with the model - trainer.state.save_to_json(os.path.join(training_args.output_dir, "trainer_state.json")) - - # For convenience, we also re-save the tokenizer to the same directory, - # so that you can share your model easily on huggingface.co/models =) - tokenizer.save_pretrained(training_args.output_dir) - - # Evaluation - if training_args.do_eval: - logger.info("*** Evaluate ***") - - metrics = trainer.evaluate(metric_key_prefix="val") - metrics["val_n_objs"] = data_args.n_val - metrics["val_loss"] = round(metrics["val_loss"], 4) - - if trainer.is_world_process_zero(): - - handle_metrics("val", metrics, training_args.output_dir) - all_metrics.update(metrics) - - if training_args.do_predict: - logger.info("*** Predict ***") - - test_output = trainer.predict(test_dataset=test_dataset, metric_key_prefix="test") - metrics = test_output.metrics - metrics["test_n_objs"] = data_args.n_test - - if trainer.is_world_process_zero(): - metrics["test_loss"] = round(metrics["test_loss"], 4) - handle_metrics("test", metrics, training_args.output_dir) - all_metrics.update(metrics) - - if training_args.predict_with_generate: - test_preds = tokenizer.batch_decode( - test_output.predictions, skip_special_tokens=True, clean_up_tokenization_spaces=True - ) - test_preds = lmap(str.strip, test_preds) - write_txt_file(test_preds, os.path.join(training_args.output_dir, "test_generations.txt")) - - if trainer.is_world_process_zero(): - save_json(all_metrics, os.path.join(training_args.output_dir, "all_results.json")) - - return all_metrics - - -def _mp_fn(index): - # For xla_spawn (TPUs) - main() - - -if __name__ == "__main__": - main() diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/minify_dataset.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/minify_dataset.py deleted file mode 100755 index 8fd03196a0b..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/minify_dataset.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -# Copyright 2020 The HuggingFace Team. 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 pathlib import Path - -import fire - - -def minify(src_dir: str, dest_dir: str, n: int): - """Write first n lines of each file f in src_dir to dest_dir/f """ - src_dir = Path(src_dir) - dest_dir = Path(dest_dir) - dest_dir.mkdir(exist_ok=True) - for path in src_dir.iterdir(): - new = [x.rstrip() for x in list(path.open().readlines())][:n] - dest_path = dest_dir.joinpath(path.name) - print(dest_path) - dest_path.open("w").write("\n".join(new)) - - -if __name__ == "__main__": - fire.Fire(minify) diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_calculate_rouge.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_calculate_rouge.py deleted file mode 100644 index bd1dd57a272..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_calculate_rouge.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright 2020 The HuggingFace Team. 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 defaultdict -from pathlib import Path - -import pandas as pd - -from rouge_cli import calculate_rouge_path -from utils import calculate_rouge - - -PRED = [ - 'Prosecutor: "No videos were used in the crash investigation" German papers say they saw a cell phone video of the final seconds on board Flight 9525. The Germanwings co-pilot says he had a "previous episode of severe depression" German airline confirms it knew of Andreas Lubitz\'s depression years before he took control.', - "The Palestinian Authority officially becomes the 123rd member of the International Criminal Court. The formal accession was marked with a ceremony at The Hague, in the Netherlands. The Palestinians signed the ICC's founding Rome Statute in January. Israel and the United States opposed the Palestinians' efforts to join the body.", - "Amnesty International releases its annual report on the death penalty. The report catalogs the use of state-sanctioned killing as a punitive measure across the globe. At least 607 people were executed around the world in 2014, compared to 778 in 2013. The U.S. remains one of the worst offenders for imposing capital punishment.", -] - -TGT = [ - 'Marseille prosecutor says "so far no videos were used in the crash investigation" despite media reports . Journalists at Bild and Paris Match are "very confident" the video clip is real, an editor says . Andreas Lubitz had informed his Lufthansa training school of an episode of severe depression, airline says .', - "Membership gives the ICC jurisdiction over alleged crimes committed in Palestinian territories since last June . Israel and the United States opposed the move, which could open the door to war crimes investigations against Israelis .", - "Amnesty's annual death penalty report catalogs encouraging signs, but setbacks in numbers of those sentenced to death . Organization claims that governments around the world are using the threat of terrorism to advance executions . The number of executions worldwide has gone down by almost 22% compared with 2013, but death sentences up by 28% .", -] - - -def test_disaggregated_scores_are_determinstic(): - no_aggregation = calculate_rouge(PRED, TGT, bootstrap_aggregation=False, rouge_keys=["rouge2", "rougeL"]) - assert isinstance(no_aggregation, defaultdict) - no_aggregation_just_r2 = calculate_rouge(PRED, TGT, bootstrap_aggregation=False, rouge_keys=["rouge2"]) - assert ( - pd.DataFrame(no_aggregation["rouge2"]).fmeasure.mean() - == pd.DataFrame(no_aggregation_just_r2["rouge2"]).fmeasure.mean() - ) - - -def test_newline_cnn_improvement(): - k = "rougeLsum" - score = calculate_rouge(PRED, TGT, newline_sep=True, rouge_keys=[k])[k] - score_no_sep = calculate_rouge(PRED, TGT, newline_sep=False, rouge_keys=[k])[k] - assert score > score_no_sep - - -def test_newline_irrelevant_for_other_metrics(): - k = ["rouge1", "rouge2", "rougeL"] - score_sep = calculate_rouge(PRED, TGT, newline_sep=True, rouge_keys=k) - score_no_sep = calculate_rouge(PRED, TGT, newline_sep=False, rouge_keys=k) - assert score_sep == score_no_sep - - -def test_single_sent_scores_dont_depend_on_newline_sep(): - pred = [ - "Her older sister, Margot Frank, died in 1945, a month earlier than previously thought.", - 'Marseille prosecutor says "so far no videos were used in the crash investigation" despite media reports .', - ] - tgt = [ - "Margot Frank, died in 1945, a month earlier than previously thought.", - 'Prosecutor: "No videos were used in the crash investigation" German papers say they saw a cell phone video of the final seconds on board Flight 9525.', - ] - assert calculate_rouge(pred, tgt, newline_sep=True) == calculate_rouge(pred, tgt, newline_sep=False) - - -def test_pegasus_newline(): - - pred = [ - """" "a person who has such a video needs to immediately give it to the investigators," prosecutor says . "it is a very disturbing scene," editor-in-chief of bild online tells "erin burnett: outfront" """ - ] - tgt = [ - """ Marseille prosecutor says "so far no videos were used in the crash investigation" despite media reports . Journalists at Bild and Paris Match are "very confident" the video clip is real, an editor says . Andreas Lubitz had informed his Lufthansa training school of an episode of severe depression, airline says .""" - ] - - prev_score = calculate_rouge(pred, tgt, rouge_keys=["rougeLsum"], newline_sep=False)["rougeLsum"] - new_score = calculate_rouge(pred, tgt, rouge_keys=["rougeLsum"])["rougeLsum"] - assert new_score > prev_score - - -def test_rouge_cli(): - data_dir = Path("examples/seq2seq/test_data/wmt_en_ro") - metrics = calculate_rouge_path(data_dir.joinpath("test.source"), data_dir.joinpath("test.target")) - assert isinstance(metrics, dict) - metrics_default_dict = calculate_rouge_path( - data_dir.joinpath("test.source"), data_dir.joinpath("test.target"), bootstrap_aggregation=False - ) - assert isinstance(metrics_default_dict, defaultdict) diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_datasets.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_datasets.py deleted file mode 100644 index 6792fcf6ddd..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_datasets.py +++ /dev/null @@ -1,254 +0,0 @@ -# Copyright 2020 The HuggingFace Team. 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 -from pathlib import Path - -import numpy as np -import pytest -from torch.utils.data import DataLoader - -from pack_dataset import pack_data_dir -from parameterized import parameterized -from save_len_file import save_len_file -from transformers import AutoTokenizer -from transformers.models.mbart.modeling_mbart import shift_tokens_right -from transformers.testing_utils import TestCasePlus, require_torch_non_multi_gpu_but_fix_me, slow -from utils import FAIRSEQ_AVAILABLE, DistributedSortishSampler, LegacySeq2SeqDataset, Seq2SeqDataset - - -BERT_BASE_CASED = "bert-base-cased" -PEGASUS_XSUM = "google/pegasus-xsum" -ARTICLES = [" Sam ate lunch today.", "Sams lunch ingredients."] -SUMMARIES = ["A very interesting story about what I ate for lunch.", "Avocado, celery, turkey, coffee"] -T5_TINY = "patrickvonplaten/t5-tiny-random" -BART_TINY = "sshleifer/bart-tiny-random" -MBART_TINY = "sshleifer/tiny-mbart" -MARIAN_TINY = "sshleifer/tiny-marian-en-de" - - -def _dump_articles(path: Path, articles: list): - content = "\n".join(articles) - Path(path).open("w").writelines(content) - - -def make_test_data_dir(tmp_dir): - for split in ["train", "val", "test"]: - _dump_articles(os.path.join(tmp_dir, f"{split}.source"), ARTICLES) - _dump_articles(os.path.join(tmp_dir, f"{split}.target"), SUMMARIES) - return tmp_dir - - -class TestAll(TestCasePlus): - @parameterized.expand( - [ - MBART_TINY, - MARIAN_TINY, - T5_TINY, - BART_TINY, - PEGASUS_XSUM, - ], - ) - @slow - @require_torch_non_multi_gpu_but_fix_me - def test_seq2seq_dataset_truncation(self, tok_name): - tokenizer = AutoTokenizer.from_pretrained(tok_name) - tmp_dir = make_test_data_dir(tmp_dir=self.get_auto_remove_tmp_dir()) - max_len_source = max(len(tokenizer.encode(a)) for a in ARTICLES) - max_len_target = max(len(tokenizer.encode(a)) for a in SUMMARIES) - max_src_len = 4 - max_tgt_len = 8 - assert max_len_target > max_src_len # Will be truncated - assert max_len_source > max_src_len # Will be truncated - src_lang, tgt_lang = "ro_RO", "de_DE" # ignored for all but mbart, but never causes error. - train_dataset = Seq2SeqDataset( - tokenizer, - data_dir=tmp_dir, - type_path="train", - max_source_length=max_src_len, - max_target_length=max_tgt_len, # ignored - src_lang=src_lang, - tgt_lang=tgt_lang, - ) - dataloader = DataLoader(train_dataset, batch_size=2, collate_fn=train_dataset.collate_fn) - for batch in dataloader: - assert isinstance(batch, dict) - assert batch["attention_mask"].shape == batch["input_ids"].shape - # show that articles were trimmed. - assert batch["input_ids"].shape[1] == max_src_len - # show that targets are the same len - assert batch["labels"].shape[1] == max_tgt_len - if tok_name != MBART_TINY: - continue - # check language codes in correct place - batch["decoder_input_ids"] = shift_tokens_right(batch["labels"], tokenizer.pad_token_id) - assert batch["decoder_input_ids"][0, 0].item() == tokenizer.lang_code_to_id[tgt_lang] - assert batch["decoder_input_ids"][0, -1].item() == tokenizer.eos_token_id - assert batch["input_ids"][0, -2].item() == tokenizer.eos_token_id - assert batch["input_ids"][0, -1].item() == tokenizer.lang_code_to_id[src_lang] - - break # No need to test every batch - - @parameterized.expand([BART_TINY, BERT_BASE_CASED]) - @require_torch_non_multi_gpu_but_fix_me - def test_legacy_dataset_truncation(self, tok): - tokenizer = AutoTokenizer.from_pretrained(tok) - tmp_dir = make_test_data_dir(tmp_dir=self.get_auto_remove_tmp_dir()) - max_len_source = max(len(tokenizer.encode(a)) for a in ARTICLES) - max_len_target = max(len(tokenizer.encode(a)) for a in SUMMARIES) - trunc_target = 4 - train_dataset = LegacySeq2SeqDataset( - tokenizer, - data_dir=tmp_dir, - type_path="train", - max_source_length=20, - max_target_length=trunc_target, - ) - dataloader = DataLoader(train_dataset, batch_size=2, collate_fn=train_dataset.collate_fn) - for batch in dataloader: - assert batch["attention_mask"].shape == batch["input_ids"].shape - # show that articles were trimmed. - assert batch["input_ids"].shape[1] == max_len_source - assert 20 >= batch["input_ids"].shape[1] # trimmed significantly - # show that targets were truncated - assert batch["labels"].shape[1] == trunc_target # Truncated - assert max_len_target > trunc_target # Truncated - break # No need to test every batch - - @require_torch_non_multi_gpu_but_fix_me - def test_pack_dataset(self): - tokenizer = AutoTokenizer.from_pretrained("facebook/mbart-large-cc25") - - tmp_dir = Path(make_test_data_dir(tmp_dir=self.get_auto_remove_tmp_dir())) - orig_examples = tmp_dir.joinpath("train.source").open().readlines() - save_dir = Path(make_test_data_dir(tmp_dir=self.get_auto_remove_tmp_dir())) - pack_data_dir(tokenizer, tmp_dir, 128, save_dir) - orig_paths = {x.name for x in tmp_dir.iterdir()} - new_paths = {x.name for x in save_dir.iterdir()} - packed_examples = save_dir.joinpath("train.source").open().readlines() - # orig: [' Sam ate lunch today.\n', 'Sams lunch ingredients.'] - # desired_packed: [' Sam ate lunch today.\n Sams lunch ingredients.'] - assert len(packed_examples) < len(orig_examples) - assert len(packed_examples) == 1 - assert len(packed_examples[0]) == sum(len(x) for x in orig_examples) - assert orig_paths == new_paths - - @pytest.mark.skipif(not FAIRSEQ_AVAILABLE, reason="This test requires fairseq") - @require_torch_non_multi_gpu_but_fix_me - def test_dynamic_batch_size(self): - if not FAIRSEQ_AVAILABLE: - return - ds, max_tokens, tokenizer = self._get_dataset(max_len=64) - required_batch_size_multiple = 64 - batch_sampler = ds.make_dynamic_sampler(max_tokens, required_batch_size_multiple=required_batch_size_multiple) - batch_sizes = [len(x) for x in batch_sampler] - assert len(set(batch_sizes)) > 1 # it's not dynamic batch size if every batch is the same length - assert sum(batch_sizes) == len(ds) # no dropped or added examples - data_loader = DataLoader(ds, batch_sampler=batch_sampler, collate_fn=ds.collate_fn, num_workers=2) - failures = [] - num_src_per_batch = [] - for batch in data_loader: - src_shape = batch["input_ids"].shape - bs = src_shape[0] - assert bs % required_batch_size_multiple == 0 or bs < required_batch_size_multiple - num_src_tokens = np.product(batch["input_ids"].shape) - num_src_per_batch.append(num_src_tokens) - if num_src_tokens > (max_tokens * 1.1): - failures.append(num_src_tokens) - assert num_src_per_batch[0] == max(num_src_per_batch) - if failures: - raise AssertionError(f"too many tokens in {len(failures)} batches") - - @require_torch_non_multi_gpu_but_fix_me - def test_sortish_sampler_reduces_padding(self): - ds, _, tokenizer = self._get_dataset(max_len=512) - bs = 2 - sortish_sampler = ds.make_sortish_sampler(bs, shuffle=False) - - naive_dl = DataLoader(ds, batch_size=bs, collate_fn=ds.collate_fn, num_workers=2) - sortish_dl = DataLoader(ds, batch_size=bs, collate_fn=ds.collate_fn, num_workers=2, sampler=sortish_sampler) - - pad = tokenizer.pad_token_id - - def count_pad_tokens(data_loader, k="input_ids"): - return [batch[k].eq(pad).sum().item() for batch in data_loader] - - assert sum(count_pad_tokens(sortish_dl, k="labels")) < sum(count_pad_tokens(naive_dl, k="labels")) - assert sum(count_pad_tokens(sortish_dl)) < sum(count_pad_tokens(naive_dl)) - assert len(sortish_dl) == len(naive_dl) - - def _get_dataset(self, n_obs=1000, max_len=128): - if os.getenv("USE_REAL_DATA", False): - data_dir = "examples/seq2seq/wmt_en_ro" - max_tokens = max_len * 2 * 64 - if not Path(data_dir).joinpath("train.len").exists(): - save_len_file(MARIAN_TINY, data_dir) - else: - data_dir = "examples/seq2seq/test_data/wmt_en_ro" - max_tokens = max_len * 4 - save_len_file(MARIAN_TINY, data_dir) - - tokenizer = AutoTokenizer.from_pretrained(MARIAN_TINY) - ds = Seq2SeqDataset( - tokenizer, - data_dir=data_dir, - type_path="train", - max_source_length=max_len, - max_target_length=max_len, - n_obs=n_obs, - ) - return ds, max_tokens, tokenizer - - @require_torch_non_multi_gpu_but_fix_me - def test_distributed_sortish_sampler_splits_indices_between_procs(self): - ds, max_tokens, tokenizer = self._get_dataset() - ids1 = set(DistributedSortishSampler(ds, 256, num_replicas=2, rank=0, add_extra_examples=False)) - ids2 = set(DistributedSortishSampler(ds, 256, num_replicas=2, rank=1, add_extra_examples=False)) - assert ids1.intersection(ids2) == set() - - @parameterized.expand( - [ - MBART_TINY, - MARIAN_TINY, - T5_TINY, - BART_TINY, - PEGASUS_XSUM, - ], - ) - @require_torch_non_multi_gpu_but_fix_me - def test_dataset_kwargs(self, tok_name): - tokenizer = AutoTokenizer.from_pretrained(tok_name, use_fast=False) - if tok_name == MBART_TINY: - train_dataset = Seq2SeqDataset( - tokenizer, - data_dir=make_test_data_dir(tmp_dir=self.get_auto_remove_tmp_dir()), - type_path="train", - max_source_length=4, - max_target_length=8, - src_lang="EN", - tgt_lang="FR", - ) - kwargs = train_dataset.dataset_kwargs - assert "src_lang" in kwargs and "tgt_lang" in kwargs - else: - train_dataset = Seq2SeqDataset( - tokenizer, - data_dir=make_test_data_dir(tmp_dir=self.get_auto_remove_tmp_dir()), - type_path="train", - max_source_length=4, - max_target_length=8, - ) - kwargs = train_dataset.dataset_kwargs - assert "add_prefix_space" not in kwargs if tok_name != BART_TINY else "add_prefix_space" in kwargs - assert len(kwargs) == 1 if tok_name == BART_TINY else len(kwargs) == 0 diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_fsmt_bleu_score.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_fsmt_bleu_score.py deleted file mode 100644 index beb7f2bc985..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_fsmt_bleu_score.py +++ /dev/null @@ -1,71 +0,0 @@ -# coding=utf-8 -# Copyright 2020 Huggingface -# -# 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 io -import json -import unittest - -from parameterized import parameterized -from transformers import FSMTForConditionalGeneration, FSMTTokenizer -from transformers.testing_utils import get_tests_dir, require_torch, slow, torch_device -from utils import calculate_bleu - - -filename = get_tests_dir() + "/test_data/fsmt/fsmt_val_data.json" -with io.open(filename, "r", encoding="utf-8") as f: - bleu_data = json.load(f) - - -@require_torch -class ModelEvalTester(unittest.TestCase): - def get_tokenizer(self, mname): - return FSMTTokenizer.from_pretrained(mname) - - def get_model(self, mname): - model = FSMTForConditionalGeneration.from_pretrained(mname).to(torch_device) - if torch_device == "cuda": - model.half() - return model - - @parameterized.expand( - [ - ["en-ru", 26.0], - ["ru-en", 22.0], - ["en-de", 22.0], - ["de-en", 29.0], - ] - ) - @slow - def test_bleu_scores(self, pair, min_bleu_score): - # note: this test is not testing the best performance since it only evals a small batch - # but it should be enough to detect a regression in the output quality - mname = f"facebook/wmt19-{pair}" - tokenizer = self.get_tokenizer(mname) - model = self.get_model(mname) - - src_sentences = bleu_data[pair]["src"] - tgt_sentences = bleu_data[pair]["tgt"] - - batch = tokenizer(src_sentences, return_tensors="pt", truncation=True, padding="longest").to(torch_device) - outputs = model.generate( - input_ids=batch.input_ids, - num_beams=8, - ) - decoded_sentences = tokenizer.batch_decode( - outputs, skip_special_tokens=True, clean_up_tokenization_spaces=False - ) - scores = calculate_bleu(decoded_sentences, tgt_sentences) - print(scores) - self.assertGreaterEqual(scores["bleu"], min_bleu_score) diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_seq2seq_examples.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_seq2seq_examples.py deleted file mode 100644 index ecc0524c37d..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_seq2seq_examples.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright 2020 The HuggingFace Team. 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 -from pathlib import Path -from unittest.mock import patch - -from parameterized import parameterized -from run_eval import run_generate -from run_eval_search import run_search -from transformers.testing_utils import CaptureStdout, TestCasePlus, slow -from utils import ROUGE_KEYS - - -logging.basicConfig(level=logging.DEBUG) -logger = logging.getLogger() - - -def _dump_articles(path: Path, articles: list): - content = "\n".join(articles) - Path(path).open("w").writelines(content) - - -T5_TINY = "patrickvonplaten/t5-tiny-random" -BART_TINY = "sshleifer/bart-tiny-random" -MBART_TINY = "sshleifer/tiny-mbart" - -stream_handler = logging.StreamHandler(sys.stdout) -logger.addHandler(stream_handler) -logging.disable(logging.CRITICAL) # remove noisy download output from tracebacks - - -class TestTheRest(TestCasePlus): - def run_eval_tester(self, model): - input_file_name = Path(self.get_auto_remove_tmp_dir()) / "utest_input.source" - output_file_name = input_file_name.parent / "utest_output.txt" - assert not output_file_name.exists() - articles = [" New York (CNN)When Liana Barrientos was 23 years old, she got married in Westchester County."] - _dump_articles(input_file_name, articles) - - score_path = str(Path(self.get_auto_remove_tmp_dir()) / "scores.json") - task = "translation_en_to_de" if model == T5_TINY else "summarization" - testargs = f""" - run_eval_search.py - {model} - {input_file_name} - {output_file_name} - --score_path {score_path} - --task {task} - --num_beams 2 - --length_penalty 2.0 - """.split() - - with patch.object(sys, "argv", testargs): - run_generate() - assert Path(output_file_name).exists() - # os.remove(Path(output_file_name)) - - # test one model to quickly (no-@slow) catch simple problems and do an - # extensive testing of functionality with multiple models as @slow separately - def test_run_eval(self): - self.run_eval_tester(T5_TINY) - - # any extra models should go into the list here - can be slow - @parameterized.expand([BART_TINY, MBART_TINY]) - @slow - def test_run_eval_slow(self, model): - self.run_eval_tester(model) - - # testing with 2 models to validate: 1. translation (t5) 2. summarization (mbart) - @parameterized.expand([T5_TINY, MBART_TINY]) - @slow - def test_run_eval_search(self, model): - input_file_name = Path(self.get_auto_remove_tmp_dir()) / "utest_input.source" - output_file_name = input_file_name.parent / "utest_output.txt" - assert not output_file_name.exists() - - text = { - "en": ["Machine learning is great, isn't it?", "I like to eat bananas", "Tomorrow is another great day!"], - "de": [ - "Maschinelles Lernen ist großartig, oder?", - "Ich esse gerne Bananen", - "Morgen ist wieder ein toller Tag!", - ], - } - - tmp_dir = Path(self.get_auto_remove_tmp_dir()) - score_path = str(tmp_dir / "scores.json") - reference_path = str(tmp_dir / "val.target") - _dump_articles(input_file_name, text["en"]) - _dump_articles(reference_path, text["de"]) - task = "translation_en_to_de" if model == T5_TINY else "summarization" - testargs = f""" - run_eval_search.py - {model} - {str(input_file_name)} - {str(output_file_name)} - --score_path {score_path} - --reference_path {reference_path} - --task {task} - """.split() - testargs.extend(["--search", "num_beams=1:2 length_penalty=0.9:1.0"]) - - with patch.object(sys, "argv", testargs): - with CaptureStdout() as cs: - run_search() - expected_strings = [" num_beams | length_penalty", model, "Best score args"] - un_expected_strings = ["Info"] - if "translation" in task: - expected_strings.append("bleu") - else: - expected_strings.extend(ROUGE_KEYS) - for w in expected_strings: - assert w in cs.out - for w in un_expected_strings: - assert w not in cs.out - assert Path(output_file_name).exists() - os.remove(Path(output_file_name)) diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_seq2seq_examples_multi_gpu.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_seq2seq_examples_multi_gpu.py deleted file mode 100644 index 6625f061b56..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_seq2seq_examples_multi_gpu.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 2020 The HuggingFace Team. 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. - -# as due to their complexity multi-gpu tests could impact other tests, and to aid debug we have those in a separate module. - -import os -import sys - -from transformers.testing_utils import TestCasePlus, execute_subprocess_async, get_gpu_count, require_torch_gpu, slow - -from .utils import load_json - - -class TestSummarizationDistillerMultiGPU(TestCasePlus): - @classmethod - def setUpClass(cls): - return cls - - @slow - @require_torch_gpu - def test_distributed_eval(self): - output_dir = self.get_auto_remove_tmp_dir() - args = f""" - --model_name Helsinki-NLP/opus-mt-en-ro - --save_dir {output_dir} - --data_dir {self.test_file_dir_str}/test_data/wmt_en_ro - --num_beams 2 - --task translation - """.split() - - # we want this test to run even if there is only one GPU, but if there are more we use them all - n_gpu = get_gpu_count() - distributed_args = f""" - -m torch.distributed.launch - --nproc_per_node={n_gpu} - {self.test_file_dir}/run_distributed_eval.py - """.split() - cmd = [sys.executable] + distributed_args + args - execute_subprocess_async(cmd, env=self.get_env()) - - metrics_save_path = os.path.join(output_dir, "test_bleu.json") - metrics = load_json(metrics_save_path) - # print(metrics) - self.assertGreaterEqual(metrics["bleu"], 25) diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_tatoeba_conversion.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_tatoeba_conversion.py deleted file mode 100644 index 5747811bddd..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/old_test_tatoeba_conversion.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright 2020 The HuggingFace Team. 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 tempfile -import unittest - -from transformers.file_utils import cached_property -from transformers.models.marian.convert_marian_tatoeba_to_pytorch import DEFAULT_REPO, TatoebaConverter -from transformers.testing_utils import require_torch_non_multi_gpu_but_fix_me, slow - - -@unittest.skipUnless(os.path.exists(DEFAULT_REPO), "Tatoeba directory does not exist.") -class TatoebaConversionTester(unittest.TestCase): - @cached_property - def resolver(self): - tmp_dir = tempfile.mkdtemp() - return TatoebaConverter(save_dir=tmp_dir) - - @slow - @require_torch_non_multi_gpu_but_fix_me - def test_resolver(self): - self.resolver.convert_models(["heb-eng"]) - - @slow - @require_torch_non_multi_gpu_but_fix_me - def test_model_card(self): - content, mmeta = self.resolver.write_model_card("opus-mt-he-en", dry_run=True) - assert mmeta["long_pair"] == "heb-eng" diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/pack_dataset.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/pack_dataset.py deleted file mode 100755 index 6f226de2cc2..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/pack_dataset.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python -# Copyright 2020 The HuggingFace Team. 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. -"""Fill examples with bitext up to max_tokens without breaking up examples. -[['I went', 'yo fui'], -['to the store', 'a la tienda'] -] -=> ['I went to the store', 'yo fui a la tienda'] -""" - -import argparse -import shutil -from pathlib import Path - -from tqdm import tqdm - -from transformers import AutoTokenizer - - -def pack_examples(tok, src_examples, tgt_examples, max_tokens=1024): - - finished_src, finished_tgt = [], [] - - sorted_examples = list(zip(src_examples, tgt_examples)) - new_src, new_tgt = sorted_examples[0] - - def is_too_big(strang): - return tok(strang, return_tensors="pt").input_ids.shape[1] > max_tokens - - for src, tgt in tqdm(sorted_examples[1:]): - cand_src = new_src + " " + src - cand_tgt = new_tgt + " " + tgt - if is_too_big(cand_src) or is_too_big(cand_tgt): # cant fit, finalize example - finished_src.append(new_src) - finished_tgt.append(new_tgt) - new_src, new_tgt = src, tgt - else: # can fit, keep adding - new_src, new_tgt = cand_src, cand_tgt - - # cleanup - if new_src: - assert new_tgt - finished_src.append(new_src) - finished_tgt.append(new_tgt) - return finished_src, finished_tgt - - -def pack_data_dir(tok, data_dir: Path, max_tokens, save_path): - save_path = Path(save_path) - save_path.mkdir(exist_ok=True) - for split in ["train"]: - src_path, tgt_path = data_dir / f"{split}.source", data_dir / f"{split}.target" - src_docs = [x.rstrip() for x in Path(src_path).open().readlines()] - tgt_docs = [x.rstrip() for x in Path(tgt_path).open().readlines()] - packed_src, packed_tgt = pack_examples(tok, src_docs, tgt_docs, max_tokens) - print(f"packed {split} split from {len(src_docs)} examples -> {len(packed_src)}.") - Path(save_path / f"{split}.source").open("w").write("\n".join(packed_src)) - Path(save_path / f"{split}.target").open("w").write("\n".join(packed_tgt)) - for split in ["val", "test"]: - src_path, tgt_path = data_dir / f"{split}.source", data_dir / f"{split}.target" - shutil.copyfile(src_path, save_path / f"{split}.source") - shutil.copyfile(tgt_path, save_path / f"{split}.target") - - -def packer_cli(): - parser = argparse.ArgumentParser() - parser.add_argument("--tok_name", type=str, help="like facebook/bart-large-cnn,t5-base, etc.") - parser.add_argument("--max_seq_len", type=int, default=128) - parser.add_argument("--data_dir", type=str) - parser.add_argument("--save_path", type=str) - args = parser.parse_args() - tokenizer = AutoTokenizer.from_pretrained(args.tok_name) - return pack_data_dir(tokenizer, Path(args.data_dir), args.max_seq_len, args.save_path) - - -if __name__ == "__main__": - packer_cli() diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/requirements.txt b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/requirements.txt deleted file mode 100644 index e40aef17932..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/requirements.txt +++ /dev/null @@ -1,20 +0,0 @@ -tensorboard -scikit-learn -seqeval -psutil -sacrebleu -rouge-score -tensorflow_datasets -matplotlib -git-python==1.0.3 -faiss-cpu -streamlit -elasticsearch -nltk -pandas -datasets >= 1.1.3 -fire -pytest -conllu -sentencepiece != 0.1.92 -protobuf diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/romanian_postprocessing.md b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/romanian_postprocessing.md deleted file mode 100644 index 938f0d1d722..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/romanian_postprocessing.md +++ /dev/null @@ -1,65 +0,0 @@ -### Motivation -Without processing, english-> romanian mbart-large-en-ro gets BLEU score 26.8 on the WMT data. -With post processing, it can score 37.. -Here is the postprocessing code, stolen from @mjpost in this [issue](https://github.com/pytorch/fairseq/issues/1758) - - - -### Instructions -Note: You need to have your test_generations.txt before you start this process. -(1) Setup `mosesdecoder` and `wmt16-scripts` -```bash -cd $HOME -git clone git@github.com:moses-smt/mosesdecoder.git -cd mosesdecoder -git clone git@github.com:rsennrich/wmt16-scripts.git -``` - -(2) define a function for post processing. - It removes diacritics and does other things I don't understand -```bash -ro_post_process () { - sys=$1 - ref=$2 - export MOSES_PATH=$HOME/mosesdecoder - REPLACE_UNICODE_PUNCT=$MOSES_PATH/scripts/tokenizer/replace-unicode-punctuation.perl - NORM_PUNC=$MOSES_PATH/scripts/tokenizer/normalize-punctuation.perl - REM_NON_PRINT_CHAR=$MOSES_PATH/scripts/tokenizer/remove-non-printing-char.perl - REMOVE_DIACRITICS=$MOSES_PATH/wmt16-scripts/preprocess/remove-diacritics.py - NORMALIZE_ROMANIAN=$MOSES_PATH/wmt16-scripts/preprocess/normalise-romanian.py - TOKENIZER=$MOSES_PATH/scripts/tokenizer/tokenizer.perl - - - - lang=ro - for file in $sys $ref; do - cat $file \ - | $REPLACE_UNICODE_PUNCT \ - | $NORM_PUNC -l $lang \ - | $REM_NON_PRINT_CHAR \ - | $NORMALIZE_ROMANIAN \ - | $REMOVE_DIACRITICS \ - | $TOKENIZER -no-escape -l $lang \ - > $(basename $file).tok - done - # compute BLEU - cat $(basename $sys).tok | sacrebleu -tok none -s none -b $(basename $ref).tok -} -``` - -(3) Call the function on test_generations.txt and test.target -For example, -```bash -ro_post_process enro_finetune/test_generations.txt wmt_en_ro/test.target -``` -This will split out a new blue score and write a new fine called `test_generations.tok` with post-processed outputs. - - - - - - - - - -``` diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/rouge_cli.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/rouge_cli.py deleted file mode 100644 index cd636bbcd1c..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/rouge_cli.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2020 The HuggingFace Team. 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 fire - -from utils import calculate_rouge, save_json - - -def calculate_rouge_path(pred_path, tgt_path, save_path=None, **kwargs): - """Kwargs will be passed to calculate_rouge""" - pred_lns = [x.strip() for x in open(pred_path).readlines()] - tgt_lns = [x.strip() for x in open(tgt_path).readlines()][: len(pred_lns)] - metrics = calculate_rouge(pred_lns, tgt_lns, **kwargs) - if save_path is not None: - save_json(metrics, save_path, indent=None) - return metrics # these print nicely - - -if __name__ == "__main__": - fire.Fire(calculate_rouge_path) diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/run_distributed_eval.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/run_distributed_eval.py deleted file mode 100755 index 90a348078f3..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/run_distributed_eval.py +++ /dev/null @@ -1,261 +0,0 @@ -#!/usr/bin/env python -# Copyright 2020 The HuggingFace Team. 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 argparse -import shutil -import time -from json import JSONDecodeError -from logging import getLogger -from pathlib import Path -from typing import Dict, List - -import torch -from torch.utils.data import DataLoader -from tqdm import tqdm - -from transformers import AutoModelForSeq2SeqLM, AutoTokenizer -from utils import ( - Seq2SeqDataset, - calculate_bleu, - calculate_rouge, - chunks, - lmap, - load_json, - parse_numeric_n_bool_cl_kwargs, - save_json, - use_task_specific_params, - write_txt_file, -) - - -logger = getLogger(__name__) - - -def eval_data_dir( - data_dir, - save_dir: str, - model_name: str, - bs: int = 8, - max_source_length: int = 1024, - type_path="val", - n_obs=None, - fp16=False, - task="summarization", - local_rank=None, - num_return_sequences=1, - dataset_kwargs: Dict = None, - prefix="", - **generate_kwargs, -) -> Dict: - """Run evaluation on part of the data for one gpu and save to {save_dir}/rank_{rank}_output.json""" - model_name = str(model_name) - assert local_rank is not None - torch.distributed.init_process_group(backend="nccl", rank=local_rank) - - save_dir = Path(save_dir) - save_path = save_dir.joinpath(f"rank_{local_rank}_output.json") - torch.cuda.set_device(local_rank) - model = AutoModelForSeq2SeqLM.from_pretrained(model_name).cuda() - if fp16: - model = model.half() - # determine if we need to increase num_beams - use_task_specific_params(model, task) # update config with task specific params - num_beams = generate_kwargs.pop("num_beams", model.config.num_beams) # AttributeError risk? - if num_return_sequences > num_beams: - num_beams = num_return_sequences - - tokenizer = AutoTokenizer.from_pretrained(model_name) - logger.info(f"Inferred tokenizer type: {tokenizer.__class__}") # if this is wrong, check config.model_type. - - if max_source_length is None: - max_source_length = tokenizer.model_max_length - if prefix is None: - prefix = prefix or getattr(model.config, "prefix", "") or "" - ds = Seq2SeqDataset( - tokenizer, - data_dir, - max_source_length, - max_target_length=1024, - type_path=type_path, - n_obs=n_obs, - prefix=prefix, - **dataset_kwargs, - ) - # I set shuffle=True for a more accurate progress bar. - # If all the longest samples are first, the prog bar estimate is too high at the beginning. - sampler = ds.make_sortish_sampler(bs, distributed=True, add_extra_examples=False, shuffle=True) - data_loader = DataLoader(ds, sampler=sampler, batch_size=bs, collate_fn=ds.collate_fn) - results = [] - for batch in tqdm(data_loader): - summaries = model.generate( - input_ids=batch["input_ids"].to(model.device), - attention_mask=batch["attention_mask"].to(model.device), - num_return_sequences=num_return_sequences, - num_beams=num_beams, - **generate_kwargs, - ) - preds = tokenizer.batch_decode(summaries, skip_special_tokens=True, clean_up_tokenization_spaces=False) - ids = batch["ids"] - if num_return_sequences > 1: - preds = chunks(preds, num_return_sequences) # batch size chunks, each of size num_return_seq - for i, pred in enumerate(preds): - results.append(dict(pred=pred, id=ids[i].item())) - save_json(results, save_path) - return results, sampler.num_replicas - - -def run_generate(): - parser = argparse.ArgumentParser( - epilog="Unspecified args like --num_beams=2 --decoder_start_token_id=4 are passed to model.generate" - ) - parser.add_argument("--data_dir", type=str, help="like cnn_dm/test.source") - parser.add_argument( - "--model_name", - type=str, - help="like facebook/bart-large-cnn,t5-base, etc.", - default="sshleifer/distilbart-xsum-12-3", - ) - parser.add_argument("--save_dir", type=str, help="where to save", default="tmp_gen") - parser.add_argument("--max_source_length", type=int, default=None) - parser.add_argument( - "--type_path", type=str, default="test", help="which subset to evaluate typically train/val/test" - ) - parser.add_argument("--task", type=str, default="summarization", help="used for task_specific_params + metrics") - parser.add_argument("--bs", type=int, default=8, required=False, help="batch size") - parser.add_argument( - "--local_rank", type=int, default=-1, required=False, help="should be passed by distributed.launch" - ) - - parser.add_argument( - "--n_obs", type=int, default=None, required=False, help="How many observations. Defaults to all." - ) - parser.add_argument( - "--num_return_sequences", type=int, default=1, required=False, help="How many sequences to return" - ) - parser.add_argument( - "--sync_timeout", - type=int, - default=600, - required=False, - help="How long should master process wait for other processes to finish.", - ) - parser.add_argument("--src_lang", type=str, default=None, required=False) - parser.add_argument("--tgt_lang", type=str, default=None, required=False) - parser.add_argument( - "--prefix", type=str, required=False, default=None, help="will be added to the begininng of src examples" - ) - parser.add_argument("--fp16", action="store_true") - parser.add_argument("--debug", action="store_true") - start_time = time.time() - args, rest = parser.parse_known_args() - generate_kwargs = parse_numeric_n_bool_cl_kwargs(rest) - if generate_kwargs and args.local_rank <= 0: - print(f"parsed the following generate kwargs: {generate_kwargs}") - json_save_dir = Path(args.save_dir + "_tmp") - Path(json_save_dir).mkdir(exist_ok=True) # this handles locking. - intermediate_files = list(json_save_dir.glob("rank_*.json")) - if intermediate_files: - raise ValueError(f"Found files at {json_save_dir} please move or remove them.") - # In theory, a node could finish and save before another node hits this. If this happens, we can address later. - dataset_kwargs = {} - if args.src_lang is not None: - dataset_kwargs["src_lang"] = args.src_lang - if args.tgt_lang is not None: - dataset_kwargs["tgt_lang"] = args.tgt_lang - - Path(args.save_dir).mkdir(exist_ok=True) - results, num_replicas = eval_data_dir( - args.data_dir, - json_save_dir, - args.model_name, - type_path=args.type_path, - bs=args.bs, - fp16=args.fp16, - task=args.task, - local_rank=args.local_rank, - n_obs=args.n_obs, - max_source_length=args.max_source_length, - num_return_sequences=args.num_return_sequences, - prefix=args.prefix, - dataset_kwargs=dataset_kwargs, - **generate_kwargs, - ) - - if args.local_rank <= 0: - save_dir = Path(args.save_dir) - save_dir.mkdir(exist_ok=True) - partial_results = gather_results_from_each_node(num_replicas, json_save_dir, args.sync_timeout) - preds = combine_partial_results(partial_results) - if args.num_return_sequences > 1: - save_path = save_dir.joinpath("pseudolabel_results.json") - print(f"Saving aggregated results at {save_path}, intermediate in {json_save_dir}/") - save_json(preds, save_path) - return - tgt_file = Path(args.data_dir).joinpath(args.type_path + ".target") - labels = [x.rstrip() for x in open(tgt_file).readlines()][: len(preds)] - - # Calculate metrics, save metrics, and save _generations.txt - calc_bleu = "translation" in args.task - score_fn = calculate_bleu if calc_bleu else calculate_rouge - metric_name = "bleu" if calc_bleu else "rouge" - metrics: Dict = score_fn(preds, labels) - metrics["n_obs"] = len(preds) - runtime = time.time() - start_time - metrics["seconds_per_sample"] = round(runtime / metrics["n_obs"], 4) - metrics["n_gpus"] = num_replicas - # TODO(@stas00): add whatever metadata to metrics - metrics_save_path = save_dir.joinpath(f"{args.type_path}_{metric_name}.json") - save_json(metrics, metrics_save_path, indent=None) - print(metrics) - write_txt_file(preds, save_dir.joinpath(f"{args.type_path}_generations.txt")) - if args.debug: - write_txt_file(labels, save_dir.joinpath(f"{args.type_path}.target")) - else: - shutil.rmtree(json_save_dir) - - -def combine_partial_results(partial_results) -> List: - """Concatenate partial results into one file, then sort it by id.""" - records = [] - for partial_result in partial_results: - records.extend(partial_result) - records = list(sorted(records, key=lambda x: x["id"])) - preds = [x["pred"] for x in records] - return preds - - -def gather_results_from_each_node(num_replicas, save_dir, timeout) -> List[Dict[str, List]]: - # WAIT FOR lots of .json files - start_wait = time.time() - logger.info("waiting for all nodes to finish") - json_data = None - while (time.time() - start_wait) < timeout: - json_files = list(save_dir.glob("rank_*.json")) - if len(json_files) < num_replicas: - continue - try: - # make sure all json files are fully saved - json_data = lmap(load_json, json_files) - return json_data - except JSONDecodeError: - continue - else: - raise TimeoutError("Rank 0 gave up on waiting for other processes") - # Unreachable - - -if __name__ == "__main__": - # Usage for MT: - run_generate() diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/run_eval.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/run_eval.py deleted file mode 100755 index e21f57c1c60..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/run_eval.py +++ /dev/null @@ -1,182 +0,0 @@ -#!/usr/bin/env python -# Copyright 2020 The HuggingFace Team. 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 argparse -import datetime -import json -import time -import warnings -from logging import getLogger -from pathlib import Path -from typing import Dict, List - -import torch -from tqdm import tqdm - -from transformers import AutoModelForSeq2SeqLM, AutoTokenizer -from utils import calculate_bleu, calculate_rouge, chunks, parse_numeric_n_bool_cl_kwargs, use_task_specific_params - - -logger = getLogger(__name__) - - -DEFAULT_DEVICE = "cuda" if torch.cuda.is_available() else "cpu" - - -def generate_summaries_or_translations( - examples: List[str], - out_file: str, - model_name: str, - batch_size: int = 8, - device: str = DEFAULT_DEVICE, - fp16=False, - task="summarization", - prefix=None, - **generate_kwargs, -) -> Dict: - """Save model.generate results to , and return how long it took.""" - fout = Path(out_file).open("w", encoding="utf-8") - model_name = str(model_name) - model = AutoModelForSeq2SeqLM.from_pretrained(model_name).to(device) - if fp16: - model = model.half() - - tokenizer = AutoTokenizer.from_pretrained(model_name) - logger.info(f"Inferred tokenizer type: {tokenizer.__class__}") # if this is wrong, check config.model_type. - - start_time = time.time() - # update config with task specific params - use_task_specific_params(model, task) - if prefix is None: - prefix = prefix or getattr(model.config, "prefix", "") or "" - for examples_chunk in tqdm(list(chunks(examples, batch_size))): - examples_chunk = [prefix + text for text in examples_chunk] - batch = tokenizer(examples_chunk, return_tensors="pt", truncation=True, padding="longest").to(device) - summaries = model.generate( - input_ids=batch.input_ids, - attention_mask=batch.attention_mask, - **generate_kwargs, - ) - dec = tokenizer.batch_decode(summaries, skip_special_tokens=True, clean_up_tokenization_spaces=False) - for hypothesis in dec: - fout.write(hypothesis + "\n") - fout.flush() - fout.close() - runtime = int(time.time() - start_time) # seconds - n_obs = len(examples) - return dict(n_obs=n_obs, runtime=runtime, seconds_per_sample=round(runtime / n_obs, 4)) - - -def datetime_now(): - return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - - -def run_generate(verbose=True): - """ - - Takes input text, generates output, and then using reference calculates the BLEU scores. - - The results are saved to a file and returned to the caller, and printed out unless ``verbose=False`` is passed. - - Args: - verbose (:obj:`bool`, `optional`, defaults to :obj:`True`): print results to stdout - - Returns: - a tuple: ``(scores, params}`` - - ``scores``: a dict of scores data ``{'bleu': 39.6501, 'n_obs': 2000, 'runtime': 186, 'seconds_per_sample': 0.093}`` - - ``params``: a dict of custom params, e.g. ``{'num_beams': 5, 'length_penalty': 0.8}`` - """ - - parser = argparse.ArgumentParser() - parser.add_argument("model_name", type=str, help="like facebook/bart-large-cnn,t5-base, etc.") - parser.add_argument("input_path", type=str, help="like cnn_dm/test.source") - parser.add_argument("save_path", type=str, help="where to save summaries") - parser.add_argument("--reference_path", type=str, required=False, help="like cnn_dm/test.target") - parser.add_argument("--score_path", type=str, required=False, default="metrics.json", help="where to save metrics") - parser.add_argument("--device", type=str, required=False, default=DEFAULT_DEVICE, help="cuda, cuda:1, cpu etc.") - parser.add_argument( - "--prefix", type=str, required=False, default=None, help="will be added to the begininng of src examples" - ) - parser.add_argument("--task", type=str, default="summarization", help="used for task_specific_params + metrics") - parser.add_argument("--bs", type=int, default=8, required=False, help="batch size") - parser.add_argument( - "--n_obs", type=int, default=-1, required=False, help="How many observations. Defaults to all." - ) - parser.add_argument("--fp16", action="store_true") - parser.add_argument("--dump-args", action="store_true", help="print the custom hparams with the results") - parser.add_argument( - "--info", - nargs="?", - type=str, - const=datetime_now(), - help="use in conjunction w/ --dump-args to print with the results whatever other info you'd like, e.g. lang=en-ru. If no value is passed, the current datetime string will be used.", - ) - # Unspecified args like --num_beams=2 --decoder_start_token_id=4 are passed to model.generate - args, rest = parser.parse_known_args() - parsed_args = parse_numeric_n_bool_cl_kwargs(rest) - if parsed_args and verbose: - print(f"parsed the following generate kwargs: {parsed_args}") - examples = [" " + x.rstrip() if "t5" in args.model_name else x.rstrip() for x in open(args.input_path).readlines()] - if args.n_obs > 0: - examples = examples[: args.n_obs] - Path(args.save_path).parent.mkdir(exist_ok=True) - - if args.reference_path is None and Path(args.score_path).exists(): - warnings.warn(f"score_path {args.score_path} will be overwritten unless you type ctrl-c.") - - if args.device == "cpu" and args.fp16: - # this mix leads to RuntimeError: "threshold_cpu" not implemented for 'Half' - raise ValueError("Can't mix --fp16 and --device cpu") - - runtime_metrics = generate_summaries_or_translations( - examples, - args.save_path, - args.model_name, - batch_size=args.bs, - device=args.device, - fp16=args.fp16, - task=args.task, - prefix=args.prefix, - **parsed_args, - ) - - if args.reference_path is None: - return {} - - # Compute scores - score_fn = calculate_bleu if "translation" in args.task else calculate_rouge - output_lns = [x.rstrip() for x in open(args.save_path).readlines()] - reference_lns = [x.rstrip() for x in open(args.reference_path).readlines()][: len(output_lns)] - scores: dict = score_fn(output_lns, reference_lns) - scores.update(runtime_metrics) - - if args.dump_args: - scores.update(parsed_args) - if args.info: - scores["info"] = args.info - - if verbose: - print(scores) - - if args.score_path is not None: - json.dump(scores, open(args.score_path, "w")) - - return scores - - -if __name__ == "__main__": - # Usage for MT: - # python run_eval.py MODEL_NAME $DATA_DIR/test.source $save_dir/test_translations.txt --reference_path $DATA_DIR/test.target --score_path $save_dir/test_bleu.json --task translation $@ - run_generate(verbose=True) diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/run_eval_search.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/run_eval_search.py deleted file mode 100755 index f7b3bda0f54..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/run_eval_search.py +++ /dev/null @@ -1,151 +0,0 @@ -#!/usr/bin/env python -# Copyright 2020 The HuggingFace Team. 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 argparse -import itertools -import operator -import sys -from collections import OrderedDict - -from run_eval import datetime_now, run_generate -from utils import ROUGE_KEYS - - -# A table of supported tasks and the list of scores in the order of importance to be sorted by. -# To add a new task, simply list the score names that `run_eval.run_generate()` returns -task_score_names = { - "translation": ["bleu"], - "summarization": ROUGE_KEYS, -} - - -def parse_search_arg(search): - groups = search.split() - entries = {k: vs for k, vs in (g.split("=") for g in groups)} - entry_names = list(entries.keys()) - sets = [list((f"--{k} {v}") for v in vs.split(":")) for k, vs in entries.items()] - matrix = [list(x) for x in itertools.product(*sets)] - return matrix, entry_names - - -def run_search(): - """ - Run parametric search over the desired hparam space with help of ``run_eval.py``. - - All the arguments except ``--search`` are passed to ``run_eval.py`` as is. The values inside of "--search" are parsed, reformatted and fed to ``run_eval.py`` as additional args. - - The format for the ``--search`` value is a simple string with hparams and colon separated values to try, e.g.: - ``` - --search "num_beams=5:10 length_penalty=0.8:1.0:1.2 early_stopping=true:false" - ``` - which will generate ``12`` ``(2*3*2)`` searches for a product of each hparam. For example the example that was just used will invoke ``run_eval.py`` repeatedly with: - - ``` - --num_beams 5 --length_penalty 0.8 --early_stopping true - --num_beams 5 --length_penalty 0.8 --early_stopping false - [...] - --num_beams 10 --length_penalty 1.2 --early_stopping false - ``` - - On completion, this function prints a markdown table of the results sorted by the best BLEU score and the winning arguments. - - - """ - prog = sys.argv[0] - - parser = argparse.ArgumentParser( - usage="\n\nImportant: this script accepts all arguments `run_eval.py` accepts and then a few extra, therefore refer to `run_eval.py -h` for the complete list." - ) - parser.add_argument( - "--search", - type=str, - required=False, - help='param space to search, e.g. "num_beams=5:10 length_penalty=0.8:1.0:1.2"', - ) - parser.add_argument( - "--bs", type=int, default=8, required=False, help="initial batch size (may get reduced if it's too big)" - ) - parser.add_argument("--task", type=str, help="used for task_specific_params + metrics") - parser.add_argument( - "--info", - nargs="?", - type=str, - const=datetime_now(), - help="add custom notes to be printed before the results table. If no value is passed, the current datetime string will be used.", - ) - args, args_main = parser.parse_known_args() - # we share some of the args - args_main.extend(["--task", args.task]) - args_normal = [prog] + args_main - - # to support variations like translation_en_to_de" - task = "translation" if "translation" in args.task else "summarization" - - matrix, col_names = parse_search_arg(args.search) - col_names[0:0] = task_score_names[task] # score cols first - col_widths = {col: len(str(col)) for col in col_names} - results = [] - for r in matrix: - hparams = {k: v for k, v in (x.replace("--", "").split() for x in r)} - args_exp = " ".join(r).split() - args_exp.extend(["--bs", str(args.bs)]) # in case we need to reduce its size due to CUDA OOM - sys.argv = args_normal + args_exp - - # XXX: need to trap CUDA OOM and lower args.bs if that happens and retry - - scores = run_generate(verbose=False) - # make sure scores are first in the table - result = OrderedDict() - for score in task_score_names[task]: - result[score] = scores[score] - result.update(hparams) - results.append(result) - - # find widest entries - for k, v in result.items(): - l = len(str(v)) - if l > col_widths[k]: - col_widths[k] = l - - results_sorted = sorted(results, key=operator.itemgetter(*task_score_names[task]), reverse=True) - print(" | ".join([f"{col:{col_widths[col]}}" for col in col_names])) - print(" | ".join([f"{'-'*col_widths[col]}" for col in col_names])) - for row in results_sorted: - print(" | ".join([f"{row[col]:{col_widths[col]}}" for col in col_names])) - - best = results_sorted[0] - for score in task_score_names[task]: - del best[score] - best_args = [f"--{k} {v}" for k, v in best.items()] - dyn_args = ["--bs", str(args.bs)] - if args.info: - print(f"\nInfo: {args.info}") - print("\nBest score args:") - print(" ".join(args_main + best_args + dyn_args)) - - return results_sorted - - -if __name__ == "__main__": - # Usage: - # [normal-run_eval_search.py cmd plus] \ - # --search="num_beams=1:5:10 length_penalty=0.8:1:1.2 early_stopping=true:false" - # - # Example: - # PYTHONPATH="src:examples/seq2seq" python examples/seq2seq/run_eval_search.py $MODEL_NAME \ - # $DATA_DIR/val.source $SAVE_DIR/test_translations.txt --reference_path $DATA_DIR/val.target \ - # --score_path $SAVE_DIR/test_bleu.json --bs $BS --task translation \ - # --search="num_beams=1:5:10 length_penalty=0.8:1:1.2 early_stopping=true:false" - run_search() diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/save_len_file.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/save_len_file.py deleted file mode 100755 index 9e73b59e7e5..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/save_len_file.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python -# Copyright 2020 The HuggingFace Team. 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 fire -from torch.utils.data import DataLoader -from tqdm import tqdm - -from transformers import AutoTokenizer -from utils import Seq2SeqDataset, pickle_save - - -def save_len_file( - tokenizer_name, data_dir, max_source_length=1024, max_target_length=1024, consider_target=False, **kwargs -): - """Save max(src_len, tgt_len) for each example to allow dynamic batching.""" - tok = AutoTokenizer.from_pretrained(tokenizer_name) - train_ds = Seq2SeqDataset(tok, data_dir, max_source_length, max_target_length, type_path="train", **kwargs) - pad = tok.pad_token_id - - def get_lens(ds): - dl = tqdm( - DataLoader(ds, batch_size=512, num_workers=8, shuffle=False, collate_fn=ds.collate_fn), - desc=str(ds.len_file), - ) - max_lens = [] - for batch in dl: - src_lens = batch["input_ids"].ne(pad).sum(1).tolist() - tgt_lens = batch["labels"].ne(pad).sum(1).tolist() - if consider_target: - for src, tgt in zip(src_lens, tgt_lens): - max_lens.append(max(src, tgt)) - else: - max_lens.extend(src_lens) - return max_lens - - train_lens = get_lens(train_ds) - val_ds = Seq2SeqDataset(tok, data_dir, max_source_length, max_target_length, type_path="val", **kwargs) - val_lens = get_lens(val_ds) - pickle_save(train_lens, train_ds.len_file) - pickle_save(val_lens, val_ds.len_file) - - -if __name__ == "__main__": - fire.Fire(save_len_file) diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/save_randomly_initialized_model.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/save_randomly_initialized_model.py deleted file mode 100755 index 1b7b17fde8d..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/save_randomly_initialized_model.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python -# Copyright 2020 The HuggingFace Team. 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 fire - -from transformers import AutoConfig, AutoModelForSeq2SeqLM, AutoTokenizer - - -def save_randomly_initialized_version(config_name: str, save_dir: str, **config_kwargs): - """Save a randomly initialized version of a model using a pretrained config. - Args: - config_name: which config to use - save_dir: where to save the resulting model and tokenizer - config_kwargs: Passed to AutoConfig - - Usage:: - save_randomly_initialized_version("facebook/bart-large-cnn", "distilbart_random_cnn_6_3", encoder_layers=6, decoder_layers=3, num_beams=3) - """ - cfg = AutoConfig.from_pretrained(config_name, **config_kwargs) - model = AutoModelForSeq2SeqLM.from_config(cfg) - model.save_pretrained(save_dir) - AutoTokenizer.from_pretrained(config_name).save_pretrained(save_dir) - return model - - -if __name__ == "__main__": - fire.Fire(save_randomly_initialized_version) diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/sentence_splitter.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/sentence_splitter.py deleted file mode 100644 index 54a07967efa..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/sentence_splitter.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2020 The HuggingFace Team. 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 re - -from filelock import FileLock - - -try: - import nltk - - NLTK_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - NLTK_AVAILABLE = False - -if NLTK_AVAILABLE: - with FileLock(".lock") as lock: - nltk.download("punkt", quiet=True) - - -def add_newline_to_end_of_each_sentence(x: str) -> str: - """This was added to get rougeLsum scores matching published rougeL scores for BART and PEGASUS.""" - re.sub("", "", x) # remove pegasus newline char - assert NLTK_AVAILABLE, "nltk must be installed to separate newlines between sentences. (pip install nltk)" - return "\n".join(nltk.sent_tokenize(x)) diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/seq2seq_trainer.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/seq2seq_trainer.py deleted file mode 100644 index cba3e958e9c..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/seq2seq_trainer.py +++ /dev/null @@ -1,258 +0,0 @@ -# Copyright 2020 The HuggingFace Team. 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 typing import Any, Dict, List, Optional, Tuple, Union - -import torch -from torch import nn -from torch.utils.data import DistributedSampler, RandomSampler - -from transformers import PreTrainedModel, Trainer, logging -from transformers.file_utils import is_torch_tpu_available -from transformers.integrations import is_fairscale_available -from transformers.models.fsmt.configuration_fsmt import FSMTConfig -from transformers.optimization import ( - Adafactor, - AdamW, - get_constant_schedule, - get_constant_schedule_with_warmup, - get_cosine_schedule_with_warmup, - get_cosine_with_hard_restarts_schedule_with_warmup, - get_linear_schedule_with_warmup, - get_polynomial_decay_schedule_with_warmup, -) -from transformers.trainer_pt_utils import get_tpu_sampler -from transformers.training_args import ParallelMode - - -if is_fairscale_available(): - from fairscale.optim import OSS - - -logger = logging.get_logger(__name__) - -arg_to_scheduler = { - "linear": get_linear_schedule_with_warmup, - "cosine": get_cosine_schedule_with_warmup, - "cosine_w_restarts": get_cosine_with_hard_restarts_schedule_with_warmup, - "polynomial": get_polynomial_decay_schedule_with_warmup, - "constant": get_constant_schedule, - "constant_w_warmup": get_constant_schedule_with_warmup, -} - - -class Seq2SeqTrainer(Trainer): - def __init__(self, config=None, data_args=None, *args, **kwargs): - super().__init__(*args, **kwargs) - - if config is None: - assert isinstance( - self.model, PreTrainedModel - ), f"If no `config` is passed the model to be trained has to be of type `PreTrainedModel`, but is {self.model.__class__}" - self.config = self.model.config - else: - self.config = config - - self.data_args = data_args - self.vocab_size = self.config.tgt_vocab_size if isinstance(self.config, FSMTConfig) else self.config.vocab_size - - if self.args.label_smoothing != 0 or (self.data_args is not None and self.data_args.ignore_pad_token_for_loss): - assert ( - self.config.pad_token_id is not None - ), "Make sure that `config.pad_token_id` is correcly defined when ignoring `pad_token` for loss calculation or doing label smoothing." - - if self.config.pad_token_id is None and self.config.eos_token_id is not None: - logger.warn( - f"The `config.pad_token_id` is `None`. Using `config.eos_token_id` = {self.config.eos_token_id} for padding.." - ) - - if self.args.label_smoothing == 0: - self.loss_fn = torch.nn.CrossEntropyLoss(ignore_index=self.config.pad_token_id) - else: - # dynamically import label_smoothed_nll_loss - from utils import label_smoothed_nll_loss - - self.loss_fn = label_smoothed_nll_loss - - def create_optimizer_and_scheduler(self, num_training_steps: int): - """ - Setup the optimizer and the learning rate scheduler. - - We provide a reasonable default that works well. If you want to use something else, you can pass a tuple in the - Trainer's init through :obj:`optimizers`, or subclass and override this method in a subclass. - """ - if self.optimizer is None: - no_decay = ["bias", "LayerNorm.weight"] - optimizer_grouped_parameters = [ - { - "params": [p for n, p in self.model.named_parameters() if not any(nd in n for nd in no_decay)], - "weight_decay": self.args.weight_decay, - }, - { - "params": [p for n, p in self.model.named_parameters() if any(nd in n for nd in no_decay)], - "weight_decay": 0.0, - }, - ] - optimizer_cls = Adafactor if self.args.adafactor else AdamW - if self.args.adafactor: - optimizer_cls = Adafactor - optimizer_kwargs = {"scale_parameter": False, "relative_step": False} - else: - optimizer_cls = AdamW - optimizer_kwargs = { - "betas": (self.args.adam_beta1, self.args.adam_beta2), - "eps": self.args.adam_epsilon, - } - optimizer_kwargs["lr"] = self.args.learning_rate - if self.sharded_dpp: - self.optimizer = OSS( - params=optimizer_grouped_parameters, - optim=optimizer_cls, - **optimizer_kwargs, - ) - else: - self.optimizer = optimizer_cls(optimizer_grouped_parameters, **optimizer_kwargs) - - if self.lr_scheduler is None: - self.lr_scheduler = self._get_lr_scheduler(num_training_steps) - else: # ignoring --lr_scheduler - logger.warn("scheduler is passed to `Seq2SeqTrainer`, `--lr_scheduler` arg is ignored.") - - def _get_lr_scheduler(self, num_training_steps): - schedule_func = arg_to_scheduler[self.args.lr_scheduler] - if self.args.lr_scheduler == "constant": - scheduler = schedule_func(self.optimizer) - elif self.args.lr_scheduler == "constant_w_warmup": - scheduler = schedule_func(self.optimizer, num_warmup_steps=self.args.warmup_steps) - else: - scheduler = schedule_func( - self.optimizer, num_warmup_steps=self.args.warmup_steps, num_training_steps=num_training_steps - ) - return scheduler - - def _get_train_sampler(self) -> Optional[torch.utils.data.sampler.Sampler]: - if isinstance(self.train_dataset, torch.utils.data.IterableDataset): - return None - elif is_torch_tpu_available(): - return get_tpu_sampler(self.train_dataset) - else: - if self.args.sortish_sampler: - self.train_dataset.make_sortish_sampler( - self.args.per_device_train_batch_size, - distributed=(self.args.parallel_mode == ParallelMode.DISTRIBUTED), - ) - - return ( - RandomSampler(self.train_dataset) - if self.args.local_rank == -1 - else DistributedSampler(self.train_dataset) - ) - - def _compute_loss(self, model, inputs, labels): - if self.args.label_smoothing == 0: - if self.data_args is not None and self.data_args.ignore_pad_token_for_loss: - # force training to ignore pad token - logits = model(**inputs, use_cache=False)[0] - loss = self.loss_fn(logits.view(-1, logits.shape[-1]), labels.view(-1)) - else: - # compute usual loss via models - loss, logits = model(**inputs, labels=labels, use_cache=False)[:2] - else: - # compute label smoothed loss - logits = model(**inputs, use_cache=False)[0] - lprobs = torch.nn.functional.log_softmax(logits, dim=-1) - loss, _ = self.loss_fn(lprobs, labels, self.args.label_smoothing, ignore_index=self.config.pad_token_id) - return loss, logits - - def compute_loss(self, model, inputs): - labels = inputs.pop("labels") - loss, _ = self._compute_loss(model, inputs, labels) - return loss - - def prediction_step( - self, - model: nn.Module, - inputs: Dict[str, Union[torch.Tensor, Any]], - prediction_loss_only: bool, - ignore_keys: Optional[List[str]] = None, - ) -> Tuple[Optional[float], Optional[torch.Tensor], Optional[torch.Tensor]]: - """ - Perform an evaluation step on :obj:`model` using obj:`inputs`. - - Subclass and override to inject custom behavior. - - Args: - model (:obj:`nn.Module`): - The model to evaluate. - inputs (:obj:`Dict[str, Union[torch.Tensor, Any]]`): - The inputs and targets of the model. - - The dictionary will be unpacked before being fed to the model. Most models expect the targets under the - argument :obj:`labels`. Check your model's documentation for all accepted arguments. - prediction_loss_only (:obj:`bool`): - Whether or not to return the loss only. - - Return: - Tuple[Optional[float], Optional[torch.Tensor], Optional[torch.Tensor]]: - A tuple with the loss, logits and labels (each being optional). - """ - inputs = self._prepare_inputs(inputs) - - gen_kwargs = { - "max_length": self.data_args.val_max_target_length - if self.data_args is not None - else self.config.max_length, - "num_beams": self.data_args.eval_beams if self.data_args is not None else self.config.num_beams, - } - - if self.args.predict_with_generate and not self.args.prediction_loss_only: - generated_tokens = self.model.generate( - inputs["input_ids"], - attention_mask=inputs["attention_mask"], - **gen_kwargs, - ) - # in case the batch is shorter than max length, the output should be padded - if generated_tokens.shape[-1] < gen_kwargs["max_length"]: - generated_tokens = self._pad_tensors_to_max_len(generated_tokens, gen_kwargs["max_length"]) - - labels = inputs.pop("labels") - with torch.no_grad(): - # compute loss on predict data - loss, logits = self._compute_loss(model, inputs, labels) - - loss = loss.mean().detach() - if self.args.prediction_loss_only: - return (loss, None, None) - - logits = generated_tokens if self.args.predict_with_generate else logits - - if labels.shape[-1] < gen_kwargs["max_length"]: - labels = self._pad_tensors_to_max_len(labels, gen_kwargs["max_length"]) - - return (loss, logits, labels) - - def _pad_tensors_to_max_len(self, tensor, max_length): - # If PAD token is not defined at least EOS token has to be defined - pad_token_id = self.config.pad_token_id if self.config.pad_token_id is not None else self.config.eos_token_id - - if pad_token_id is None: - raise ValueError( - f"Make sure that either `config.pad_token_id` or `config.eos_token_id` is defined if tensor has to be padded to `max_length`={max_length}" - ) - - padded_tensor = pad_token_id * torch.ones( - (tensor.shape[0], max_length), dtype=tensor.dtype, device=tensor.device - ) - padded_tensor[:, : tensor.shape[-1]] = tensor - return padded_tensor diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/seq2seq_training_args.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/seq2seq_training_args.py deleted file mode 100644 index 6ec220181ad..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/seq2seq_training_args.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2020 The HuggingFace Team. 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 -from dataclasses import dataclass, field -from typing import Optional - -from seq2seq_trainer import arg_to_scheduler -from transformers import TrainingArguments - - -logger = logging.getLogger(__name__) - - -@dataclass -class Seq2SeqTrainingArguments(TrainingArguments): - """ - Parameters: - label_smoothing (:obj:`float`, `optional`, defaults to 0): - The label smoothing epsilon to apply (if not zero). - sortish_sampler (:obj:`bool`, `optional`, defaults to :obj:`False`): - Whether to SortishSamler or not. It sorts the inputs according to lenghts in-order to minimizing the padding size. - predict_with_generate (:obj:`bool`, `optional`, defaults to :obj:`False`): - Whether to use generate to calculate generative metrics (ROUGE, BLEU). - """ - - label_smoothing: Optional[float] = field( - default=0.0, metadata={"help": "The label smoothing epsilon to apply (if not zero)."} - ) - sortish_sampler: bool = field(default=False, metadata={"help": "Whether to SortishSamler or not."}) - predict_with_generate: bool = field( - default=False, metadata={"help": "Whether to use generate to calculate generative metrics (ROUGE, BLEU)."} - ) - adafactor: bool = field(default=False, metadata={"help": "whether to use adafactor"}) - encoder_layerdrop: Optional[float] = field( - default=None, metadata={"help": "Encoder layer dropout probability. Goes into model.config."} - ) - decoder_layerdrop: Optional[float] = field( - default=None, metadata={"help": "Decoder layer dropout probability. Goes into model.config."} - ) - dropout: Optional[float] = field(default=None, metadata={"help": "Dropout probability. Goes into model.config."}) - attention_dropout: Optional[float] = field( - default=None, metadata={"help": "Attention dropout probability. Goes into model.config."} - ) - lr_scheduler: Optional[str] = field( - default="linear", - metadata={"help": f"Which lr scheduler to use. Selected in {sorted(arg_to_scheduler.keys())}"}, - ) diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/fsmt/build-eval-data.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/fsmt/build-eval-data.py deleted file mode 100755 index 46487c07ea8..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/fsmt/build-eval-data.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python - -import io -import json -import subprocess - - -pairs = [ - ["en", "ru"], - ["ru", "en"], - ["en", "de"], - ["de", "en"], -] - -n_objs = 8 - - -def get_all_data(pairs, n_objs): - text = {} - for src, tgt in pairs: - pair = f"{src}-{tgt}" - cmd = f"sacrebleu -t wmt19 -l {pair} --echo src".split() - src_lines = subprocess.run(cmd, stdout=subprocess.PIPE).stdout.decode("utf-8").splitlines() - cmd = f"sacrebleu -t wmt19 -l {pair} --echo ref".split() - tgt_lines = subprocess.run(cmd, stdout=subprocess.PIPE).stdout.decode("utf-8").splitlines() - text[pair] = {"src": src_lines[:n_objs], "tgt": tgt_lines[:n_objs]} - return text - - -text = get_all_data(pairs, n_objs) -filename = "./fsmt_val_data.json" -with io.open(filename, "w", encoding="utf-8") as f: - bleu_data = json.dump(text, f, indent=2, ensure_ascii=False) diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/fsmt/fsmt_val_data.json b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/fsmt/fsmt_val_data.json deleted file mode 100644 index f38b3057333..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/fsmt/fsmt_val_data.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "en-ru": { - "src": [ - "Welsh AMs worried about 'looking like muppets'", - "There is consternation among some AMs at a suggestion their title should change to MWPs (Member of the Welsh Parliament).", - "It has arisen because of plans to change the name of the assembly to the Welsh Parliament.", - "AMs across the political spectrum are worried it could invite ridicule.", - "One Labour AM said his group was concerned \"it rhymes with Twp and Pwp.\"", - "For readers outside of Wales: In Welsh twp means daft and pwp means poo.", - "A Plaid AM said the group as a whole was \"not happy\" and has suggested alternatives.", - "A Welsh Conservative said his group was \"open minded\" about the name change, but noted it was a short verbal hop from MWP to Muppet." - ], - "tgt": [ - "Члены Национальной ассамблеи Уэльса обеспокоены, что \"выглядят как куклы\"", - "Некоторые члены Национальной ассамблеи Уэльса в ужасе от предложения о том, что их наименование должно измениться на MPW (члены Парламента Уэльса).", - "Этот вопрос был поднят в связи с планами по переименованию ассамблеи в Парламент Уэльса.", - "Члены Национальной ассамблеи Уэльса всего политического спектра обеспокоены, что это может породить насмешки.", - "Один из лейбористских членов Национальной ассамблеи Уэльса сказал, что его партия обеспокоена тем, что \"это рифмуется с Twp и Pwp\".", - "Для читателей за предлами Уэльса: по-валлийски twp означает \"глупый\", а pwp означает \"какашка\".", - "Член Национальной ассамблеи от Плайд сказал, что эта партия в целом \"не счастлива\" и предложил альтернативы.", - "Представитель Консервативной партии Уэльса сказал, что его партия \"открыта\" к переименованию, но отметил, что между WMP и Muppet небольшая разница в произношении." - ] - }, - "ru-en": { - "src": [ - "Названо число готовящихся к отправке в Донбасс новобранцев из Украины", - "Официальный представитель Народной милиции самопровозглашенной Луганской Народной Республики (ЛНР) Андрей Марочко заявил, что зимой 2018-2019 года Украина направит в Донбасс не менее 3 тыс. новобранцев.", - "По его словам, таким образом Киев планирует \"хоть как-то доукомплектовать подразделения\".", - "\"Нежелание граждан Украины проходить службу в рядах ВС Украины, массовые увольнения привели к низкой укомплектованности подразделений\", - рассказал Марочко, которого цитирует \"РИА Новости\".", - "Он также не исключил, что реальные цифры призванных в армию украинцев могут быть увеличены в случае необходимости.", - "В 2014-2017 годах Киев начал так называемую антитеррористическую операцию (АТО), которую позже сменили на операцию объединенных сил (ООС).", - "Предполагалось, что эта мера приведет к усилению роли украинских силовиков в урегулировании ситуации.", - "В конце августа 2018 года ситуация в Донбассе обострилась из-за убийства главы ДНР Александра Захарченко." - ], - "tgt": [ - "The number of new Ukrainian recruits ready to go to Donbass has become public", - "Official representative of the peoples’ militia of the self-proclaimed Lugansk People’s Republic Andrey Marochko claimed that Ukrainian will send at least 3 thousand new recruits to Donbass in winter 2018-2019.", - "This is how Kyiv tries “at least somehow to staff the units,” he said.", - "“The unwillingness of Ukrainian citizens to serve in the Ukraine’s military forces, mass resignments lead to low understaffing,” said Marochko cited by RIA Novosti.", - "Also, he doesn’t exclude that the real numbers of conscripts in the Ukrainian army can be raised is necessary.", - "In 2014-2017, Kyiv started so-called antiterrorist operation, that ws later changed to the united forces operation.", - "This measure was supposed to strengthen the role of the Ukrainian military in settling the situation.", - "In the late August 2018, the situation in Donbass escalated as the DNR head Aleksandr Zakharchenko was killed." - ] - }, - "en-de": { - "src": [ - "Welsh AMs worried about 'looking like muppets'", - "There is consternation among some AMs at a suggestion their title should change to MWPs (Member of the Welsh Parliament).", - "It has arisen because of plans to change the name of the assembly to the Welsh Parliament.", - "AMs across the political spectrum are worried it could invite ridicule.", - "One Labour AM said his group was concerned \"it rhymes with Twp and Pwp.\"", - "For readers outside of Wales: In Welsh twp means daft and pwp means poo.", - "A Plaid AM said the group as a whole was \"not happy\" and has suggested alternatives.", - "A Welsh Conservative said his group was \"open minded\" about the name change, but noted it was a short verbal hop from MWP to Muppet." - ], - "tgt": [ - "Walisische Ageordnete sorgen sich \"wie Dödel auszusehen\"", - "Es herrscht Bestürzung unter einigen Mitgliedern der Versammlung über einen Vorschlag, der ihren Titel zu MWPs (Mitglied der walisischen Parlament) ändern soll.", - "Der Grund dafür waren Pläne, den Namen der Nationalversammlung in Walisisches Parlament zu ändern.", - "Mitglieder aller Parteien der Nationalversammlung haben Bedenken, dass sie sich dadurch Spott aussetzen könnten.", - "Ein Labour-Abgeordneter sagte, dass seine Gruppe \"sich mit Twp und Pwp reimt\".", - "Hinweis für den Leser: „twp“ im Walisischen bedeutet „bescheuert“ und „pwp“ bedeutet „Kacke“.", - "Ein Versammlungsmitglied von Plaid Cymru sagte, die Gruppe als Ganzes sei \"nicht glücklich\" und hat Alternativen vorgeschlagen.", - "Ein walisischer Konservativer sagte, seine Gruppe wäre „offen“ für eine Namensänderung, wies aber darauf hin, dass es von „MWP“ (Mitglied des Walisischen Parlaments) nur ein kurzer verbaler Sprung zu „Muppet“ ist." - ] - }, - "de-en": { - "src": [ - "Schöne Münchnerin 2018: Schöne Münchnerin 2018 in Hvar: Neun Dates", - "Von az, aktualisiert am 04.05.2018 um 11:11", - "Ja, sie will...", - "\"Schöne Münchnerin\" 2018 werden!", - "Am Nachmittag wartet erneut eine Überraschung auf unsere Kandidatinnen: sie werden das romantische Candlelight-Shooting vor der MY SOLARIS nicht alleine bestreiten, sondern an der Seite von Male-Model Fabian!", - "Hvar - Flirten, kokettieren, verführen - keine einfachen Aufgaben für unsere Mädchen.", - "Insbesondere dann, wenn in Deutschland ein Freund wartet.", - "Dennoch liefern die neun \"Schöne Münchnerin\"-Kandidatinnen beim Shooting mit People-Fotograf Tuan ab und trotzen Wind, Gischt und Regen wie echte Profis." - ], - "tgt": [ - "The Beauty of Munich 2018: the Beauty of Munich 2018 in Hvar: Nine dates", - "From A-Z, updated on 04/05/2018 at 11:11", - "Yes, she wants to...", - "to become \"The Beauty of Munich\" in 2018!", - "In the afternoon there is another surprise waiting for our contestants: they will be competing for the romantic candlelight photo shoot at MY SOLARIS not alone, but together with a male-model Fabian!", - "Hvar with its flirting, coquetting, and seduction is not an easy task for our girls.", - "Especially when there is a boyfriend waiting in Germany.", - "Despite dealing with wind, sprays and rain, the nine contestants of \"The Beauty of Munich\" behaved like real professionals at the photo shoot with People-photographer Tuan." - ] - } -} \ No newline at end of file diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/test_data b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/test_data deleted file mode 120000 index 9eee112ad74..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/test_data +++ /dev/null @@ -1 +0,0 @@ -seq2seq/test_data \ No newline at end of file diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/test.source b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/test.source deleted file mode 100644 index 3eea3d95b8e..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/test.source +++ /dev/null @@ -1,20 +0,0 @@ -UN Chief Says There Is No Military Solution in Syria Secretary-General Ban Ki-moon says his response to Russia's stepped up military support for Syria is that "there is no military solution" to the nearly five-year conflict and more weapons will only worsen the violence and misery for millions of people. The U.N. chief again urged all parties, including the divided U.N. Security Council, to unite and support inclusive negotiations to find a political solution. Ban told a news conference Wednesday that he plans to meet with foreign ministers of the five permanent council nations - the U.S., Russia, China, Britain and France - on the sidelines of the General Assembly's ministerial session later this month to discuss Syria. -He expressed regret that divisions in the council and among the Syrian people and regional powers "made this situation unsolvable." Ban urged the five permanent members to show the solidarity and unity they did in achieving an Iran nuclear deal in addressing the Syria crisis. 8 Poll Numbers That Show Donald Trump Is For Real Some have tried to label him a flip-flopper. Others have dismissed him as a joke. And some are holding out for an implosion. But no matter how some Republicans are trying to drag Donald Trump down from atop the polls, it hasn't worked (yet). -Ten of the last 11 national polls have shown Donald Trump's lead at double digits, and some are starting to ask seriously what it means for the real estate mogul's nomination chances. Of course, it's still early in the election cycle. None of this is to say that Trump is likely to win the Republican nomination. Pundits point out that at this time in 2011, Rick Perry's lead was giving way to a rising Herman Cain, neither of whom won even one state in the nomination process. And there are many reasons he would struggle in a general election. But outside groups like Jeb Bush's Super PAC and the economic conservative group Club for Growth are recognizing Trump's staying power and beginning to unload their dollars to topple him. -Here are some recent poll numbers that suggest that the real estate mogul isn't just a passing phase: Trump's favorability ratings have turned 180 degrees. Right before Donald Trump announced his candidacy in mid-June, a Monmouth University poll showed only two in 10 Republicans had a positive view of the real estate mogul. By mid-July, it was 40 percent. In early August, it was 52 percent. Now, six in 10 Republicans have a favorable view of Donald Trump. Roughly three in 10 say they have a negative view. And these numbers hold up in early states. A Quinnipiac poll in Iowa last week found that 60 percent of Republicans there had a favorable view of Trump. -Two-thirds of GOP voters would be happy with Trump as the nominee. In a CNN/ORC poll last week, 67 percent of Republicans said they would be either "enthusiastic" or "satisfied" if Trump were the nominee. Only two in 10 say they would be "upset" if he were the nominee. Only Ben Carson generates roughly the same level of enthusiasm as Trump (43 percent say they would be "enthusiastic" vs. 40 percent who say the same of Trump). The next closest in enthusiasm? Marco Rubio with only 21 percent. -On the flip side, 47 percent of Republican voters say they would be "dissatisfied" or "upset" if establishment favorite Jeb Bush becomes the nominee. A majority of Republicans don't see Trump's temperament as a problem. While Donald Trump has been widely criticized for his bombast and insults, 52 percent of leaned Republican voters nationwide think that the real estate mogul has the right temperament to be president, according to Monday's ABC News/Washington Post poll. The same number holds in the first-in-the-nation caucus state of Iowa, where the same 52 percent of Republicans think he has the personality to be commander in chief, according to Quinnipiac last week. -Still, 44 percent think he doesn't have the personality to serve effectively, and almost six in 10 independents say his temperament does not belong in the White House, according to ABC/Post. Republican voters are getting used to the idea. When they put on their pundit hats, Republican voters think Trump is for real. When asked who is most likely to win the GOP nomination, four in 10 said Trump was the best bet, according to a CNN/ORC poll out last week. That's a change from when four in 10 placed their money on Jeb Bush in late July. Full disclosure: GOP voters haven't had the clearest crystal ball in the past. -At this time last cycle, four in 10 Republicans picked Rick Perry to win the nomination, vs. only 28 percent for eventual nominee Mitt Romney. Still, it shows that a plurality of GOP voters see Trump's campaign as plausible. Even if Republicans rallied around another candidate, Trump still beats almost everyone. Some pundits point out that the splintered field is likely contributing to Trump's lead, while anti-Trump support is be spread diffusely among more than a dozen other candidates. But a Monmouth University poll in early September shows that, in a hypothetical head-to-head matchup between Trump and most other Republican candidates, Trump almost always garners majority support. -He leads Carly Fiorina by 13 points, Marco Rubio by 14 points, Walker by 15 points, Jeb Bush by 19 points, and, finally, Rand Paul, John Kasich and Chris Christie by 33 points each. He's in a dead heat with Ted Cruz. The only candidate who beats him? Ben Carson would lead the businessman by a wide 19 points in a hypothetical head-to-head. A bare majority of Donald Trump's supporters say they've made up their minds. A new CBS/NYT poll out on Tuesday shows that just more than half of voters who support Trump say they have locked in their votes. Obviously, a lot can happen to change that, and no one can really say they would never change their mind. -46 percent said they are leaving the door open to switching candidates. Still, Trump's strongest competition at the moment is from fellow outsider neurosurgeon Ben Carson, but voters who say they have made up their minds are twice as likely to go for Trump. Six in 10 Republicans say they agree with Trump on immigration. Even since Donald Trump called immigrants from Mexico "rapists" in his campaign announcement speech two months ago, immigration has been front and center in the 2016 conversation. Some are worried that Trump's bombast will drive crucial Hispanic voters away from the Republican Party and damage rebranding efforts. -But according to Monday's new ABC/Post poll, six in 10 Republicans say they agree with Trump on immigration issues. So as long as immigration remains in the spotlight, it seems Donald Trump will remain too. Frustration with government is climbing to new highs. Donald Trump and Ben Carson now account for roughly half of the support from Republican voters, largely due to their outsider status. Six in 10 Republicans in Monday's new ABC/Post poll say they want a political outsider over someone with government experience. And they are angry at Washington, too. -A Des Moines Register/Bloomberg poll in Iowa from two weeks ago shows that three in four Iowa Republicans are frustrated with Republicans in Congress, with 54 percent "unsatisfied" and 21 percent "mad as hell." Jeremy Corbyn to make debut at Prime Minister's Questions Since his election, Mr Corbyn's debut at PMQs has been keenly awaited New Labour leader Jeremy Corbyn is to make his debut at Prime Minister's Questions later, taking on David Cameron for the first time. -Mr Corbyn will rise to ask the first of his six allotted questions shortly after midday, with his performance likely to be closely scrutinised by the media and Labour MPs. He has called for "less theatre and more facts" at the weekly showpiece. He has also said he could skip some sessions, leaving them to colleagues. The encounter will be the first parliamentary test of Mr Corbyn's leadership, coming after his appointment of a shadow cabinet and his speech to the TUC annual congress on Tuesday. -Meanwhile, the Labour leader's decision to stand in silence during the singing of the national anthem at a service on Tuesday to mark the 75th anniversary of the Battle of Britain has attracted criticism from a number of Tory MPs and is the focus of several front page stories in the newspapers. Mr Corbyn's decision not to sing the national anthem has attracted attention A spokesman for Mr Corbyn said he had "stood in respectful silence" and did recognise the "heroism of the Royal Air Force in the Battle of Britain." -But a member of Mr Corbyn's shadow cabinet, Owen Smith, told BBC Two's Newsnight programme he would have advised the Labour leader to sing the national anthem "irrespective" of his belief that the monarchy should be abolished. Nearly a dozen shadow ministers have refused to serve in Mr Corbyn's top team, citing differences over the economy, defence and foreign affairs, while less than a sixth of the parliamentary party originally backed him as leader. BBC political correspondent Robin Brant says policy differences are also "stacking up" within Labour following Mr Corbyn's appointment over its position on the European Union and the government's cap on benefits. -Mr Corbyn told the TUC conference Labour was putting forward amendments to remove the whole idea of a cap altogether. Hours later Mr Smith, the shadow work and pensions secretary, said the party was "very clear" that it was only opposing government plans to reduce the level of cap from £26,000 to £23,000. Mr Corbyn will be the fifth Labour leader that David Cameron has faced across the despatch box over the past decade since he became Tory leader. The Labour leader, who has promised a different approach to politics, says he has "crowd sourced" ideas for questions to ask Mr Cameron and has been given more than 30,000 suggestions. -The Islington North MP has said PMQs is too confrontational and that he will refrain from both "repartee" and trading barbs, instead vowing to focus on serious issues such as poverty, inequality and the challenges facing young people. Mr Corbyn has said that Angela Eagle, the shadow business secretary, will deputise for him at PMQs when he does not attend - for instance when Mr Cameron is travelling abroad. He has also floated the idea of allowing other colleagues to take the floor on occasion, saying he had approached the Commons Speaker John Bercow to discuss the issue. -When he became leader in 2005, Mr Cameron said he wanted to move away from the "Punch and Judy" style of politics often associated with PMQs but admitted some years later that he had failed. Since it was first televised in 1990, PMQs has been seen as a key barometer of a leader's judgement, their command of the Commons and their standing among their fellow MPs although critics have argued it has become a caricature and is in need of far-reaching reforms. 'Shot in Joburg': Homeless youth trained as photographers Downtown Johannesburg is a tough place to be homeless. -But one group of former street children have found a way to learn a skill and make a living. "I was shot in Joburg" is a non-profit studio that teaches homeless youngsters how to take photographs of their neighbourhood and make a profit from it. BBC News went to meet one of the project's first graduates. JD Sports boss says higher wages could hurt expansion JD Sports Executive Chairman Peter Cowgill says a higher minimum wage for UK workers could mean "more spending power in the pockets of potential consumers." But that spending power is unlikely to outweigh the higher labour costs at his firm, he says. -The costs could hit JD Sports' expansion plans, he added, which could mean fewer extra jobs. Thanasi Kokkinakis backed by Tennis Australia president Steve Healy Thanasi Kokkinakis deserves kudos rather than criticism for his behaviour. Thanasi Kokkinakis has been the collateral damage in the recent storm around his friend Nick Kyrgios and deserves kudos rather than criticism for his own behaviour, according to Tennis Australia president Steve Healy. \ No newline at end of file diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/test.target b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/test.target deleted file mode 100644 index 8c88fd05326..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/test.target +++ /dev/null @@ -1,20 +0,0 @@ -Șeful ONU declară că nu există soluții militare în Siria Secretarul General Ban Ki-moon afirmă că răspunsul său la suportul militar al Rusiei pentru Siria este că „nu există o soluție militară” la conflictul care durează de aproape cinci ani iar mai multe arme nu ar face decât să agraveze violența și suferința a milioane de oameni. Șeful ONU a solicitat din nou tuturor părților, inclusiv Consiliului de securitate ONU divizat să se unifice și să susțină negocierile pentru a găsi o soluție politică. Ban a declarat miercuri în cadrul unei conferințe că intenționează să se întâlnească luna aceasta cu miniștrii de externe din cinci țări permanent prezente în consiliu - SUA, Rusia, China, Anglia și Franța - pe marginea sesiunii ministeriale a Adunării Generale pentru a discuta despre Siria. -Ban și-a exprimat regretul că divizările în consiliu și între poporul sirian și puterile regionale „au făcut această situație de nerezolvat”. Ban le-a cerut celor cinci membri permanenți să dea dovadă de solidaritatea și unitatea arătate atunci când au reușit să încheie un acord referitor la armele nucleare ale Iranului, abordând astfel criza din Siria. 8 cifre din sondaje care arată că Donald Trump are șanse reale Unii au încercat să îl eticheteze ca politician „flip-flop”. Alții l-au numit o glumă. Iar alții așteaptă implozia. Însă indiferent de modul în care unii republicani încearcă să îl dărâme pe Donald Trump din vârful sondajelor, nu a funcționat (încă). -Zece din ultimele 11 sondaje naționale au arătat că Donald Trump conduce cu un procent din două cifre iar unele voci încep să se întrebe serios ce înseamnă acest lucru pentru șansele de numire ale mogulului imobiliar. Desigur, este încă prematur. Nimic din toate acestea nu spune că Trump va câștiga cursa pentru nominalizarea republicanilor. Pundits arată că, în aceeași perioadă a anului 2011, avansul lui Rick Perry îi făcea loc lui Herman Cain în sondaje, dar niciunul dintre ei nu a câștigat în vreun stat în cursa de nominalizare. Iar motivele pentru care s-ar lupta din greu la alegerile generale sunt numeroase. Însă grupurile din exterior precum Super PAC al lui Jeb Bush și grupul conservator economic Club for Growth admit puterea lui Trump și încep să îl susțină cu bani. -În continuare vă prezentăm câteva cifre din sondaje recente care sugerează că mogulul imobiliar nu este doar ceva trecător: Cifrele care indică susținerea față de Trump s-au întors la 180 grade. Chiar înainte ca Donald Trump să își anunțe candidatura, la mijlocul lui iunie, un sondaj realizat de Universitatea din Monmouth arăta că doar doi din 10 republicani aveau o părere pozitivă despre mogulul imobiliar. Până la mijlocul lui iulie, procentul a urcat la 40%. La începutul lui august, era 52%. În prezent, șase din 10 republicani au o părere favorabilă despre Donald Trump. Aproximativ trei din 10 declară că au o părere negativă. Aceste cifre se mențin. Un sondaj realizat săptămâna trecută de Quinnipiac în Iowa a concluzionat că 60% dintre republicanii din regiune au o părere favorabilă despre Trump. -Două treimi dintre alegătorii GOP ar fi fericiți dacă Trump ar câștiga cursa pentru nominalizare. Într-un sondaj realizat săptămâna trecută de CNN/ORC, 67% dintre republicani au declarat că ar fi „entuziasmați” sau „mulțumiți” dacă Trump ar câștiga cursa pentru nominalizare. Doar doi din 10 declară că ar fi „supărați” dacă Trump ar câștiga cursa pentru nominalizare. Doar Ben Carson generează aproximativ același nivel de entuziasm ca Trump (43% declară că ar fi „entuziasmați” față de 40% care declară același lucru despre Trump). Cel mai aproape în ceea ce privește entuziasmul? Marco Rubio, cu doar 21%. -De partea cealaltă, 47% dintre alegătorii republicani afirmă că ar fi „nemulțumiți” sau „supărați” dacă favoritul Jeb Bush câștigă cursa pentru nominalizare. Majoritatea republicanilor nu consideră temperamentul lui Trump o problemă. Deși Donald Trump a fost puternic criticat pentru insultele aduse și stilul său bombastic, 52% dintre alegătorii republicani la nivel național consideră că mogulul imobiliar are temperamentul potrivit pentru a fi președinte, conform sondajului realizat luni de ABC News/Washington Post. Regăsim aceleași cifre în statul Iowa, unde tot 52% dintre republicani cred că Trump are personalitatea potrivită pentru a fi conducător, conform sondajului realizat săptămâna trecută de Quinnipiac. -Totuși, 44% sunt de părere că nu are personalitatea necesară pentru a acționa eficient și aproape șase din 10 independenți afirmă că temperamentul său nu are ce căuta la Casa Albă, conform ABC/Post. Alegătorii republicani se obișnuiesc cu ideea. Atunci când iau atitudinea de intelectuali, alegătorii republicani consideră că Trump este autentic. Conform unui sondaj realizat săptămâna trecută de CNN/ORC, la întrebarea cine are cele mai multe șanse să câștige cursa pentru nominalizare GOP, patru din 10 au declarat că Trump. Situația s-a schimbat față de finalul lui iulie, când patru din 10 ar fi pariat pe Jeb Bush. Informare completă: în trecut, alegătorii GOP nu au citit foarte bine viitorul. -În aceeași perioadă a ultimelor alegeri, patru din 10 republicani l-au ales pe Rick Perry în cursa pentru nominalizare, față de doar 28% pentru Mitt Romney. Însă, aceste cifre arată că majoritatea alegătorilor GOP consideră plauzibilă campania lui Trump. Chiar dacă republicanii sau repliat spre un alt candidat. Trump încă se află în fruntea tuturor. Unele voci spun că situația divizată va contribui probabil la victoria lui Trump, în timp ce susținerea contra lui Trump se va împărți la mai mult de doisprezece candidați. Însă un sondaj derulat la începutul lui septembrie de Universitatea din Monmouth arată că, în situația ipotetică a unei colaborări între Trump și majoritatea celorlalți candidați republicani, aproape întotdeauna Trump va beneficia de susținerea majoritară. -Trump se află la distanță de 13 puncte de Carly Fiorina, la 14 puncte de Marco Rubio, la 15 puncte de Walker, la 19 puncte de Jeb Bush și, în cele din urmă, la câte 33 de puncte față de Rand Paul, John Kasich și Chris Christie. Este aproape la egalitate cu Ted Cruz. Singurul candidat care îl învinge? Ben Carson l-ar învinge pe omul de afaceri cu 19 puncte într-o confruntare ipotetică de unu la unu. Majoritatea susținătorilor lui Donald Trump declară că s-au decis. Un nou sondaj realizat marți de CBS/NYT arată că peste jumătate dintre alegătorii care îl susțin pe Trump declară că nu își schimbă opțiunea de vot. Evident, se pot întâmpla multe în acest sens și nimeni nu poate spune că aceștia nu se vor răzgândi niciodată. -46% afirmă că lasă portița deschisă posibilității de a-și schimba opțiunea. Cu toate acestea, cel mai important adversar al lui Trump este în prezent neurochirurgul Ben Carson, însă este de două ori mai probabil ca alegătorii care declară că s-au decis să voteze cu Trump. Șase din 10 republicani afirmă că sunt de acord cu Trump în problema imigrării. De când Donald Trump i-a numit pe imigranții din Mexic „violatori” în discursul de deschidere a campaniei sale, în urmă cu două luni, imigrarea a fost subiectul central în campania pentru 2016. Unii sunt îngrijorați că stilul bombastic al lui Trump va duce la o scindare între alegătorii hispanici importanți și Partidul Republican și va prejudicia eforturile de rebranding. -Însă, conform sondajului realizat luni de ABC/Post, șase din 10 republicani afirmă că sunt de acord cu Trump în problema imigrării. Așa că, se pare că atâta timp cât problema imigrării rămâne în lumina reflectoarelor, la fel va rămâne și Doland Trump. Frustrarea față de autorități atinge noi culmi. Donald Trump și Ben Carson sunt acum susținuți de aproape jumătate dintre alegătorii republicani, în mare parte datorită statutului lor de outsideri. Conform sondajului realizat luni de ABC/Post, șase din 10 republicani afirmă că preferă un outsider politic în detrimentul cuiva cu experiență în guvernare. Oamenii sunt de asemenea supărați pe autoritățile de la Washington. -Un sondaj derulat în urmă cu două săptămâni în Iowa de către Des Moines Register/Bloomberg arată că trei din patru republicani din Iowa sunt frustrați de prestația republicanilor din COngres, 54% declarându-se „nemulțumiți” iar 21% „nervoși la culme”. Jeremy Corbyn își face debutul la Prime Minister's Questions Încă de la alegerea sa, debutul domnului Corbyn la PMQs a fost îndelung așteptat Noul lider al Partidului Laburist, Jeremy Corbyn, își va face mai târziu debutul la Prime Minister's Questions, confruntându-se pentru prima dată cu David Cameron. -Dl Corbyn va adresa primele dintre cele șase întrebări la care are dreptul la scurt timp după prânz; prestația sa va fi probabil analizată îndeaproape de mass-media și parlamentarii laburiști. În cadrul aparițiilor săptămânale, el a cerut „mai puțin teatru și mai multe fapte”. A declarat de asemenea că poate renunța la câteva participări și că le cedează colegilor săi. Confruntarea va fi primul test parlamentar al Dl Corbyn în poziție de lider, venind după ce a numit un „cabinet fantomă” și după discursul pe care l-a ținut marți la congresul anual TUC. -Între timp, decizia liderului Partidului laburist de a păstra tăcerea la rostirea imnului național în cadrul unei slujbe ținute marți cu ocazia aniversării a 75 de ani de la Bătălia Angliei a atras critici din partea unor parlamentari conservatori și a ținut prima pagină a ziarelor. Decizia domnului Corbyn de a nu cânta imnul național a atras atenția Un purtător de cuvânt al Dl Corbyn a declarat că acesta „a păstrat tăcerea în mod respectuos” și a recunoscut „eroismul Forțelor aeriene britanice în Bătălia Angliei.” -Însă un membru al cabinetului fantomă al Dl Corbyn, Owen Smith, a declarat pentru emisiunea Two's Newsnight transmisă de BBC că i-ar fi recomandat liderului laburist să cânte imnul național „indiferent” de credința sa că monarhia ar trebui abolită. În jur de doisprezece miniștri din cabinetul fantomă au refuzat să facă parte din echipa de frunte a Dl Corbyn, argumentând prin diferențe de opinie legate de economie, apărare și externe, în timp ce mai puțin de o șesime din partidul parlamentar l-a susținut ca lider. Corespondentul politic al BBC, Robin Brant, declară că diferențele de politică „se cumulează” în Partidul Laburist după numirea domnului Corbyn referitor la poziția sa față de Uniunea Europeană și limita de beneficii. -Dl Corbyn a declarat la conferința TUC că Partidul Laburist va aduce modificări prin care se va elimina integral ideea limitării. Câteva ore mai târziu, Dl Smith, Ministrul Muncii și Pensiilor, a declarat că partidul „este foarte clar” în opoziția exclusivă față de planurile guvernului de a reduce nivelul „cap” de la 26.000 lire la 23.000 lire. Dl Corbyn va fi al cincilea lider laburist cu care se confruntă David Cameron la tribună în ultimul deceniu, de când a preluat conducerea Partidului Conservator. Liderul laburist, care a promis o abordare diferită a politicii, spune că are idei „din surse externe” pentru întrebări pe care să i le adreseze Domnului Cameron și că a primit peste 30.000 de sugestii. -Parlamentarul Islington North a afirmat că PMQs implică un nivel de confruntare prea înalt și că se va abține de la replici și atacuri, angajându-se să se concentreze în schimb pe probleme serioase precum sărăcia, inegalitatea și provocările cu care se confruntă tinerii. Dl Corbyn a declarat că Angela Eagle, Ministrul de finanțe, îi va ține locul la PMQs atunci când el nu poate participa - de exemplu atunci când Dl Cameron se deplasează în străinătate. A exprimat de asemenea ideea că va permite altor colegi să ia cuvântul ocazional, spunând că l-a abordat pe Președintele Camerei Deputaților, John Bercow, pentru a discuta acest aspect. -În 2005, când a preluat conducerea, Dl Cameron a declarat că dorește să renunțe la stilul politic „Punch and Judy” asociat adesea cu PMQs însă a recunoscut câțiva ani mai târziu că nu a reușit în demersul său. De la prima transmisie, în 1990, PMQs a fost considerată un barometru cheie al raționamentului unui lider, al modului în care acesta conduce Camera Deputaților și a poziției sale în rândul colegilor parlamentari, deși criticii afirmă a ca devenit o caricatură și că are nevoie de o reformare profundă. „Cadru în Joburg”: Tineri fără adăpost beneficiază de cursuri de fotografie Este dificil să fii un om fără adăpost în Johannesburg. -Însă un grup de oameni care au trăit pe străzi în copilărie au găsit un mod de a învăța o meserie și de a-și câștiga traiul. „I was shot în Joburg” este un studio non-profit care îi învață pe tinerii fără adăpost să facă fotografii ale zonelor în care trăiesc și să câștige bani din asta. BBC News s-a întâlnit cu unul dintre primii absolvenți ai proiectului. Șeful JD Sports spune că salariile mai mari ar putea dăuna extinderii Președintele JD Sports, Peter Cowgill, declară că o creștere a salariului minim în Marea Britanie ar putea însemna „o putere de cumpărare mai mare în buzunarele potențialilor consumatori.” Este însă puțin probabil ca respectiva putere de cumpărare să depășească costurile mai mari pentru forța de muncă în cadrul firmei, afirmă el. -Costurile ar putea avea impact asupra planurilor de extindere ale JD Sports, a adăugat el, ceea ce ar putea însemna mai puține locuri de muncă noi. Thanasi Kokkinakis susținut de președintele Tennis Australia, Steve Healy Thanasi Kokkinakis ar merita să fie lăudat și nu criticat pentru comportamentul său. Thanasi Kokkinakis a fost victimă colaterală în „furtuna” creată în jurul prietenului său, Nick Kyrgios, iar comportamentul său merită mai degrabă cuvinte de laudă și nu critică, în opinia președintelui Tennis Australia, Steve Healy. \ No newline at end of file diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/train.len b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/train.len deleted file mode 100644 index 33ce003c8ae3139914a389a714812a2ab13aece4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26 hcmZo*jxA)+@V4-d_nz!s?Op24=3V2R>s_6y2LNfV2s8iy diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/train.source b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/train.source deleted file mode 100644 index d77722d4a57..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/train.source +++ /dev/null @@ -1,11 +0,0 @@ -Corrections to votes and voting intentions: see Minutes Assignment conferred on a Member: see Minutes Membership of committees and delegations: see Minutes Decisions concerning certain documents: see Minutes Forwarding of texts adopted during the sitting: see Minutes Dates for next sittings: see Minutes -Membership of Parliament: see Minutes Approval of Minutes of previous sitting: see Minutes Membership of Parliament: see Minutes Verification of credentials: see Minutes Documents received: see Minutes Written statements and oral questions (tabling): see Minutes Petitions: see Minutes Texts of agreements forwarded by the Council: see Minutes Action taken on Parliament's resolutions: see Minutes Agenda for next sitting: see Minutes Closure of sitting (The sitting was closed at 7.45 p.m.) -Election of Vice-Presidents of the European Parliament (deadline for submitting nominations): see Minutes (The sitting was suspended at 12.40 p.m. and resumed at 3.00 p.m.) Election of Quaestors of the European Parliament (deadline for submitting nominations): see Minutes (The sitting was suspended at 3.25 p.m. and resumed at 6.00 p.m.) Agenda for next sitting: see Minutes Closure of sitting (The sitting was closed at 6.15 p.m.) Opening of the sitting (The sitting was opened at 9.35 a.m.) Documents received: see Minutes Approval of Minutes of previous sitting: see Minutes Membership of Parliament: see Minutes -Membership of committees (deadline for tabling amendments): see Minutes (The sitting was suspended at 7 p.m. and resumed at 9 p.m.) Agenda for next sitting: see Minutes Closure of sitting (The sitting was suspended at 23.25 p.m.) Documents received: see Minutes Communication of Council common positions: see Minutes (The sitting was suspended at 11.35 a.m. and resumed for voting time at noon) Approval of Minutes of previous sitting: see Minutes Committee of Inquiry into the crisis of the Equitable Life Assurance Society (extension of mandate): see Minutes -Announcement by the President: see Minutes 1. Membership of committees (vote) 2. Amendment of the ACP-EC Partnership Agreement (vote) 4. Certification of train drivers operating locomotives and trains on the railway system in the Community (vote) 6. Law applicable to non-contractual obligations ("ROME II") (vote) 8. Seventh and eighth annual reports on arms exports (vote) Corrections to votes and voting intentions: see Minutes Membership of committees and delegations: see Minutes Request for waiver of parliamentary immunity: see Minutes Decisions concerning certain documents: see Minutes -Written statements for entry -Written statements for entry in the register (Rule 116): see Minutes Forwarding of texts adopted during the sitting: see Minutes Dates for next sittings: see Minutes Adjournment of the session I declare the session of the European Parliament adjourned. (The sitting was closed at 1 p.m.) Approval of Minutes of previous sitting: see Minutes Membership of Parliament: see Minutes Request for the defence of parliamentary immunity: see Minutes Appointments to committees (proposal by the Conference of Presidents): see Minutes Documents received: see Minutes Texts of agreements forwarded by the Council: see Minutes -Action taken on Parliament's resolutions: see Minutes Oral questions and written statements (tabling): see Minutes Written statements (Rule 116): see Minutes Agenda: see Minutes 1. Appointments to parliamentary committees (vote): see Minutes Voting time Agenda for next sitting: see Minutes Closure of sitting (The sitting was closed at 12 midnight) Opening of the sitting (The sitting was opened at 09.05) Documents received: see Minutes Approval of Minutes of previous sitting: see Minutes 1. Protection of passengers against displaced luggage (vote) 2. -Approval of motor vehicles with regard to the forward field of vision of the driver (vote) 3. EC-Korea Agreement on scientific and technological cooperation (vote) 4. Mainstreaming sustainability in development cooperation policies (vote) 5. Draft Amending Budget No 1/2007 (vote) 7. EC-Gabon Fisheries Partnership (vote) 10. Limitation periods in cross-border disputes involving personal injuries and fatal accidents (vote) 12. Strategy for a strengthened partnership with the Pacific Islands (vote) 13. The European private company statute (vote) That concludes the vote. -Corrections to votes and voting intentions: see Minutes Assignment conferred on a Member: see Minutes Membership of committees and delegations: see Minutes Decisions concerning certain documents: see Minutes Forwarding of texts adopted during the sitting: see Minutes Dates for next sittings: see Minutes -Written statements for entry diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/train.target b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/train.target deleted file mode 100644 index f18d80d3d47..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/train.target +++ /dev/null @@ -1,11 +0,0 @@ -Corectările voturilor şi intenţiile de vot: a se vedea procesul-verbal Misiune încredinţată unui deputat: consultaţi procesul-verbal Componenţa comisiilor şi a delegaţiilor: a se vedea procesul-verbal Decizii privind anumite documente: a se vedea procesul-verbal Transmiterea textelor adoptate în cursul prezentei şedinţe: a se vedea procesul-verbal Calendarul următoarelor şedinţe: a se vedea procesul-verbal -Componenţa Parlamentului: a se vedea procesul-verbal Aprobarea procesului-verbal al şedinţei precedente: a se vedea procesul-verbal Componenţa Parlamentului: a se vedea procesul-verbal Verificarea prerogativelor: a se vedea procesul-verbal Depunere de documente: a se vedea procesul-verbal Declaraţii scrise şi întrebări orale (depunere): consultaţi procesul-verbal Petiţii: a se vedea procesul-verbal Transmiterea de către Consiliu a textelor acordurilor: a se vedea procesul-verbal Cursul dat rezoluţiilor Parlamentului: a se vedea procesul-verbal Ordinea de zi a următoarei şedinţe: a se vedea procesul-verbal Ridicarea şedinţei (Se levanta la sesión a las 19.45 horas) -Alegerea vicepreşedinţilor Parlamentului European (termenul de depunere a candidaturilor): consultaţi procesul-verbal (Die Sitzung wird um 12.40 Uhr unterbrochen und um 15.00 Uhr wiederaufgenommen). Alegerea chestorilor Parlamentului European (termenul de depunere a candidaturilor): consultaţi procesul-verbal (Die Sitzung wird um 15.25 Uhr unterbrochen und um 18.00 Uhr wiederaufgenommen). Ordinea de zi a următoarei şedinţe: a se vedea procesul-verbal Ridicarea şedinţei (Die Sitzung wird um 18.15 Uhr geschlossen.) Deschiderea şedinţei (Die Sitzung wird um 9.35 Uhr eröffnet.) Depunerea documentelor: a se vedea procesul-verbal Aprobarea procesului-verbal al şedinţei precedente: a se vedea procesul-verbal Componenţa Parlamentului: a se vedea procesul-verbal -Componenţa comisiilor (termenul de depunere a amendamentelor): consultaţi procesul-verbal (La seduta, sospesa alle 19.00, è ripresa alle 21.00) Ordinea de zi a următoarei şedinţe: a se vedea procesul-verbal Ridicarea şedinţei (Die Sitzung wird um 23.25 Uhr geschlossen.) Depunerea documentelor: a se vedea procesul-verbal Comunicarea poziţiilor comune ale Parlamentului: a se vedea procesul-verbal (La séance, suspendue à 11h35 dans l'attente de l'Heure des votes, est reprise à midi) Aprobarea procesului-verbal al şedinţei precedente: a se vedea procesul-verbal Comisia de anchetă privind criza societăţii de asigurări "Equitable Life” (prelungirea mandatului): consultaţi procesul-verbal -Comunicarea Preşedintelui: consultaţi procesul-verbal 1. Componenţa comisiilor (vot) 2. Modificarea Acordului de parteneriat ACP-CE ("Acordul de la Cotonou”) (vot) 4. Certificarea mecanicilor de locomotivă care conduc locomotive şi trenuri în sistemul feroviar comunitar (vot) 6. Legea aplicabilă obligaţiilor necontractuale ("Roma II”) (vot) 8. Al şaptelea şi al optulea raport anual privind exportul de armament (vot) Corectările voturilor şi intenţiile de vot: a se vedea procesul-verbal Componenţa comisiilor şi a delegaţiilor: a se vedea procesul-verbal Cerere de ridicare a imunităţii parlamentare: consultaţi procesul-verbal Decizii privind anumite documente: a se vedea procesul-verbal -Declaraţii scrise înscrise -Declaraţii scrise înscrise în registru (articolul 116 din Regulamentul de procedură): a se vedea procesul-verbal Transmiterea textelor adoptate în cursul prezentei şedinţe: a se vedea procesul-verbal Calendarul următoarelor şedinţe: a se vedea procesul-verbal Întreruperea sesiunii Dichiaro interrotta la sessione del Parlamento europeo. (La seduta è tolta alle 13.00) Aprobarea procesului-verbal al şedinţei precedente: a se vedea procesul-verbal Componenţa Parlamentului: a se vedea procesul-verbal Cerere de apărare a imunităţii parlamentare: consultaţi procesul-verbal Numiri în comisii (propunerea Conferinţei preşedinţilor): consultaţi procesul-verbal Depunerea documentelor: a se vedea procesul-verbal Transmiterea de către Consiliu a textelor acordurilor: a se vedea procesul-verbal -Continuări ale rezoluţiilor Parlamentului: consultaţi procesul-verbal Declaraţii scrise şi întrebări orale (depunere): consultaţi procesul-verbal Declaraţii scrise (articolul 116 din Regulamentul de procedură) Ordinea de zi: a se vedea procesul-verbal 1. Numiri în comisiile parlamentare (vot): consultaţi procesul-verbal Timpul afectat votului Ordinea de zi a următoarei şedinţe: a se vedea procesul-verbal Ridicarea şedinţei (La seduta è tolta alle 24.00) Deschiderea şedinţei (The sitting was opened at 09.05) Depunerea documentelor: a se vedea procesul-verbal Aprobarea procesului-verbal al şedinţei precedente: a se vedea procesul-verbal 1. Protecţia pasagerilor împotriva deplasării bagajelor (vot) 2. -Omologarea vehiculelor cu motor cu privire la câmpul de vizibilitate înainte al conducătorului auto (vot) 3. Acordul CE-Coreea de cooperare ştiinţifică şi tehnologică (vot) 4. Integrarea durabilităţii în politicile de cooperare pentru dezvoltare (vot) 5. Proiect de buget rectificativ nr.1/2007 (vot) 7. Acordul de parteneriat în domeniul pescuitului între Comunitatea Europeană şi Republica Gaboneză (vot) 10. Termenele de prescripţie aplicabile în cadrul litigiilor transfrontaliere cu privire la vătămările corporale şi accidentele mortale (vot) 12. Relaţiile UE cu insulele din Pacific: Strategie pentru un parteneriat consolidat (vot) 13. Statutul societăţii private europene (vot) Damit ist die Abstimmungsstunde beendet. -Corectările voturilor şi intenţiile de vot: a se vedea procesul-verbal Misiune încredinţată unui deputat: consultaţi procesul-verbal Componenţa comisiilor şi a delegaţiilor: a se vedea procesul-verbal Decizii privind anumite documente: a se vedea procesul-verbal Transmiterea textelor adoptate în cursul prezentei şedinţe: a se vedea procesul-verbal Calendarul următoarelor şedinţe: a se vedea procesul-verbal -Declaraţii scrise înscrise diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/val.len b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/val.len deleted file mode 100644 index 897314a960b28d927b597805693e63f9de71d903..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40 tcmZo*jxA)+@NV_)_3rd;_nrmBQ@kg8PxDUqp6NZ$djXK$<1Lb^2LLau4=w-z diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/val.source b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/val.source deleted file mode 100644 index c895d0ae247..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/val.source +++ /dev/null @@ -1,16 +0,0 @@ -Brazil's Former Presidential Chief-of-Staff to Stand Trial A federal judge on Tuesday accepted the charges filed against Brazil's former presidential chief of staff for his alleged involvement in a massive corruption scheme at state-owned oil company Petrobras. The federal prosecutor's office said Jose Dirceu will face trial on the corruption, racketeering and money laundering charges filed earlier this month. Fourteen other people will also be tried, including Joao Vaccari Neto, the former treasurer of Brazil's governing Workers' Party and Renato de Souza Duque, Petrobras' former head of corporate services. -Dirceu is the most senior member of the ruling Workers' Party to be taken into custody in connection with the scheme. Dirceu served as former President Luiz Inacio Lula da Silva's chief of staff between 2003 and 2005. He was arrested early August in his home, where he already was under house arrest serving an 11-year sentence for his involvement in a cash-for-votes scheme in Congress more than 10 years ago. Prosecutors have said that Dirceu masterminded the kickback scheme at Petrobras, accepted bribes while in office and continued to receive payments from contractors after he was jailed in late 2013 for the vote-buying scandal. -According to prosecutors, the scheme at Petrobras involved roughly $2 billion in bribes and other illegal funds. Some of that money was allegedly funneled back to campaign coffers of the ruling party and its allies. It also allegedly included the payment of bribes to Petrobras executives in return for inflated contracts. 'Miraculous' recovery for Peshawar massacre schoolboy A teenager paralysed after being shot four times in Pakistan's deadliest terror attack has made a "miraculous" recovery following treatment in the UK. Muhammad Ibrahim Khan, 13, had been told by doctors in Pakistan that he would never walk again. -At least 140 people, mostly children, were killed when gunmen stormed Peshawar's Army Public School last December. Muhammad, who arrived in London last month for surgery, is being discharged from hospital later. Exactly nine months ago, on an ordinary Tuesday morning, Muhammad sat in his first aid class listening to his teachers intently. At the same time seven gunmen disguised in security uniforms were entering the Army Public School. They were strapped with explosives and had one simple mission in mind: Kill every man, woman and child they came across. "I can't forget what happened that day," Muhammad says with a severe stare. -We were sitting in the auditorium, we were asking questions... and then we heard heavy gunfire outside. The terrorists moved inside and they started killing - our teacher was burned alive. Muhammad described pulling four other pupils out of the auditorium as the carnage unfolded. He said he then heard his friend, Hamza calling to him. He said, 'oh brother save me'. I held his hand. That's when I was shot in the back, and he was shot in the head. Most of the people killed in the attack were pupils Hamza died in Muhammad's arms. Muhammad recalled blacking out after that, and the next thing he knew he was in a hospital bed, paralysed from the waist down. -Doctors in Peshawar in northern Pakistan, and then Rawalpindi, close to the capital, told his family there was no treatment, and he would never walk again. "Seeing him I felt like my soul had left my body," says Muhammad's father, Sher Khan Those nine months were the hardest in my life. But Mr Khan and his wife, Sherbano, refused to believe that their cricket-mad son would never be able to use his legs again. They campaigned, and appealed for help on Pakistani TV, gaining the support of high profile people such as cricketer turned politician Imran Khan. -Finally, they were able to raise the funds to bring Muhammad to the UK and provide him with treatment at London's private Harley Street Clinic. Consultant neurosurgeon Irfan Malik described Muhammad as "terrified" when he first arrived at the hospital. "He'd spent the last [few] months lying on a bed, unable to move side to side," says Mr Malik. He was weak, he had a pressure sore on his back. He wasn't in great shape. A vertebra at the base of Muhammad's spine was destroyed Muhammad was shot in his shoulder, his hip, and his back during the attack, damaging his lower spine - leading to paralysis. -But during six hours of surgery, Mr Malik and his team were able to reattach nerve endings and reconstruct the damaged part of the spine. Even Mr Malik was surprised at what happened next. Exactly one week after the surgery Muhammad stood up and started taking steps and walking. We were not expecting to get that sort of excellent result. That was miraculous," he says. Less than two weeks after his operation, Muhammad is ready to leave hospital and start the long road to recovery. Muhammad has defied the odds and started to walk again He says he wants to build his strength and continue his education in the UK. But he says he is determined to return to Pakistan, join the army and help fight terrorism. -"I feel like I have a second chance at life," he says as he shows off pictures he's drawn of guns scribbled out next to school books and pens Muhammad grows physically stronger every day but the psychological trauma he continues to endure is unimaginable. "My anger is not diminishing" he says. In my school little kids were killed. What was their crime? His mother, wiping a tear from her eye, caressed his head and said: "I can see my son walking again." He'll be able to get on with his normal life. 'Super Voice' 4G service from Three offers better signal Three is making use of a lower frequency 4G spectrum that can travel more widely -Mobile phone provider Three has launched a UK service it says will improve reception inside buildings and in rural black spots. Its 4G Super Voice enables customers to make calls and send texts using a lower frequency spectrum. Other networks are looking into introducing the technology, known as Voice Over Long-Term Evolution (VoLTE). It currently works on only the Samsung Galaxy S5, but recent iPhone handsets will be added in the coming months. Three said up to 5.5 million customers would have access to the service by 2017. -Chief technology officer Bryn Jones said: "By the end of the year, one million of our customers will have access to better indoor coverage and be able to use their phones in more places than ever before." Stars prepare for panto season Pantomime season is big business for theatres up and down the UK, with many getting ready for this year's season now. Some of the biggest names in showbusiness now take part in the yuletide theatre. Matthew Kelly and Hayley Mills will be appearing in Cinderella - one as an ugly sister, the other as fairy godmother. They reveal their panto secrets to BBC Breakfast. Steven Wilson: 'If I don't do anything, I feel this creeping guilt' -Steven Wilson was recently the big winner at the Progressive Music Awards Steven Wilson is often dubbed the hardest working musician in the world of progressive rock. The multi-talented musician won three prizes at this month's Progressive Music Awards in London, including album of the year for Hand. The Guardian's five-star review called it "a smart, soulful and immersive work of art." Since the 1980s, Wilson has been the driving force in a number of musical projects, the best known of which is the rock band Porcupine Tree. Now, ahead of two sell-out shows at the Royal Albert Hall, Wilson is releasing a vinyl-only double LP, Transience, to showcase the "more accessible" side of his solo output. -He tells the BBC about his love of vinyl, his busy schedule and explains how comic actor Matt Berry came to be his support act. What does vinyl mean to you? I grew up at the very tail end of the vinyl era, and at the time, I remember, we couldn't wait for CD to come along because vinyl was so frustrating. You would buy the record, take it home, and it would have a scratch, and you would have to take it back again. I love CDs, and for some kinds of music - classical for example - it is better than vinyl. But the problem with the CD and digital downloads is that there's nothing you can really cherish or treasure. Owning vinyl is like having a beautiful painting hanging in your living room. -It's something you can hold, pore over the lyrics and immerse yourself in the art work. I thought it was just a nostalgic thing, but it can't be if kids too young to remember vinyl are enjoying that kind of experience. Do you have a piece of vinyl that you treasure? The truth is I got rid of 100% of my vinyl in the 90s. All the vinyl I have is re-bought. I started off from the perspective that I wanted to recreate the collection I had when I was 15, but it's gone beyond that. The first record which I persuaded my parents to buy for me was Electric Light Orchestra's Out of the Blue. -If I still had my original copy, it would have sentimental value, but, alas, it's in a charity shop somewhere. Steven Wilson hopes the album will be a doorway for potential new fans Why release your new compilation Transience on vinyl? It was originally conceived as an idea for Record Store Day, but we missed the boat on that. My record company had suggested I put together some of my shorter, more accessible songs. I got a bit obsessed by the idea to make something like "an introduction to Steven Wilson," and I was committed to it being a vinyl-only release. Anyone who buys the vinyl does also get a high-resolution download. -Do you have a concern that the album won't show your work in a true light? \ No newline at end of file diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/val.target b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/val.target deleted file mode 100644 index 178d85d7190..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/test_data/wmt_en_ro/val.target +++ /dev/null @@ -1,16 +0,0 @@ -Fostul șef al cabinetului prezidențial brazilian este adus în fața instanței Marți, un judecător federal a acceptat acuzațiile aduse împotriva fostului șef al cabinetului prezidențial brazilian pentru presupusa implicare a acestuia într-o schemă masivă de corupție privind compania petrolieră de stat Petrobras. Biroul procurorului federal a declarat că Jose Dirceu va fi trimis în judecată pentru acuzațiile de corupție, înșelătorie și spălare de bani aduse în această lună. Alte paisprezece persoane vor fi judecate, printre acestea numărându-se Joao Vaccari Neto, fostul trezorier al Partidului Muncitorilor, aflat la putere în Brazilia, și Renato de Souza Duque, fostul președinte al serviciilor pentru întreprinderi ale Petrobras. -Dirceu este cel mai vechi membru al Partidului Muncitorilor aflat la guvernare luat în custodie pentru legăturile cu această schemă. Dirceu a servit ca șef de cabinet al fostului președinte Luiz Inacio Lula da Silva între 2003 și 2005. A fost arestat la începutul lui august de acasă, unde deja se afla sub arest la domiciliu, cu o pedeapsă de 11 ani pentru implicarea într-o schemă de cumpărare a voturilor în Congres cu peste 10 ani în urmă. Procurorii au declarat că Dirceu a dezvoltat schema de luare de mită de la Petrobras, a acceptat mită în timp ce se afla în funcție și a continuat să primească plăți de la antreprenori după ce a fost închis la sfârșitul lui 2013 pentru scandalul voturilor cumpărate. -Conform procurorilor, schema de la Petrobras a implicat aproximativ 2 miliarde de dolari sub formă de mită și alte fonduri ilegale. O parte din acei bani s-ar fi întors în fondul de campanie al partidului aflat la guvernare și al aliaților acestora. De asemenea, ar fi inclus mită către directorii Petrobras în schimbul unor contracte umflate. Recuperarea „miraculoasă” a unui elev supraviețuitor al masacrului de la Peshawar Un adolescent paralizat după ce fusese împușcat de patru ori în cel mai cumplit atac terorist din Pakistan a reușit o recuperare „miraculoasă” după ce a urmat un tratament în Regatul Unit. Lui Mohamed Ibrahim Khan, în vârstă de 13 ani, doctorii din Pakistan îi spuseseră că nu va mai putea să meargă niciodată. -Cel puțin 140 de persoane, majoritatea copii, au fost ucise când bărbați înarmați au atacat școala publică a armatei din Peshawar în luna decembrie a anului trecut. Mohamed, care a sosit la Londra luna trecută pentru operație, va fi externat mai târziu din spital. Exact cu nouă luni în urmă, într-o dimineață obișnuită de marți, Mohamed stătea la ora de primul ajutor și își asculta atent profesorii. Chiar atunci, șapte bărbați înarmați deghizați în uniformele agenților de pază intrau în școala publică a armatei. Purtau centuri cu explozivi și aveau de îndeplinit o misiune simplă: să îi ucidă pe toți bărbații, femeile și copiii care le ieșeau în cale. „Nu pot uita ce s-a întâmplat în acea zi”, spune Mohamed cu o privire aspră. -Stăteam în amfiteatru, puneam întrebări... apoi am auzit focuri de armă afară. Teroriștii au intrat înăuntru și au început să ucidă. Profesorul nostru a fost ars de viu. Mohamed descrie cum a scos patru elevi din amfiteatru în timp ce se desfășura carnagiul. Apoi spune că și-a auzit prietenul, pe Hamza, strigându-l. Spunea „oh, frate, salvează-mă”. L-am ținut de mână. Atunci eu am fost împușcat în spate, iar el în cap. Cei mai mulți dintre cei uciși în atac erau elevi Hamza a murit în brațele lui Mohamed. Mohamed își amintește că imediat după asta a leșinat și că următorul lucru pe care l-a știut a fost că se afla pe un pat de spital, paralizat de la brâu în jos. -Doctorii din Peshawar din nordul Pakistanului, apoi cei din Rawalpindi, aproape de capitală, i-au spus familiei sale că nu exista tratament și că nu va mai putea merge niciodată. „Când l-am văzut, am simțit cum îmi iese sufletul”, spune Sher Khan, tatăl lui Mohamed. Acele nouă luni au fost cele mai grele din viața mea. Însă Khan și soția lui, Sherbano, au refuzat să creadă că fiul lor atât de pasionat de crichet nu-și va mai putea folosi vreodată picioarele. Au făcut o campanie și au cerut ajutor de la televiziunea pakistaneză, atrăgând sprijinul unor oameni faimoși precum Imran Khan, jucător de crichet devenit politician. -Într-un final, au reușit să strângă fonduri pentru a-l duce pe Mohamed în Regatul Unit și a-i oferi tratament la clinica privată Harley Street din Londra. Neurochirurgul consultant Irfan Malik l-a descris pe Mohamed drept „înspăimântat” când acesta a ajuns la spital. „Își petrecuse ultimele [câteva] luni zăcând în pat, fără să se poată mișca de pe o parte pe alta, spune Malik. Era slăbit, se pusese multă presiune pe spatele lui. Nu era într-o formă prea bună. O vertebră de la baza coloanei vertebrale a lui Mohamed fusese distrusă Mohamed fusese împușcat în umăr, în șold și în spate în timpul atacului, iar coloana vertebrală inferioară îi fusese distrusă, ducând la paralizie. -Însă, în timpul unei operații care a durat șase ore, Malik și echipa lui au reușit să lege din nou terminațiile nervoase și să reconstruiască partea distrusă a coloanei. Chiar și Malik a fost surprins de ceea ce s-a întâmplat în continuare. Exact la o săptămână după operație, Mohamed s-a ridicat și a început să facă pași și să meargă. Nu ne așteptam la un rezultat atât de bun. A fost un miracol”, spune acesta. În mai puțin de două săptămâni de la operație, Mohamed este gata să părăsească spitalul și să înceapă procesul lung de recuperare. Mohamed a sfidat soarta și a început să meargă din nou Vrea să devină puternic și să își continue studiile în Regatul Unit. Însă este hotărât să revină în Pakistan, să se înroleze în armată și să lupte împotriva terorismului. -„Simt că am încă o șansă la viață” spune el, arătând imaginile cu arme desenate de el lângă manuale școlare și stilouri Fizic, Mohamed devine tot mai puternic în fiecare zi, însă trauma psihologică prin care trece și acum este de neimaginat. „Furia mea nu a scăzut”, mărturisește el. În școala mea au fost uciși copii mici. Ce crimă au comis ei? Mama lui își șterge o lacrimă, îl mângâie pe creștet și spune: „Îmi văd fiul mergând din nou”. Va putea să-și continue firesc viața. Serviciul 4G „Super Voice” de la Three oferă semnal mai bun Three folosește un spectru 4G cu o frecvență mai joasă, care poate acoperi o zonă mai extinsă -Furnizorul de telefonie mobilă Three a lansat în Regatul Unit un serviciu despre care spune că va îmbunătăți recepția în interiorul clădirilor și în zonele rurale fără semnal. Serviciul 4G Super Voice le permite clienților să efectueze apeluri și să trimită mesaje text folosind un spectru cu o frecvență mai joasă. Și alte rețele intenționează să introducă aceeași tehnologie, cunoscută ca „Voice Over Long-Term Evolution (VoLTE)”. Aceasta funcționează momentan doar cu Samsung Galaxy S5, însă telefoanele iPhone recente vor beneficia de ea în lunile următoare. Three menționează că până la 5,5 milioane de clienți vor avea acces la serviciu până în 2017. -Responsabilul șef pentru tehnologie, Bryn Jones a declarat: „Până la sfârșitul anului, un milion dintre clienții noștri vor avea acces la o acoperire mai bună în interior și își vor putea folosi telefoanele în mai multe locuri ca până acum”. Vedetele se pregătesc pentru stagiunea de pantomimă Stagiunea de pantomimă este foarte importantă pentru teatrele din tot Regatul Unit, multe dintre ele pregătindu-se acum pentru stagiunea din acest an. Acum, la teatrul de Crăciun participă unele dintre numele cele mai mari din showbusiness. Matthew Kelly și Hayley Mills vor apărea în Cenușăreasa - primul în rolul uneia dintre surorile rele, iar a doua în rolul zânei. Aceștia dezvăluie secretele pantomimei lor la BBC Breakfast. Steven Wilson: „Dacă nu fac nimic, mă simt vinovat” -Steven Wilson a fost desemnat recent drept marele câștigător al Progressive Music Awards Steven Wilson a fost numit de multe ori drept cel mai muncitor muzician din lumea rockului progresiv. Talentatul muzician a câștigat trei premii la Progressive Music Awards, care a avut loc luna aceasta la Londra, printre care și premiul pentru cel mai bun album al anului pentru Hand. În recenzia sa de cinci stele, The Guardian a numit albumul „o operă de artă inteligentă, expresivă și captivantă”. Încă din anii 1980, Wilson este motorul mai multor proiecte muzicale, cel mai cunoscut dintre acestea fiind trupa de rock Porcupine Tree. Acum, înainte de două spectacole cu casa închisă la Royal Albert Hall, Wilson lansează un dublu LP doar în format vinil, Transience, pentru a arăta latura „mai accesibilă” a activității sale solo. -A povestit pentru BBC despre dragostea lui pentru viniluri și despre programul său încărcat și a explicat cum a ajuns actorul de comedie Matt Berry să îi deschidă spectacolele. Ce înseamnă vinil pentru tine? Am crescut chiar în perioada de sfârșit a erei vinilurilor și îmi amintesc că atunci abia așteptam apariția CD-ului, căci vinilul era atât de enervant. Cumpărai un disc, mergeai cu el acasă, avea o zgârietură și trebuia să îl aduci înapoi. Iubesc CD-urile, iar pentru anumite tipuri de muzică, de exemplu cea clasică, sunt mai bune decât vinilurile. Însă problema cu CD-urile și cu descărcările digitale este aceea că nu mai există nimic pe care să îl prețuiești cu adevărat. Să ai un vinil e ca și cum ai avea un tablou frumos agățat în sufragerie. -E ceva ce poți ține în mână, în timp ce te lași absorbit de versuri și copleșit de actul artistic. Am crezut că e doar o chestie nostalgică, însă nu are cum să fie așa dacă unor puști prea tineri să-și amintească de viniluri le place acest gen de experiență. Ai vreun vinil la care ții în mod special? Recunosc că am scăpat de toate vinilurile în anii '90. Toate vinilurile pe care le am sunt cumpărate din nou. Am pornit de la ideea de a reface colecția pe care o aveam la 15 ani, însă am trecut de limita aceea. Primul disc pe care mi-am convins părinții să mi-l cumpere a fost Out of the Blue de la Electric Light Orchestra. -Dacă aș mai fi avut încă exemplarul inițial, acesta ar fi avut valoare sentimentală, însă, din păcate, se află pe undeva printr-un magazin de caritate. Steven Wilson speră că albumul va fi o poartă către posibili fani noi De ce ți-ai lansat noua compilație Transience pe vinil? Aceasta a fost concepută inițial ca idee pentru Ziua magazinelor de discuri, însă am ratat ocazia. Casa mea de discuri sugerase să adun câteva dintre melodiile mele mai scurte și mai accesibile. Am ajuns să fiu ușor obsedat de ideea de a face ceva gen „introducere în muzica lui Steven Wilson” și am ținut neapărat ca proiectul să fie lansat doar pe vinil. Cine cumpără vinilul primește, de asemenea, și o variantă descărcată la rezoluție înaltă. -Ești îngrijorat că albumul nu va arăta muzica ta în adevărata ei lumină? \ No newline at end of file diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/train_distil_marian_enro.sh b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/train_distil_marian_enro.sh deleted file mode 100644 index fc1b90595c5..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/train_distil_marian_enro.sh +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright 2020 The HuggingFace Team. 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. - -export WANDB_PROJECT=distil-marian -export BS=64 -export GAS=1 -export m=sshleifer/student_marian_en_ro_6_3 -export MAX_LEN=128 -python finetune_trainer.py \ - --tokenizer_name $m --model_name_or_path $m \ - --data_dir $ENRO_DIR \ - --output_dir marian_en_ro_6_3 --overwrite_output_dir \ - --learning_rate=3e-4 \ - --warmup_steps 500 --sortish_sampler \ - --fp16 \ - --gradient_accumulation_steps=$GAS \ - --per_device_train_batch_size=$BS --per_device_eval_batch_size=$BS \ - --freeze_encoder --freeze_embeds \ - --num_train_epochs=6 \ - --save_steps 3000 --eval_steps 3000 \ - --max_source_length $MAX_LEN --max_target_length $MAX_LEN \ - --val_max_target_length $MAX_TGT_LEN --test_max_target_length $MAX_TGT_LEN \ - --do_train --do_eval --do_predict \ - --evaluation_strategy steps \ - --predict_with_generate --logging_first_step \ - --task translation --label_smoothing_factor 0.1 \ - "$@" diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/train_distil_marian_enro_tpu.sh b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/train_distil_marian_enro_tpu.sh deleted file mode 100644 index 2fce7684ab4..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/train_distil_marian_enro_tpu.sh +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright 2020 The HuggingFace Team. 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. - -export WANDB_PROJECT=distil-marian -export BS=64 -export m=sshleifer/student_marian_en_ro_6_3 -export MAX_LEN=128 -export TPU_NUM_CORES=8 - -python xla_spawn.py --num_cores $TPU_NUM_CORES \ - finetune_trainer.py \ - --tokenizer_name $m --model_name_or_path $m \ - --data_dir $ENRO_DIR \ - --output_dir marian_en_ro_6_3 --overwrite_output_dir \ - --learning_rate=3e-4 \ - --warmup_steps 500 \ - --per_device_train_batch_size=$BS --per_device_eval_batch_size=$BS \ - --freeze_encoder --freeze_embeds \ - --num_train_epochs=6 \ - --save_steps 500 --eval_steps 500 \ - --logging_first_step --logging_steps 200 \ - --max_source_length $MAX_LEN --max_target_length $MAX_LEN \ - --val_max_target_length $MAX_TGT_LEN --test_max_target_length $MAX_TGT_LEN \ - --do_train --do_eval \ - --evaluation_strategy steps \ - --prediction_loss_only \ - --task translation --label_smoothing_factor 0.1 \ - "$@" diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/train_distilbart_cnn.sh b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/train_distilbart_cnn.sh deleted file mode 100644 index ec0aec8e597..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/train_distilbart_cnn.sh +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright 2020 The HuggingFace Team. 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. - -export WANDB_PROJECT=distilbart-trainer -export BS=32 -export m=sshleifer/student_cnn_12_6 -export tok=facebook/bart-large -export MAX_TGT_LEN=142 - -python finetune_trainer.py \ - --model_name_or_path $m --tokenizer_name $tok \ - --data_dir cnn_dm \ - --output_dir distilbart-cnn-12-6 --overwrite_output_dir \ - --learning_rate=3e-5 \ - --warmup_steps 500 --sortish_sampler \ - --fp16 \ - --n_val 500 \ - --gradient_accumulation_steps=1 \ - --per_device_train_batch_size=$BS --per_device_eval_batch_size=$BS \ - --freeze_encoder --freeze_embeds \ - --num_train_epochs=2 \ - --save_steps 3000 --eval_steps 3000 \ - --logging_first_step \ - --max_target_length 56 --val_max_target_length $MAX_TGT_LEN --test_max_target_length $MAX_TGT_LEN\ - --do_train --do_eval --do_predict \ - --evaluation_strategy steps \ - --predict_with_generate --sortish_sampler \ - "$@" diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/train_mbart_cc25_enro.sh b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/train_mbart_cc25_enro.sh deleted file mode 100644 index 2b603eda7c3..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/train_mbart_cc25_enro.sh +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2020 The HuggingFace Team. 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. - -python finetune_trainer.py \ - --model_name_or_path=facebook/mbart-large-cc25 \ - --data_dir $ENRO_DIR \ - --output_dir mbart_cc25_enro --overwrite_output_dir \ - --learning_rate=3e-5 \ - --warmup_steps 500 \ - --fp16 \ - --label_smoothing 0.1 \ - --adam_eps 1e-06 \ - --src_lang en_XX --tgt_lang ro_RO \ - --freeze_embeds \ - --per_device_train_batch_size=4 --per_device_eval_batch_size=4 \ - --max_source_length 128 --max_target_length 128 --val_max_target_length 128 --test_max_target_length 128\ - --sortish_sampler \ - --num_train_epochs 6 \ - --save_steps 25000 --eval_steps 25000 --logging_steps 1000 \ - --do_train --do_eval --do_predict \ - --evaluation_strategy steps \ - --predict_with_generate --logging_first_step \ - --task translation \ - "$@" diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/utils.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/utils.py deleted file mode 100644 index 2b4700e9f77..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/utils.py +++ /dev/null @@ -1,664 +0,0 @@ -# Copyright 2020 The HuggingFace Team. 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 itertools -import json -import linecache -import math -import os -import pickle -import socket -from logging import getLogger -from pathlib import Path -from typing import Callable, Dict, Iterable, List, Tuple, Union - -import git -import numpy as np -import torch -import torch.distributed as dist -from rouge_score import rouge_scorer, scoring -from sacrebleu import corpus_bleu -from torch import nn -from torch.utils.data import Dataset, Sampler - -from sentence_splitter import add_newline_to_end_of_each_sentence -from transformers import BartTokenizer, EvalPrediction, PreTrainedTokenizer, T5Tokenizer -from transformers.file_utils import cached_property -from transformers.models.bart.modeling_bart import shift_tokens_right - - -try: - from fairseq.data.data_utils import batch_by_size - - FAIRSEQ_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - FAIRSEQ_AVAILABLE = False - - -def label_smoothed_nll_loss(lprobs, target, epsilon, ignore_index=-100): - """From fairseq""" - if target.dim() == lprobs.dim() - 1: - target = target.unsqueeze(-1) - nll_loss = -lprobs.gather(dim=-1, index=target) - smooth_loss = -lprobs.sum(dim=-1, keepdim=True) - if ignore_index is not None: - pad_mask = target.eq(ignore_index) - nll_loss.masked_fill_(pad_mask, 0.0) - smooth_loss.masked_fill_(pad_mask, 0.0) - else: - nll_loss = nll_loss.squeeze(-1) - smooth_loss = smooth_loss.squeeze(-1) - - nll_loss = nll_loss.sum() # mean()? Scared to break other math. - smooth_loss = smooth_loss.sum() - eps_i = epsilon / lprobs.size(-1) - loss = (1.0 - epsilon) * nll_loss + eps_i * smooth_loss - return loss, nll_loss - - -def lmap(f: Callable, x: Iterable) -> List: - """list(map(f, x))""" - return list(map(f, x)) - - -def calculate_bleu(output_lns, refs_lns, **kwargs) -> dict: - """Uses sacrebleu's corpus_bleu implementation.""" - return {"bleu": round(corpus_bleu(output_lns, [refs_lns], **kwargs).score, 4)} - - -def build_compute_metrics_fn(task_name: str, tokenizer: PreTrainedTokenizer) -> Callable[[EvalPrediction], Dict]: - def non_pad_len(tokens: np.ndarray) -> int: - return np.count_nonzero(tokens != tokenizer.pad_token_id) - - def decode_pred(pred: EvalPrediction) -> Tuple[List[str], List[str]]: - pred_ids = pred.predictions - label_ids = pred.label_ids - pred_str = tokenizer.batch_decode(pred_ids, skip_special_tokens=True) - label_ids[label_ids == -100] = tokenizer.pad_token_id - label_str = tokenizer.batch_decode(label_ids, skip_special_tokens=True) - pred_str = lmap(str.strip, pred_str) - label_str = lmap(str.strip, label_str) - return pred_str, label_str - - def summarization_metrics(pred: EvalPrediction) -> Dict: - pred_str, label_str = decode_pred(pred) - rouge: Dict = calculate_rouge(pred_str, label_str) - summ_len = np.round(np.mean(lmap(non_pad_len, pred.predictions)), 1) - rouge.update({"gen_len": summ_len}) - return rouge - - def translation_metrics(pred: EvalPrediction) -> Dict: - pred_str, label_str = decode_pred(pred) - bleu: Dict = calculate_bleu(pred_str, label_str) - gen_len = np.round(np.mean(lmap(non_pad_len, pred.predictions)), 1) - bleu.update({"gen_len": gen_len}) - return bleu - - compute_metrics_fn = summarization_metrics if "summarization" in task_name else translation_metrics - return compute_metrics_fn - - -def trim_batch( - input_ids, - pad_token_id, - attention_mask=None, -): - """Remove columns that are populated exclusively by pad_token_id""" - keep_column_mask = input_ids.ne(pad_token_id).any(dim=0) - if attention_mask is None: - return input_ids[:, keep_column_mask] - else: - return (input_ids[:, keep_column_mask], attention_mask[:, keep_column_mask]) - - -class AbstractSeq2SeqDataset(Dataset): - def __init__( - self, - tokenizer, - data_dir, - max_source_length, - max_target_length, - type_path="train", - n_obs=None, - prefix="", - **dataset_kwargs - ): - super().__init__() - self.src_file = Path(data_dir).joinpath(type_path + ".source") - self.tgt_file = Path(data_dir).joinpath(type_path + ".target") - self.len_file = Path(data_dir).joinpath(type_path + ".len") - if os.path.exists(self.len_file): - self.src_lens = pickle_load(self.len_file) - self.used_char_len = False - else: - self.src_lens = self.get_char_lens(self.src_file) - self.used_char_len = True - self.max_source_length = max_source_length - self.max_target_length = max_target_length - assert min(self.src_lens) > 0, f"found empty line in {self.src_file}" - self.tokenizer = tokenizer - self.prefix = prefix if prefix is not None else "" - - if n_obs is not None: - self.src_lens = self.src_lens[:n_obs] - self.pad_token_id = self.tokenizer.pad_token_id - self.dataset_kwargs = dataset_kwargs - dataset_kwargs.update({"add_prefix_space": True} if isinstance(self.tokenizer, BartTokenizer) else {}) - - def __len__(self): - return len(self.src_lens) - - @staticmethod - def get_char_lens(data_file): - return [len(x) for x in Path(data_file).open().readlines()] - - @cached_property - def tgt_lens(self): - """Length in characters of target documents""" - return self.get_char_lens(self.tgt_file) - - def make_sortish_sampler(self, batch_size, distributed=False, shuffle=True, **kwargs): - if distributed: - return DistributedSortishSampler(self, batch_size, shuffle=shuffle, **kwargs) - else: - return SortishSampler(self.src_lens, batch_size, shuffle=shuffle) - - def make_dynamic_sampler(self, max_tokens_per_batch=1024, **kwargs): - assert FAIRSEQ_AVAILABLE, "Dynamic batch size requires `pip install fairseq`" - assert not self.used_char_len, "You must call python make_len_file.py before calling make_dynamic_sampler" - sorted_indices = list(self.make_sortish_sampler(1024, shuffle=False)) - - def num_tokens_in_example(i): - return min(self.src_lens[i], self.max_target_length) - - # call fairseq cython function - batch_sampler: List[List[int]] = batch_by_size( - sorted_indices, - num_tokens_fn=num_tokens_in_example, - max_tokens=max_tokens_per_batch, - required_batch_size_multiple=64, - ) - shuffled_batches = [batch_sampler[i] for i in np.random.permutation(range(len(batch_sampler)))] - # move the largest batch to the front to OOM quickly (uses an approximation for padding) - approximate_toks_per_batch = [max(self.src_lens[i] for i in batch) * len(batch) for batch in shuffled_batches] - largest_batch_idx = np.argmax(approximate_toks_per_batch) - shuffled_batches[0], shuffled_batches[largest_batch_idx] = ( - shuffled_batches[largest_batch_idx], - shuffled_batches[0], - ) - return shuffled_batches - - def __getitem__(self, item): - raise NotImplementedError("You must implement this") - - def collate_fn(self, batch): - raise NotImplementedError("You must implement this") - - -class LegacySeq2SeqDataset(AbstractSeq2SeqDataset): - def __getitem__(self, index) -> Dict[str, torch.Tensor]: - """Call tokenizer on src and tgt_lines""" - index = index + 1 # linecache starts at 1 - source_line = self.prefix + linecache.getline(str(self.src_file), index).rstrip("\n") - tgt_line = linecache.getline(str(self.tgt_file), index).rstrip("\n") - assert source_line, f"empty source line for index {index}" - assert tgt_line, f"empty tgt line for index {index}" - source_inputs = self.encode_line(self.tokenizer, source_line, self.max_source_length) - target_inputs = self.encode_line(self.tokenizer, tgt_line, self.max_target_length) - - source_ids = source_inputs["input_ids"].squeeze() - target_ids = target_inputs["input_ids"].squeeze() - src_mask = source_inputs["attention_mask"].squeeze() - return { - "input_ids": source_ids, - "attention_mask": src_mask, - "labels": target_ids, - } - - def encode_line(self, tokenizer, line, max_length, pad_to_max_length=True, return_tensors="pt"): - """Only used by LegacyDataset""" - return tokenizer( - [line], - max_length=max_length, - padding="max_length" if pad_to_max_length else None, - truncation=True, - return_tensors=return_tensors, - **self.dataset_kwargs, - ) - - def collate_fn(self, batch) -> Dict[str, torch.Tensor]: - input_ids = torch.stack([x["input_ids"] for x in batch]) - masks = torch.stack([x["attention_mask"] for x in batch]) - target_ids = torch.stack([x["labels"] for x in batch]) - pad_token_id = self.pad_token_id - y = trim_batch(target_ids, pad_token_id) - source_ids, source_mask = trim_batch(input_ids, pad_token_id, attention_mask=masks) - batch = { - "input_ids": source_ids, - "attention_mask": source_mask, - "labels": y, - } - return batch - - -class Seq2SeqDataset(AbstractSeq2SeqDataset): - """A dataset that calls prepare_seq2seq_batch.""" - - def __getitem__(self, index) -> Dict[str, str]: - index = index + 1 # linecache starts at 1 - source_line = self.prefix + linecache.getline(str(self.src_file), index).rstrip("\n") - tgt_line = linecache.getline(str(self.tgt_file), index).rstrip("\n") - assert source_line, f"empty source line for index {index}" - assert tgt_line, f"empty tgt line for index {index}" - return {"tgt_texts": tgt_line, "src_texts": source_line, "id": index - 1} - - def collate_fn(self, batch) -> Dict[str, torch.Tensor]: - """Call prepare_seq2seq_batch.""" - batch_encoding: Dict[str, torch.Tensor] = self.tokenizer.prepare_seq2seq_batch( - [x["src_texts"] for x in batch], - tgt_texts=[x["tgt_texts"] for x in batch], - max_length=self.max_source_length, - max_target_length=self.max_target_length, - return_tensors="pt", - **self.dataset_kwargs, - ).data - batch_encoding["ids"] = torch.tensor([x["id"] for x in batch]) - return batch_encoding - - -class Seq2SeqDataCollator: - def __init__(self, tokenizer, data_args, decoder_start_token_id, tpu_num_cores=None): - self.tokenizer = tokenizer - self.pad_token_id = tokenizer.pad_token_id - self.decoder_start_token_id = decoder_start_token_id - assert ( - self.pad_token_id is not None - ), f"pad_token_id is not defined for ({self.tokenizer.__class__.__name__}), it must be defined." - self.data_args = data_args - self.tpu_num_cores = tpu_num_cores - self.dataset_kwargs = {"add_prefix_space": True} if isinstance(tokenizer, BartTokenizer) else {} - if data_args.src_lang is not None: - self.dataset_kwargs["src_lang"] = data_args.src_lang - if data_args.tgt_lang is not None: - self.dataset_kwargs["tgt_lang"] = data_args.tgt_lang - - def __call__(self, batch) -> Dict[str, torch.Tensor]: - if hasattr(self.tokenizer, "prepare_seq2seq_batch"): - batch = self._encode(batch) - input_ids, attention_mask, labels = ( - batch["input_ids"], - batch["attention_mask"], - batch["labels"], - ) - else: - input_ids = torch.stack([x["input_ids"] for x in batch]) - attention_mask = torch.stack([x["attention_mask"] for x in batch]) - labels = torch.stack([x["labels"] for x in batch]) - - labels = trim_batch(labels, self.pad_token_id) - input_ids, attention_mask = trim_batch(input_ids, self.pad_token_id, attention_mask=attention_mask) - - if isinstance(self.tokenizer, T5Tokenizer): - decoder_input_ids = self._shift_right_t5(labels) - else: - decoder_input_ids = shift_tokens_right(labels, self.pad_token_id, self.decoder_start_token_id) - - batch = { - "input_ids": input_ids, - "attention_mask": attention_mask, - "decoder_input_ids": decoder_input_ids, - "labels": labels, - } - return batch - - def _shift_right_t5(self, input_ids): - # shift inputs to the right - shifted_input_ids = input_ids.new_zeros(input_ids.shape) - shifted_input_ids[..., 1:] = input_ids[..., :-1].clone() - shifted_input_ids[..., 0] = self.pad_token_id - return shifted_input_ids - - def _encode(self, batch) -> Dict[str, torch.Tensor]: - batch_encoding = self.tokenizer.prepare_seq2seq_batch( - [x["src_texts"] for x in batch], - tgt_texts=[x["tgt_texts"] for x in batch], - max_length=self.data_args.max_source_length, - max_target_length=self.data_args.max_target_length, - padding="max_length" if self.tpu_num_cores is not None else "longest", # TPU hack - return_tensors="pt", - **self.dataset_kwargs, - ) - return batch_encoding.data - - -class SortishSampler(Sampler): - "Go through the text data by order of src length with a bit of randomness. From fastai repo." - - def __init__(self, data, batch_size, shuffle=True): - self.data, self.bs, self.shuffle = data, batch_size, shuffle - - def __len__(self) -> int: - return len(self.data) - - def __iter__(self): - return iter(sortish_sampler_indices(self.data, self.bs, shuffle=self.shuffle)) - - -def sortish_sampler_indices(data: List, bs: int, shuffle=True) -> np.array: - "Go through the text data by order of src length with a bit of randomness. From fastai repo." - if not shuffle: - return np.argsort(np.array(data) * -1) - - def key_fn(i): - return data[i] - - idxs = np.random.permutation(len(data)) - sz = bs * 50 - ck_idx = [idxs[i : i + sz] for i in range(0, len(idxs), sz)] - sort_idx = np.concatenate([sorted(s, key=key_fn, reverse=True) for s in ck_idx]) - sz = bs - ck_idx = [sort_idx[i : i + sz] for i in range(0, len(sort_idx), sz)] - max_ck = np.argmax([key_fn(ck[0]) for ck in ck_idx]) # find the chunk with the largest key, - ck_idx[0], ck_idx[max_ck] = ck_idx[max_ck], ck_idx[0] # then make sure it goes first. - sort_idx = np.concatenate(np.random.permutation(ck_idx[1:])) if len(ck_idx) > 1 else np.array([], dtype=np.int) - sort_idx = np.concatenate((ck_idx[0], sort_idx)) - return sort_idx - - -class DistributedSortishSampler(Sampler): - """Copied from torch DistributedSampler""" - - def __init__(self, dataset, batch_size, num_replicas=None, rank=None, add_extra_examples=True, shuffle=True): - if num_replicas is None: - if not dist.is_available(): - raise RuntimeError("Requires distributed package to be available") - num_replicas = dist.get_world_size() - if rank is None: - if not dist.is_available(): - raise RuntimeError("Requires distributed package to be available") - rank = dist.get_rank() - self.dataset = dataset - self.num_replicas = num_replicas - self.rank = rank - self.epoch = 0 - if add_extra_examples: - self.num_samples = int(math.ceil(len(self.dataset) * 1.0 / self.num_replicas)) - self.total_size = self.num_samples * self.num_replicas - else: - self.total_size = len(dataset) - self.num_samples = len(self.available_indices) - self.batch_size = batch_size - self.add_extra_examples = add_extra_examples - self.shuffle = shuffle - - def __iter__(self) -> Iterable: - g = torch.Generator() - g.manual_seed(self.epoch) - - sortish_data = [self.dataset.src_lens[i] for i in self.available_indices] - sortish_indices = sortish_sampler_indices(sortish_data, self.batch_size, shuffle=self.shuffle) - indices = [self.available_indices[i] for i in sortish_indices] - assert len(indices) == self.num_samples - return iter(indices) - - @cached_property - def available_indices(self) -> np.array: - indices = list(range(len(self.dataset))) - # add extra samples to make it evenly divisible - indices += indices[: (self.total_size - len(indices))] - assert len(indices) == self.total_size - # subsample - available_indices = indices[self.rank : self.total_size : self.num_replicas] - return available_indices - - def __len__(self): - return self.num_samples - - def set_epoch(self, epoch): - self.epoch = epoch - - -logger = getLogger(__name__) - - -def use_task_specific_params(model, task): - """Update config with summarization specific params.""" - task_specific_params = model.config.task_specific_params - - if task_specific_params is not None: - pars = task_specific_params.get(task, {}) - logger.info(f"setting model.config to task specific params for {task}:\n {pars}") - logger.info("note: command line args may override some of these") - model.config.update(pars) - - -def pickle_load(path): - """pickle.load(path)""" - with open(path, "rb") as f: - return pickle.load(f) - - -def pickle_save(obj, path): - """pickle.dump(obj, path)""" - with open(path, "wb") as f: - return pickle.dump(obj, f) - - -def flatten_list(summary_ids: List[List]): - return [x for x in itertools.chain.from_iterable(summary_ids)] - - -def save_git_info(folder_path: str) -> None: - """Save git information to output_dir/git_log.json""" - repo_infos = get_git_info() - save_json(repo_infos, os.path.join(folder_path, "git_log.json")) - - -def save_json(content, path, indent=4, **json_dump_kwargs): - with open(path, "w") as f: - json.dump(content, f, indent=indent, sort_keys=True, **json_dump_kwargs) - - -def load_json(path): - with open(path) as f: - return json.load(f) - - -def get_git_info(): - try: - repo = git.Repo(search_parent_directories=True) - repo_infos = { - "repo_id": str(repo), - "repo_sha": str(repo.head.object.hexsha), - "repo_branch": str(repo.active_branch), - "hostname": str(socket.gethostname()), - } - return repo_infos - except TypeError: - return { - "repo_id": None, - "repo_sha": None, - "repo_branch": None, - "hostname": None, - } - - -ROUGE_KEYS = ["rouge1", "rouge2", "rougeL", "rougeLsum"] - - -def extract_rouge_mid_statistics(dct): - new_dict = {} - for k1, v1 in dct.items(): - mid = v1.mid - new_dict[k1] = {stat: round(getattr(mid, stat), 4) for stat in ["precision", "recall", "fmeasure"]} - return new_dict - - -def calculate_rouge( - pred_lns: List[str], - tgt_lns: List[str], - use_stemmer=True, - rouge_keys=ROUGE_KEYS, - return_precision_and_recall=False, - bootstrap_aggregation=True, - newline_sep=True, -) -> Dict: - """Calculate rouge using rouge_scorer package. - - Args: - pred_lns: list of summaries generated by model - tgt_lns: list of groundtruth summaries (e.g. contents of val.target) - use_stemmer: Bool indicating whether Porter stemmer should be used to - strip word suffixes to improve matching. - rouge_keys: which metrics to compute, defaults to rouge1, rouge2, rougeL, rougeLsum - return_precision_and_recall: (False) whether to also return precision and recall. - bootstrap_aggregation: whether to do the typical bootstrap resampling of scores. Defaults to True, if False - this function returns a collections.defaultdict[metric: list of values for each observation for each subscore]`` - newline_sep:(default=True) whether to add newline between sentences. This is essential for calculation rougeL - on multi sentence summaries (CNN/DM dataset). - - Returns: - Dict[score: value] if aggregate else defaultdict(list) keyed by rouge_keys - - """ - scorer = rouge_scorer.RougeScorer(rouge_keys, use_stemmer=use_stemmer) - aggregator = scoring.BootstrapAggregator() - for pred, tgt in zip(tgt_lns, pred_lns): - # rougeLsum expects "\n" separated sentences within a summary - if newline_sep: - pred = add_newline_to_end_of_each_sentence(pred) - tgt = add_newline_to_end_of_each_sentence(tgt) - scores = scorer.score(pred, tgt) - aggregator.add_scores(scores) - - if bootstrap_aggregation: - result = aggregator.aggregate() - if return_precision_and_recall: - return extract_rouge_mid_statistics(result) # here we return dict - else: - return {k: round(v.mid.fmeasure * 100, 4) for k, v in result.items()} - - else: - return aggregator._scores # here we return defaultdict(list) - - -# Utilities for freezing parameters and checking whether they are frozen - - -def freeze_params(model: nn.Module): - """Set requires_grad=False for each of model.parameters()""" - for par in model.parameters(): - par.requires_grad = False - - -def freeze_embeds(model): - """Freeze token embeddings and positional embeddings for bart, just token embeddings for t5.""" - model_type = model.config.model_type - - if model_type in ["t5", "mt5"]: - freeze_params(model.shared) - for d in [model.encoder, model.decoder]: - freeze_params(d.embed_tokens) - elif model_type == "fsmt": - for d in [model.model.encoder, model.model.decoder]: - freeze_params(d.embed_positions) - freeze_params(d.embed_tokens) - else: - freeze_params(model.model.shared) - for d in [model.model.encoder, model.model.decoder]: - freeze_params(d.embed_positions) - freeze_params(d.embed_tokens) - - -def grad_status(model: nn.Module) -> Iterable: - return (par.requires_grad for par in model.parameters()) - - -def any_requires_grad(model: nn.Module) -> bool: - return any(grad_status(model)) - - -def assert_all_frozen(model): - model_grads: List[bool] = list(grad_status(model)) - n_require_grad = sum(lmap(int, model_grads)) - npars = len(model_grads) - assert not any(model_grads), f"{n_require_grad/npars:.1%} of {npars} weights require grad" - - -def assert_not_all_frozen(model): - model_grads: List[bool] = list(grad_status(model)) - npars = len(model_grads) - assert any(model_grads), f"none of {npars} weights require grad" - - -def parse_numeric_n_bool_cl_kwargs(unparsed_args: List[str]) -> Dict[str, Union[int, float, bool]]: - """ - Parse an argv list of unspecified command line args to a dict. - Assumes all values are either numeric or boolean in the form of true/false. - """ - result = {} - assert len(unparsed_args) % 2 == 0, f"got odd number of unparsed args: {unparsed_args}" - num_pairs = len(unparsed_args) // 2 - for pair_num in range(num_pairs): - i = 2 * pair_num - assert unparsed_args[i].startswith("--") - if unparsed_args[i + 1].lower() == "true": - value = True - elif unparsed_args[i + 1].lower() == "false": - value = False - else: - try: - value = int(unparsed_args[i + 1]) - except ValueError: - value = float(unparsed_args[i + 1]) # this can raise another informative ValueError - - result[unparsed_args[i][2:]] = value - return result - - -def write_txt_file(ordered_tgt, path): - f = Path(path).open("w") - for ln in ordered_tgt: - f.write(ln + "\n") - f.flush() - - -def chunks(lst, n): - """Yield successive n-sized chunks from lst.""" - for i in range(0, len(lst), n): - yield lst[i : i + n] - - -def check_output_dir(args, expected_items=0): - """ - Checks whether to bail out if output_dir already exists and has more than expected_items in it - - `args`: needs to have the following attributes of `args`: - - output_dir - - do_train - - overwrite_output_dir - - `expected_items`: normally 0 (default) - i.e. empty dir, but in some cases a few files are expected (e.g. recovery from OOM) - """ - if ( - os.path.exists(args.output_dir) - and len(os.listdir(args.output_dir)) > expected_items - and args.do_train - and not args.overwrite_output_dir - ): - raise ValueError( - f"Output directory ({args.output_dir}) already exists and " - f"has {len(os.listdir(args.output_dir))} items in it (expected {expected_items} items). " - "Use --overwrite_output_dir to overcome." - ) diff --git a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/xla_spawn.py b/examples/pytorch/huggingface_models/examples/legacy/seq2seq/xla_spawn.py deleted file mode 100644 index d84b4199456..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/seq2seq/xla_spawn.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright 2020 The HuggingFace Team. 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. -""" -A simple launcher script for TPU training - -Inspired by https://github.com/pytorch/pytorch/blob/master/torch/distributed/launch.py - -:: - >>> python xla_spawn.py --num_cores=NUM_CORES_YOU_HAVE - YOUR_TRAINING_SCRIPT.py (--arg1 --arg2 --arg3 and all other - arguments of your training script) - -""" - - -import importlib -import sys -from argparse import REMAINDER, ArgumentParser -from pathlib import Path - -import torch_xla.distributed.xla_multiprocessing as xmp - - -def parse_args(): - """ - Helper function parsing the command line options - @retval ArgumentParser - """ - parser = ArgumentParser( - description=( - "PyTorch TPU distributed training launch " - "helper utility that will spawn up " - "multiple distributed processes" - ) - ) - - # Optional arguments for the launch helper - parser.add_argument("--num_cores", type=int, default=1, help="Number of TPU cores to use (1 or 8).") - - # positional - parser.add_argument( - "training_script", - type=str, - help=( - "The full path to the single TPU training " - "program/script to be launched in parallel, " - "followed by all the arguments for the " - "training script" - ), - ) - - # rest from the training program - parser.add_argument("training_script_args", nargs=REMAINDER) - - return parser.parse_args() - - -def main(): - args = parse_args() - - # Import training_script as a module. - script_fpath = Path(args.training_script) - sys.path.append(str(script_fpath.parent.resolve())) - mod_name = script_fpath.stem - mod = importlib.import_module(mod_name) - - # Patch sys.argv - sys.argv = [args.training_script] + args.training_script_args + ["--tpu_num_cores", str(args.num_cores)] - - xmp.spawn(mod._mp_fn, args=(), nprocs=args.num_cores) - - -if __name__ == "__main__": - main() diff --git a/examples/pytorch/huggingface_models/examples/legacy/token-classification/README.md b/examples/pytorch/huggingface_models/examples/legacy/token-classification/README.md deleted file mode 100644 index e484f332f32..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/token-classification/README.md +++ /dev/null @@ -1,294 +0,0 @@ -## Token classification - -Based on the scripts [`run_ner.py`](https://github.com/huggingface/transformers/blob/master/examples/contrib/legacy/token-classification/run_ner.py). - -The following examples are covered in this section: - -* NER on the GermEval 2014 (German NER) dataset -* Emerging and Rare Entities task: WNUT’17 (English NER) dataset - -Details and results for the fine-tuning provided by @stefan-it. - -### GermEval 2014 (German NER) dataset - -#### Data (Download and pre-processing steps) - -Data can be obtained from the [GermEval 2014](https://sites.google.com/site/germeval2014ner/data) shared task page. - -Here are the commands for downloading and pre-processing train, dev and test datasets. The original data format has four (tab-separated) columns, in a pre-processing step only the two relevant columns (token and outer span NER annotation) are extracted: - -```bash -curl -L 'https://drive.google.com/uc?export=download&id=1Jjhbal535VVz2ap4v4r_rN1UEHTdLK5P' \ -| grep -v "^#" | cut -f 2,3 | tr '\t' ' ' > train.txt.tmp -curl -L 'https://drive.google.com/uc?export=download&id=1ZfRcQThdtAR5PPRjIDtrVP7BtXSCUBbm' \ -| grep -v "^#" | cut -f 2,3 | tr '\t' ' ' > dev.txt.tmp -curl -L 'https://drive.google.com/uc?export=download&id=1u9mb7kNJHWQCWyweMDRMuTFoOHOfeBTH' \ -| grep -v "^#" | cut -f 2,3 | tr '\t' ' ' > test.txt.tmp -``` - -The GermEval 2014 dataset contains some strange "control character" tokens like `'\x96', '\u200e', '\x95', '\xad' or '\x80'`. -One problem with these tokens is, that `BertTokenizer` returns an empty token for them, resulting in misaligned `InputExample`s. -The `preprocess.py` script located in the `scripts` folder a) filters these tokens and b) splits longer sentences into smaller ones (once the max. subtoken length is reached). - -Let's define some variables that we need for further pre-processing steps and training the model: - -```bash -export MAX_LENGTH=128 -export BERT_MODEL=bert-base-multilingual-cased -``` - -Run the pre-processing script on training, dev and test datasets: - -```bash -python3 scripts/preprocess.py train.txt.tmp $BERT_MODEL $MAX_LENGTH > train.txt -python3 scripts/preprocess.py dev.txt.tmp $BERT_MODEL $MAX_LENGTH > dev.txt -python3 scripts/preprocess.py test.txt.tmp $BERT_MODEL $MAX_LENGTH > test.txt -``` - -The GermEval 2014 dataset has much more labels than CoNLL-2002/2003 datasets, so an own set of labels must be used: - -```bash -cat train.txt dev.txt test.txt | cut -d " " -f 2 | grep -v "^$"| sort | uniq > labels.txt -``` - -#### Prepare the run - -Additional environment variables must be set: - -```bash -export OUTPUT_DIR=germeval-model -export BATCH_SIZE=32 -export NUM_EPOCHS=3 -export SAVE_STEPS=750 -export SEED=1 -``` - -#### Run the Pytorch version - -To start training, just run: - -```bash -python3 run_ner.py --data_dir ./ \ ---labels ./labels.txt \ ---model_name_or_path $BERT_MODEL \ ---output_dir $OUTPUT_DIR \ ---max_seq_length $MAX_LENGTH \ ---num_train_epochs $NUM_EPOCHS \ ---per_device_train_batch_size $BATCH_SIZE \ ---save_steps $SAVE_STEPS \ ---seed $SEED \ ---do_train \ ---do_eval \ ---do_predict -``` - -If your GPU supports half-precision training, just add the `--fp16` flag. After training, the model will be both evaluated on development and test datasets. - -#### JSON-based configuration file - -Instead of passing all parameters via commandline arguments, the `run_ner.py` script also supports reading parameters from a json-based configuration file: - -```json -{ - "data_dir": ".", - "labels": "./labels.txt", - "model_name_or_path": "bert-base-multilingual-cased", - "output_dir": "germeval-model", - "max_seq_length": 128, - "num_train_epochs": 3, - "per_device_train_batch_size": 32, - "save_steps": 750, - "seed": 1, - "do_train": true, - "do_eval": true, - "do_predict": true -} -``` - -It must be saved with a `.json` extension and can be used by running `python3 run_ner.py config.json`. - -#### Evaluation - -Evaluation on development dataset outputs the following for our example: - -```bash -10/04/2019 00:42:06 - INFO - __main__ - ***** Eval results ***** -10/04/2019 00:42:06 - INFO - __main__ - f1 = 0.8623348017621146 -10/04/2019 00:42:06 - INFO - __main__ - loss = 0.07183869666975543 -10/04/2019 00:42:06 - INFO - __main__ - precision = 0.8467916366258111 -10/04/2019 00:42:06 - INFO - __main__ - recall = 0.8784592370979806 -``` - -On the test dataset the following results could be achieved: - -```bash -10/04/2019 00:42:42 - INFO - __main__ - ***** Eval results ***** -10/04/2019 00:42:42 - INFO - __main__ - f1 = 0.8614389652384803 -10/04/2019 00:42:42 - INFO - __main__ - loss = 0.07064602487454782 -10/04/2019 00:42:42 - INFO - __main__ - precision = 0.8604651162790697 -10/04/2019 00:42:42 - INFO - __main__ - recall = 0.8624150210424085 -``` - -#### Run the Tensorflow 2 version - -To start training, just run: - -```bash -python3 run_tf_ner.py --data_dir ./ \ ---labels ./labels.txt \ ---model_name_or_path $BERT_MODEL \ ---output_dir $OUTPUT_DIR \ ---max_seq_length $MAX_LENGTH \ ---num_train_epochs $NUM_EPOCHS \ ---per_device_train_batch_size $BATCH_SIZE \ ---save_steps $SAVE_STEPS \ ---seed $SEED \ ---do_train \ ---do_eval \ ---do_predict -``` - -Such as the Pytorch version, if your GPU supports half-precision training, just add the `--fp16` flag. After training, the model will be both evaluated on development and test datasets. - -#### Evaluation - -Evaluation on development dataset outputs the following for our example: -```bash - precision recall f1-score support - - LOCderiv 0.7619 0.6154 0.6809 52 - PERpart 0.8724 0.8997 0.8858 4057 - OTHpart 0.9360 0.9466 0.9413 711 - ORGpart 0.7015 0.6989 0.7002 269 - LOCpart 0.7668 0.8488 0.8057 496 - LOC 0.8745 0.9191 0.8963 235 - ORGderiv 0.7723 0.8571 0.8125 91 - OTHderiv 0.4800 0.6667 0.5581 18 - OTH 0.5789 0.6875 0.6286 16 - PERderiv 0.5385 0.3889 0.4516 18 - PER 0.5000 0.5000 0.5000 2 - ORG 0.0000 0.0000 0.0000 3 - -micro avg 0.8574 0.8862 0.8715 5968 -macro avg 0.8575 0.8862 0.8713 5968 -``` - -On the test dataset the following results could be achieved: -```bash - precision recall f1-score support - - PERpart 0.8847 0.8944 0.8896 9397 - OTHpart 0.9376 0.9353 0.9365 1639 - ORGpart 0.7307 0.7044 0.7173 697 - LOC 0.9133 0.9394 0.9262 561 - LOCpart 0.8058 0.8157 0.8107 1150 - ORG 0.0000 0.0000 0.0000 8 - OTHderiv 0.5882 0.4762 0.5263 42 - PERderiv 0.6571 0.5227 0.5823 44 - OTH 0.4906 0.6667 0.5652 39 - ORGderiv 0.7016 0.7791 0.7383 172 - LOCderiv 0.8256 0.6514 0.7282 109 - PER 0.0000 0.0000 0.0000 11 - -micro avg 0.8722 0.8774 0.8748 13869 -macro avg 0.8712 0.8774 0.8740 13869 -``` - -### Emerging and Rare Entities task: WNUT’17 (English NER) dataset - -Description of the WNUT’17 task from the [shared task website](http://noisy-text.github.io/2017/index.html): - -> The WNUT’17 shared task focuses on identifying unusual, previously-unseen entities in the context of emerging discussions. -> Named entities form the basis of many modern approaches to other tasks (like event clustering and summarization), but recall on -> them is a real problem in noisy text - even among annotators. This drop tends to be due to novel entities and surface forms. - -Six labels are available in the dataset. An overview can be found on this [page](http://noisy-text.github.io/2017/files/). - -#### Data (Download and pre-processing steps) - -The dataset can be downloaded from the [official GitHub](https://github.com/leondz/emerging_entities_17) repository. - -The following commands show how to prepare the dataset for fine-tuning: - -```bash -mkdir -p data_wnut_17 - -curl -L 'https://github.com/leondz/emerging_entities_17/raw/master/wnut17train.conll' | tr '\t' ' ' > data_wnut_17/train.txt.tmp -curl -L 'https://github.com/leondz/emerging_entities_17/raw/master/emerging.dev.conll' | tr '\t' ' ' > data_wnut_17/dev.txt.tmp -curl -L 'https://raw.githubusercontent.com/leondz/emerging_entities_17/master/emerging.test.annotated' | tr '\t' ' ' > data_wnut_17/test.txt.tmp -``` - -Let's define some variables that we need for further pre-processing steps: - -```bash -export MAX_LENGTH=128 -export BERT_MODEL=bert-large-cased -``` - -Here we use the English BERT large model for fine-tuning. -The `preprocess.py` scripts splits longer sentences into smaller ones (once the max. subtoken length is reached): - -```bash -python3 scripts/preprocess.py data_wnut_17/train.txt.tmp $BERT_MODEL $MAX_LENGTH > data_wnut_17/train.txt -python3 scripts/preprocess.py data_wnut_17/dev.txt.tmp $BERT_MODEL $MAX_LENGTH > data_wnut_17/dev.txt -python3 scripts/preprocess.py data_wnut_17/test.txt.tmp $BERT_MODEL $MAX_LENGTH > data_wnut_17/test.txt -``` - -In the last pre-processing step, the `labels.txt` file needs to be generated. This file contains all available labels: - -```bash -cat data_wnut_17/train.txt data_wnut_17/dev.txt data_wnut_17/test.txt | cut -d " " -f 2 | grep -v "^$"| sort | uniq > data_wnut_17/labels.txt -``` - -#### Run the Pytorch version - -Fine-tuning with the PyTorch version can be started using the `run_ner.py` script. In this example we use a JSON-based configuration file. - -This configuration file looks like: - -```json -{ - "data_dir": "./data_wnut_17", - "labels": "./data_wnut_17/labels.txt", - "model_name_or_path": "bert-large-cased", - "output_dir": "wnut-17-model-1", - "max_seq_length": 128, - "num_train_epochs": 3, - "per_device_train_batch_size": 32, - "save_steps": 425, - "seed": 1, - "do_train": true, - "do_eval": true, - "do_predict": true, - "fp16": false -} -``` - -If your GPU supports half-precision training, please set `fp16` to `true`. - -Save this JSON-based configuration under `wnut_17.json`. The fine-tuning can be started with `python3 run_ner_old.py wnut_17.json`. - -#### Evaluation - -Evaluation on development dataset outputs the following: - -```bash -05/29/2020 23:33:44 - INFO - __main__ - ***** Eval results ***** -05/29/2020 23:33:44 - INFO - __main__ - eval_loss = 0.26505235286212275 -05/29/2020 23:33:44 - INFO - __main__ - eval_precision = 0.7008264462809918 -05/29/2020 23:33:44 - INFO - __main__ - eval_recall = 0.507177033492823 -05/29/2020 23:33:44 - INFO - __main__ - eval_f1 = 0.5884802220680084 -05/29/2020 23:33:44 - INFO - __main__ - epoch = 3.0 -``` - -On the test dataset the following results could be achieved: - -```bash -05/29/2020 23:33:44 - INFO - transformers.trainer - ***** Running Prediction ***** -05/29/2020 23:34:02 - INFO - __main__ - eval_loss = 0.30948806500973547 -05/29/2020 23:34:02 - INFO - __main__ - eval_precision = 0.5840108401084011 -05/29/2020 23:34:02 - INFO - __main__ - eval_recall = 0.3994439295644115 -05/29/2020 23:34:02 - INFO - __main__ - eval_f1 = 0.47440836543753434 -``` - -WNUT’17 is a very difficult task. Current state-of-the-art results on this dataset can be found [here](http://nlpprogress.com/english/named_entity_recognition.html). diff --git a/examples/pytorch/huggingface_models/examples/legacy/token-classification/run.sh b/examples/pytorch/huggingface_models/examples/legacy/token-classification/run.sh deleted file mode 100755 index f5cbf0d50e0..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/token-classification/run.sh +++ /dev/null @@ -1,36 +0,0 @@ -## The relevant files are currently on a shared Google -## drive at https://drive.google.com/drive/folders/1kC0I2UGl2ltrluI9NqDjaQJGw5iliw_J -## Monitor for changes and eventually migrate to nlp dataset -curl -L 'https://drive.google.com/uc?export=download&id=1Jjhbal535VVz2ap4v4r_rN1UEHTdLK5P' \ -| grep -v "^#" | cut -f 2,3 | tr '\t' ' ' > train.txt.tmp -curl -L 'https://drive.google.com/uc?export=download&id=1ZfRcQThdtAR5PPRjIDtrVP7BtXSCUBbm' \ -| grep -v "^#" | cut -f 2,3 | tr '\t' ' ' > dev.txt.tmp -curl -L 'https://drive.google.com/uc?export=download&id=1u9mb7kNJHWQCWyweMDRMuTFoOHOfeBTH' \ -| grep -v "^#" | cut -f 2,3 | tr '\t' ' ' > test.txt.tmp - -export MAX_LENGTH=128 -export BERT_MODEL=bert-base-multilingual-cased -python3 scripts/preprocess.py train.txt.tmp $BERT_MODEL $MAX_LENGTH > train.txt -python3 scripts/preprocess.py dev.txt.tmp $BERT_MODEL $MAX_LENGTH > dev.txt -python3 scripts/preprocess.py test.txt.tmp $BERT_MODEL $MAX_LENGTH > test.txt -cat train.txt dev.txt test.txt | cut -d " " -f 2 | grep -v "^$"| sort | uniq > labels.txt -export OUTPUT_DIR=germeval-model -export BATCH_SIZE=32 -export NUM_EPOCHS=3 -export SAVE_STEPS=750 -export SEED=1 - -python3 run_ner.py \ ---task_type NER \ ---data_dir . \ ---labels ./labels.txt \ ---model_name_or_path $BERT_MODEL \ ---output_dir $OUTPUT_DIR \ ---max_seq_length $MAX_LENGTH \ ---num_train_epochs $NUM_EPOCHS \ ---per_gpu_train_batch_size $BATCH_SIZE \ ---save_steps $SAVE_STEPS \ ---seed $SEED \ ---do_train \ ---do_eval \ ---do_predict diff --git a/examples/pytorch/huggingface_models/examples/legacy/token-classification/run_chunk.sh b/examples/pytorch/huggingface_models/examples/legacy/token-classification/run_chunk.sh deleted file mode 100755 index 13341555b69..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/token-classification/run_chunk.sh +++ /dev/null @@ -1,37 +0,0 @@ -if ! [ -f ./dev.txt ]; then - echo "Downloading CONLL2003 dev dataset...." - curl -L -o ./dev.txt 'https://github.com/davidsbatista/NER-datasets/raw/master/CONLL2003/valid.txt' -fi - -if ! [ -f ./test.txt ]; then - echo "Downloading CONLL2003 test dataset...." - curl -L -o ./test.txt 'https://github.com/davidsbatista/NER-datasets/raw/master/CONLL2003/test.txt' -fi - -if ! [ -f ./train.txt ]; then - echo "Downloading CONLL2003 train dataset...." - curl -L -o ./train.txt 'https://github.com/davidsbatista/NER-datasets/raw/master/CONLL2003/train.txt' -fi - -export MAX_LENGTH=200 -export BERT_MODEL=bert-base-uncased -export OUTPUT_DIR=chunker-model -export BATCH_SIZE=32 -export NUM_EPOCHS=3 -export SAVE_STEPS=750 -export SEED=1 - -python3 run_ner.py \ ---task_type Chunk \ ---data_dir . \ ---model_name_or_path $BERT_MODEL \ ---output_dir $OUTPUT_DIR \ ---max_seq_length $MAX_LENGTH \ ---num_train_epochs $NUM_EPOCHS \ ---per_gpu_train_batch_size $BATCH_SIZE \ ---save_steps $SAVE_STEPS \ ---seed $SEED \ ---do_train \ ---do_eval \ ---do_predict - diff --git a/examples/pytorch/huggingface_models/examples/legacy/token-classification/run_ner.py b/examples/pytorch/huggingface_models/examples/legacy/token-classification/run_ner.py deleted file mode 100644 index 983c60ee7d2..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/token-classification/run_ner.py +++ /dev/null @@ -1,321 +0,0 @@ -# coding=utf-8 -# Copyright 2018 The Google AI Language Team Authors and The HuggingFace Inc. team. -# Copyright (c) 2018, NVIDIA CORPORATION. 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. -""" Fine-tuning the library models for named entity recognition on CoNLL-2003. """ -import logging -import os -import sys -from dataclasses import dataclass, field -from importlib import import_module -from typing import Dict, List, Optional, Tuple - -import numpy as np -from seqeval.metrics import accuracy_score, f1_score, precision_score, recall_score -from torch import nn - -import transformers -from transformers import ( - AutoConfig, - AutoModelForTokenClassification, - AutoTokenizer, - DataCollatorWithPadding, - EvalPrediction, - HfArgumentParser, - Trainer, - TrainingArguments, - set_seed, -) -from transformers.trainer_utils import is_main_process -from utils_ner import Split, TokenClassificationDataset, TokenClassificationTask - - -logger = logging.getLogger(__name__) - - -@dataclass -class ModelArguments: - """ - Arguments pertaining to which model/config/tokenizer we are going to fine-tune from. - """ - - model_name_or_path: str = field( - metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"} - ) - config_name: Optional[str] = field( - default=None, metadata={"help": "Pretrained config name or path if not the same as model_name"} - ) - task_type: Optional[str] = field( - default="NER", metadata={"help": "Task type to fine tune in training (e.g. NER, POS, etc)"} - ) - tokenizer_name: Optional[str] = field( - default=None, metadata={"help": "Pretrained tokenizer name or path if not the same as model_name"} - ) - use_fast: bool = field(default=False, metadata={"help": "Set this flag to use fast tokenization."}) - # If you want to tweak more attributes on your tokenizer, you should do it in a distinct script, - # or just modify its tokenizer_config.json. - cache_dir: Optional[str] = field( - default=None, - metadata={"help": "Where do you want to store the pretrained models downloaded from huggingface.co"}, - ) - - -@dataclass -class DataTrainingArguments: - """ - Arguments pertaining to what data we are going to input our model for training and eval. - """ - - data_dir: str = field( - metadata={"help": "The input data dir. Should contain the .txt files for a CoNLL-2003-formatted task."} - ) - labels: Optional[str] = field( - default=None, - metadata={"help": "Path to a file containing all labels. If not specified, CoNLL-2003 labels are used."}, - ) - max_seq_length: int = field( - default=128, - metadata={ - "help": "The maximum total input sequence length after tokenization. Sequences longer " - "than this will be truncated, sequences shorter will be padded." - }, - ) - overwrite_cache: bool = field( - default=False, metadata={"help": "Overwrite the cached training and evaluation sets"} - ) - - -def main(): - # See all possible arguments in src/transformers/training_args.py - # or by passing the --help flag to this script. - # We now keep distinct sets of args, for a cleaner separation of concerns. - - parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments)) - if len(sys.argv) == 2 and sys.argv[1].endswith(".json"): - # If we pass only one argument to the script and it's the path to a json file, - # let's parse it to get our arguments. - model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1])) - else: - model_args, data_args, training_args = parser.parse_args_into_dataclasses() - - if ( - os.path.exists(training_args.output_dir) - and os.listdir(training_args.output_dir) - and training_args.do_train - and not training_args.overwrite_output_dir - ): - raise ValueError( - f"Output directory ({training_args.output_dir}) already exists and is not empty. Use --overwrite_output_dir to overcome." - ) - - module = import_module("tasks") - try: - token_classification_task_clazz = getattr(module, model_args.task_type) - token_classification_task: TokenClassificationTask = token_classification_task_clazz() - except AttributeError: - raise ValueError( - f"Task {model_args.task_type} needs to be defined as a TokenClassificationTask subclass in {module}. " - f"Available tasks classes are: {TokenClassificationTask.__subclasses__()}" - ) - - # Setup logging - logging.basicConfig( - format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", - datefmt="%m/%d/%Y %H:%M:%S", - level=logging.INFO if training_args.local_rank in [-1, 0] else logging.WARN, - ) - logger.warning( - "Process rank: %s, device: %s, n_gpu: %s, distributed training: %s, 16-bits training: %s", - training_args.local_rank, - training_args.device, - training_args.n_gpu, - bool(training_args.local_rank != -1), - training_args.fp16, - ) - # Set the verbosity to info of the Transformers logger (on main process only): - if is_main_process(training_args.local_rank): - transformers.utils.logging.set_verbosity_info() - transformers.utils.logging.enable_default_handler() - transformers.utils.logging.enable_explicit_format() - logger.info("Training/evaluation parameters %s", training_args) - - # Set seed - set_seed(training_args.seed) - - # Prepare CONLL-2003 task - labels = token_classification_task.get_labels(data_args.labels) - label_map: Dict[int, str] = {i: label for i, label in enumerate(labels)} - num_labels = len(labels) - - # Load pretrained model and tokenizer - # - # Distributed training: - # The .from_pretrained methods guarantee that only one local process can concurrently - # download model & vocab. - - config = AutoConfig.from_pretrained( - model_args.config_name if model_args.config_name else model_args.model_name_or_path, - num_labels=num_labels, - id2label=label_map, - label2id={label: i for i, label in enumerate(labels)}, - cache_dir=model_args.cache_dir, - ) - tokenizer = AutoTokenizer.from_pretrained( - model_args.tokenizer_name if model_args.tokenizer_name else model_args.model_name_or_path, - cache_dir=model_args.cache_dir, - use_fast=model_args.use_fast, - ) - model = AutoModelForTokenClassification.from_pretrained( - model_args.model_name_or_path, - from_tf=bool(".ckpt" in model_args.model_name_or_path), - config=config, - cache_dir=model_args.cache_dir, - ) - - # Get datasets - train_dataset = ( - TokenClassificationDataset( - token_classification_task=token_classification_task, - data_dir=data_args.data_dir, - tokenizer=tokenizer, - labels=labels, - model_type=config.model_type, - max_seq_length=data_args.max_seq_length, - overwrite_cache=data_args.overwrite_cache, - mode=Split.train, - ) - if training_args.do_train - else None - ) - eval_dataset = ( - TokenClassificationDataset( - token_classification_task=token_classification_task, - data_dir=data_args.data_dir, - tokenizer=tokenizer, - labels=labels, - model_type=config.model_type, - max_seq_length=data_args.max_seq_length, - overwrite_cache=data_args.overwrite_cache, - mode=Split.dev, - ) - if training_args.do_eval - else None - ) - - def align_predictions(predictions: np.ndarray, label_ids: np.ndarray) -> Tuple[List[int], List[int]]: - preds = np.argmax(predictions, axis=2) - - batch_size, seq_len = preds.shape - - out_label_list = [[] for _ in range(batch_size)] - preds_list = [[] for _ in range(batch_size)] - - for i in range(batch_size): - for j in range(seq_len): - if label_ids[i, j] != nn.CrossEntropyLoss().ignore_index: - out_label_list[i].append(label_map[label_ids[i][j]]) - preds_list[i].append(label_map[preds[i][j]]) - - return preds_list, out_label_list - - def compute_metrics(p: EvalPrediction) -> Dict: - preds_list, out_label_list = align_predictions(p.predictions, p.label_ids) - return { - "accuracy_score": accuracy_score(out_label_list, preds_list), - "precision": precision_score(out_label_list, preds_list), - "recall": recall_score(out_label_list, preds_list), - "f1": f1_score(out_label_list, preds_list), - } - - # Data collator - data_collator = DataCollatorWithPadding(tokenizer, pad_to_multiple_of=8) if training_args.fp16 else None - - # Initialize our Trainer - trainer = Trainer( - model=model, - args=training_args, - train_dataset=train_dataset, - eval_dataset=eval_dataset, - compute_metrics=compute_metrics, - data_collator=data_collator, - ) - - # Training - if training_args.do_train: - trainer.train( - model_path=model_args.model_name_or_path if os.path.isdir(model_args.model_name_or_path) else None - ) - trainer.save_model() - # For convenience, we also re-save the tokenizer to the same directory, - # so that you can share your model easily on huggingface.co/models =) - if trainer.is_world_process_zero(): - tokenizer.save_pretrained(training_args.output_dir) - - # Evaluation - results = {} - if training_args.do_eval: - logger.info("*** Evaluate ***") - - result = trainer.evaluate() - - output_eval_file = os.path.join(training_args.output_dir, "eval_results.txt") - if trainer.is_world_process_zero(): - with open(output_eval_file, "w") as writer: - logger.info("***** Eval results *****") - for key, value in result.items(): - logger.info(" %s = %s", key, value) - writer.write("%s = %s\n" % (key, value)) - - results.update(result) - - # Predict - if training_args.do_predict: - test_dataset = TokenClassificationDataset( - token_classification_task=token_classification_task, - data_dir=data_args.data_dir, - tokenizer=tokenizer, - labels=labels, - model_type=config.model_type, - max_seq_length=data_args.max_seq_length, - overwrite_cache=data_args.overwrite_cache, - mode=Split.test, - ) - - predictions, label_ids, metrics = trainer.predict(test_dataset) - preds_list, _ = align_predictions(predictions, label_ids) - - output_test_results_file = os.path.join(training_args.output_dir, "test_results.txt") - if trainer.is_world_process_zero(): - with open(output_test_results_file, "w") as writer: - for key, value in metrics.items(): - logger.info(" %s = %s", key, value) - writer.write("%s = %s\n" % (key, value)) - - # Save predictions - output_test_predictions_file = os.path.join(training_args.output_dir, "test_predictions.txt") - if trainer.is_world_process_zero(): - with open(output_test_predictions_file, "w") as writer: - with open(os.path.join(data_args.data_dir, "test.txt"), "r") as f: - token_classification_task.write_predictions_to_file(writer, f, preds_list) - - return results - - -def _mp_fn(index): - # For xla_spawn (TPUs) - main() - - -if __name__ == "__main__": - main() diff --git a/examples/pytorch/huggingface_models/examples/legacy/token-classification/run_pos.sh b/examples/pytorch/huggingface_models/examples/legacy/token-classification/run_pos.sh deleted file mode 100755 index 7d76ed8a2a8..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/token-classification/run_pos.sh +++ /dev/null @@ -1,37 +0,0 @@ -if ! [ -f ./dev.txt ]; then - echo "Download dev dataset...." - curl -L -o ./dev.txt 'https://github.com/UniversalDependencies/UD_English-EWT/raw/master/en_ewt-ud-dev.conllu' -fi - -if ! [ -f ./test.txt ]; then - echo "Download test dataset...." - curl -L -o ./test.txt 'https://github.com/UniversalDependencies/UD_English-EWT/raw/master/en_ewt-ud-test.conllu' -fi - -if ! [ -f ./train.txt ]; then - echo "Download train dataset...." - curl -L -o ./train.txt 'https://github.com/UniversalDependencies/UD_English-EWT/raw/master/en_ewt-ud-train.conllu' -fi - -export MAX_LENGTH=200 -export BERT_MODEL=bert-base-uncased -export OUTPUT_DIR=postagger-model -export BATCH_SIZE=32 -export NUM_EPOCHS=3 -export SAVE_STEPS=750 -export SEED=1 - -python3 run_ner.py \ ---task_type POS \ ---data_dir . \ ---model_name_or_path $BERT_MODEL \ ---output_dir $OUTPUT_DIR \ ---max_seq_length $MAX_LENGTH \ ---num_train_epochs $NUM_EPOCHS \ ---per_gpu_train_batch_size $BATCH_SIZE \ ---save_steps $SAVE_STEPS \ ---seed $SEED \ ---do_train \ ---do_eval \ ---do_predict - diff --git a/examples/pytorch/huggingface_models/examples/legacy/token-classification/run_tf_ner.py b/examples/pytorch/huggingface_models/examples/legacy/token-classification/run_tf_ner.py deleted file mode 100644 index 93fe93617fb..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/token-classification/run_tf_ner.py +++ /dev/null @@ -1,307 +0,0 @@ -#!/usr/bin/env python -# coding=utf-8 -# Copyright 2018 The HuggingFace Inc. team. -# -# 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. -""" Fine-tuning the library models for named entity recognition.""" - - -import logging -import os -from dataclasses import dataclass, field -from importlib import import_module -from typing import Dict, List, Optional, Tuple - -import numpy as np -from seqeval.metrics import classification_report, f1_score, precision_score, recall_score - -from transformers import ( - AutoConfig, - AutoTokenizer, - EvalPrediction, - HfArgumentParser, - TFAutoModelForTokenClassification, - TFTrainer, - TFTrainingArguments, -) -from transformers.utils import logging as hf_logging -from utils_ner import Split, TFTokenClassificationDataset, TokenClassificationTask - - -hf_logging.set_verbosity_info() -hf_logging.enable_default_handler() -hf_logging.enable_explicit_format() - - -logger = logging.getLogger(__name__) - - -@dataclass -class ModelArguments: - """ - Arguments pertaining to which model/config/tokenizer we are going to fine-tune from. - """ - - model_name_or_path: str = field( - metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"} - ) - config_name: Optional[str] = field( - default=None, metadata={"help": "Pretrained config name or path if not the same as model_name"} - ) - task_type: Optional[str] = field( - default="NER", metadata={"help": "Task type to fine tune in training (e.g. NER, POS, etc)"} - ) - tokenizer_name: Optional[str] = field( - default=None, metadata={"help": "Pretrained tokenizer name or path if not the same as model_name"} - ) - use_fast: bool = field(default=False, metadata={"help": "Set this flag to use fast tokenization."}) - # If you want to tweak more attributes on your tokenizer, you should do it in a distinct script, - # or just modify its tokenizer_config.json. - cache_dir: Optional[str] = field( - default=None, - metadata={"help": "Where do you want to store the pretrained models downloaded from huggingface.co"}, - ) - - -@dataclass -class DataTrainingArguments: - """ - Arguments pertaining to what data we are going to input our model for training and eval. - """ - - data_dir: str = field( - metadata={"help": "The input data dir. Should contain the .txt files for a CoNLL-2003-formatted task."} - ) - labels: Optional[str] = field( - metadata={"help": "Path to a file containing all labels. If not specified, CoNLL-2003 labels are used."} - ) - max_seq_length: int = field( - default=128, - metadata={ - "help": "The maximum total input sequence length after tokenization. Sequences longer " - "than this will be truncated, sequences shorter will be padded." - }, - ) - overwrite_cache: bool = field( - default=False, metadata={"help": "Overwrite the cached training and evaluation sets"} - ) - - -def main(): - # See all possible arguments in src/transformers/training_args.py - # or by passing the --help flag to this script. - # We now keep distinct sets of args, for a cleaner separation of concerns. - parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TFTrainingArguments)) - model_args, data_args, training_args = parser.parse_args_into_dataclasses() - - if ( - os.path.exists(training_args.output_dir) - and os.listdir(training_args.output_dir) - and training_args.do_train - and not training_args.overwrite_output_dir - ): - raise ValueError( - f"Output directory ({training_args.output_dir}) already exists and is not empty. Use --overwrite_output_dir to overcome." - ) - - module = import_module("tasks") - - try: - token_classification_task_clazz = getattr(module, model_args.task_type) - token_classification_task: TokenClassificationTask = token_classification_task_clazz() - except AttributeError: - raise ValueError( - f"Task {model_args.task_type} needs to be defined as a TokenClassificationTask subclass in {module}. " - f"Available tasks classes are: {TokenClassificationTask.__subclasses__()}" - ) - - # Setup logging - logging.basicConfig( - format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", - datefmt="%m/%d/%Y %H:%M:%S", - level=logging.INFO, - ) - logger.info( - "n_replicas: %s, distributed training: %s, 16-bits training: %s", - training_args.n_replicas, - bool(training_args.n_replicas > 1), - training_args.fp16, - ) - logger.info("Training/evaluation parameters %s", training_args) - - # Prepare Token Classification task - labels = token_classification_task.get_labels(data_args.labels) - label_map: Dict[int, str] = {i: label for i, label in enumerate(labels)} - num_labels = len(labels) - - # Load pretrained model and tokenizer - # - # Distributed training: - # The .from_pretrained methods guarantee that only one local process can concurrently - # download model & vocab. - - config = AutoConfig.from_pretrained( - model_args.config_name if model_args.config_name else model_args.model_name_or_path, - num_labels=num_labels, - id2label=label_map, - label2id={label: i for i, label in enumerate(labels)}, - cache_dir=model_args.cache_dir, - ) - tokenizer = AutoTokenizer.from_pretrained( - model_args.tokenizer_name if model_args.tokenizer_name else model_args.model_name_or_path, - cache_dir=model_args.cache_dir, - use_fast=model_args.use_fast, - ) - - with training_args.strategy.scope(): - model = TFAutoModelForTokenClassification.from_pretrained( - model_args.model_name_or_path, - from_pt=bool(".bin" in model_args.model_name_or_path), - config=config, - cache_dir=model_args.cache_dir, - ) - - # Get datasets - train_dataset = ( - TFTokenClassificationDataset( - token_classification_task=token_classification_task, - data_dir=data_args.data_dir, - tokenizer=tokenizer, - labels=labels, - model_type=config.model_type, - max_seq_length=data_args.max_seq_length, - overwrite_cache=data_args.overwrite_cache, - mode=Split.train, - ) - if training_args.do_train - else None - ) - eval_dataset = ( - TFTokenClassificationDataset( - token_classification_task=token_classification_task, - data_dir=data_args.data_dir, - tokenizer=tokenizer, - labels=labels, - model_type=config.model_type, - max_seq_length=data_args.max_seq_length, - overwrite_cache=data_args.overwrite_cache, - mode=Split.dev, - ) - if training_args.do_eval - else None - ) - - def align_predictions(predictions: np.ndarray, label_ids: np.ndarray) -> Tuple[List[int], List[int]]: - preds = np.argmax(predictions, axis=2) - batch_size, seq_len = preds.shape - out_label_list = [[] for _ in range(batch_size)] - preds_list = [[] for _ in range(batch_size)] - - for i in range(batch_size): - for j in range(seq_len): - if label_ids[i, j] != -100: - out_label_list[i].append(label_map[label_ids[i][j]]) - preds_list[i].append(label_map[preds[i][j]]) - - return preds_list, out_label_list - - def compute_metrics(p: EvalPrediction) -> Dict: - preds_list, out_label_list = align_predictions(p.predictions, p.label_ids) - - return { - "precision": precision_score(out_label_list, preds_list), - "recall": recall_score(out_label_list, preds_list), - "f1": f1_score(out_label_list, preds_list), - } - - # Initialize our Trainer - trainer = TFTrainer( - model=model, - args=training_args, - train_dataset=train_dataset.get_dataset() if train_dataset else None, - eval_dataset=eval_dataset.get_dataset() if eval_dataset else None, - compute_metrics=compute_metrics, - ) - - # Training - if training_args.do_train: - trainer.train() - trainer.save_model() - tokenizer.save_pretrained(training_args.output_dir) - - # Evaluation - results = {} - if training_args.do_eval: - logger.info("*** Evaluate ***") - - result = trainer.evaluate() - output_eval_file = os.path.join(training_args.output_dir, "eval_results.txt") - - with open(output_eval_file, "w") as writer: - logger.info("***** Eval results *****") - - for key, value in result.items(): - logger.info(" %s = %s", key, value) - writer.write("%s = %s\n" % (key, value)) - - results.update(result) - - # Predict - if training_args.do_predict: - test_dataset = TFTokenClassificationDataset( - token_classification_task=token_classification_task, - data_dir=data_args.data_dir, - tokenizer=tokenizer, - labels=labels, - model_type=config.model_type, - max_seq_length=data_args.max_seq_length, - overwrite_cache=data_args.overwrite_cache, - mode=Split.test, - ) - - predictions, label_ids, metrics = trainer.predict(test_dataset.get_dataset()) - preds_list, labels_list = align_predictions(predictions, label_ids) - report = classification_report(labels_list, preds_list) - - logger.info("\n%s", report) - - output_test_results_file = os.path.join(training_args.output_dir, "test_results.txt") - - with open(output_test_results_file, "w") as writer: - writer.write("%s\n" % report) - - # Save predictions - output_test_predictions_file = os.path.join(training_args.output_dir, "test_predictions.txt") - - with open(output_test_predictions_file, "w") as writer: - with open(os.path.join(data_args.data_dir, "test.txt"), "r") as f: - example_id = 0 - - for line in f: - if line.startswith("-DOCSTART-") or line == "" or line == "\n": - writer.write(line) - - if not preds_list[example_id]: - example_id += 1 - elif preds_list[example_id]: - output_line = line.split()[0] + " " + preds_list[example_id].pop(0) + "\n" - - writer.write(output_line) - else: - logger.warning("Maximum sequence length exceeded: No prediction for '%s'.", line.split()[0]) - - return results - - -if __name__ == "__main__": - main() diff --git a/examples/pytorch/huggingface_models/examples/legacy/token-classification/scripts/preprocess.py b/examples/pytorch/huggingface_models/examples/legacy/token-classification/scripts/preprocess.py deleted file mode 100644 index 4eaa4fe2f3b..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/token-classification/scripts/preprocess.py +++ /dev/null @@ -1,41 +0,0 @@ -import sys - -from transformers import AutoTokenizer - - -dataset = sys.argv[1] -model_name_or_path = sys.argv[2] -max_len = int(sys.argv[3]) - -subword_len_counter = 0 - -tokenizer = AutoTokenizer.from_pretrained(model_name_or_path) -max_len -= tokenizer.num_special_tokens_to_add() - -with open(dataset, "rt") as f_p: - for line in f_p: - line = line.rstrip() - - if not line: - print(line) - subword_len_counter = 0 - continue - - token = line.split()[0] - - current_subwords_len = len(tokenizer.tokenize(token)) - - # Token contains strange control characters like \x96 or \x95 - # Just filter out the complete line - if current_subwords_len == 0: - continue - - if (subword_len_counter + current_subwords_len) > max_len: - print("") - print(line) - subword_len_counter = current_subwords_len - continue - - subword_len_counter += current_subwords_len - - print(line) diff --git a/examples/pytorch/huggingface_models/examples/legacy/token-classification/tasks.py b/examples/pytorch/huggingface_models/examples/legacy/token-classification/tasks.py deleted file mode 100644 index 409be0715da..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/token-classification/tasks.py +++ /dev/null @@ -1,163 +0,0 @@ -import logging -import os -from typing import List, TextIO, Union - -from conllu import parse_incr - -from utils_ner import InputExample, Split, TokenClassificationTask - - -logger = logging.getLogger(__name__) - - -class NER(TokenClassificationTask): - def __init__(self, label_idx=-1): - # in NER datasets, the last column is usually reserved for NER label - self.label_idx = label_idx - - def read_examples_from_file(self, data_dir, mode: Union[Split, str]) -> List[InputExample]: - if isinstance(mode, Split): - mode = mode.value - file_path = os.path.join(data_dir, f"{mode}.txt") - guid_index = 1 - examples = [] - with open(file_path, encoding="utf-8") as f: - words = [] - labels = [] - for line in f: - if line.startswith("-DOCSTART-") or line == "" or line == "\n": - if words: - examples.append(InputExample(guid=f"{mode}-{guid_index}", words=words, labels=labels)) - guid_index += 1 - words = [] - labels = [] - else: - splits = line.split(" ") - words.append(splits[0]) - if len(splits) > 1: - labels.append(splits[self.label_idx].replace("\n", "")) - else: - # Examples could have no label for mode = "test" - labels.append("O") - if words: - examples.append(InputExample(guid=f"{mode}-{guid_index}", words=words, labels=labels)) - return examples - - def write_predictions_to_file(self, writer: TextIO, test_input_reader: TextIO, preds_list: List): - example_id = 0 - for line in test_input_reader: - if line.startswith("-DOCSTART-") or line == "" or line == "\n": - writer.write(line) - if not preds_list[example_id]: - example_id += 1 - elif preds_list[example_id]: - output_line = line.split()[0] + " " + preds_list[example_id].pop(0) + "\n" - writer.write(output_line) - else: - logger.warning("Maximum sequence length exceeded: No prediction for '%s'.", line.split()[0]) - - def get_labels(self, path: str) -> List[str]: - if path: - with open(path, "r") as f: - labels = f.read().splitlines() - if "O" not in labels: - labels = ["O"] + labels - return labels - else: - return ["O", "B-MISC", "I-MISC", "B-PER", "I-PER", "B-ORG", "I-ORG", "B-LOC", "I-LOC"] - - -class Chunk(NER): - def __init__(self): - # in CONLL2003 dataset chunk column is second-to-last - super().__init__(label_idx=-2) - - def get_labels(self, path: str) -> List[str]: - if path: - with open(path, "r") as f: - labels = f.read().splitlines() - if "O" not in labels: - labels = ["O"] + labels - return labels - else: - return [ - "O", - "B-ADVP", - "B-INTJ", - "B-LST", - "B-PRT", - "B-NP", - "B-SBAR", - "B-VP", - "B-ADJP", - "B-CONJP", - "B-PP", - "I-ADVP", - "I-INTJ", - "I-LST", - "I-PRT", - "I-NP", - "I-SBAR", - "I-VP", - "I-ADJP", - "I-CONJP", - "I-PP", - ] - - -class POS(TokenClassificationTask): - def read_examples_from_file(self, data_dir, mode: Union[Split, str]) -> List[InputExample]: - if isinstance(mode, Split): - mode = mode.value - file_path = os.path.join(data_dir, f"{mode}.txt") - guid_index = 1 - examples = [] - - with open(file_path, encoding="utf-8") as f: - for sentence in parse_incr(f): - words = [] - labels = [] - for token in sentence: - words.append(token["form"]) - labels.append(token["upos"]) - assert len(words) == len(labels) - if words: - examples.append(InputExample(guid=f"{mode}-{guid_index}", words=words, labels=labels)) - guid_index += 1 - return examples - - def write_predictions_to_file(self, writer: TextIO, test_input_reader: TextIO, preds_list: List): - example_id = 0 - for sentence in parse_incr(test_input_reader): - s_p = preds_list[example_id] - out = "" - for token in sentence: - out += f'{token["form"]} ({token["upos"]}|{s_p.pop(0)}) ' - out += "\n" - writer.write(out) - example_id += 1 - - def get_labels(self, path: str) -> List[str]: - if path: - with open(path, "r") as f: - return f.read().splitlines() - else: - return [ - "ADJ", - "ADP", - "ADV", - "AUX", - "CCONJ", - "DET", - "INTJ", - "NOUN", - "NUM", - "PART", - "PRON", - "PROPN", - "PUNCT", - "SCONJ", - "SYM", - "VERB", - "X", - ] diff --git a/examples/pytorch/huggingface_models/examples/legacy/token-classification/utils_ner.py b/examples/pytorch/huggingface_models/examples/legacy/token-classification/utils_ner.py deleted file mode 100644 index 837d63002db..00000000000 --- a/examples/pytorch/huggingface_models/examples/legacy/token-classification/utils_ner.py +++ /dev/null @@ -1,372 +0,0 @@ -# coding=utf-8 -# Copyright 2018 The Google AI Language Team Authors and The HuggingFace Inc. team. -# Copyright (c) 2018, NVIDIA CORPORATION. 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. -""" Named entity recognition fine-tuning: utilities to work with CoNLL-2003 task. """ - - -import logging -import os -from dataclasses import dataclass -from enum import Enum -from typing import List, Optional, Union - -from filelock import FileLock -from transformers import PreTrainedTokenizer, is_tf_available, is_torch_available - - -logger = logging.getLogger(__name__) - - -@dataclass -class InputExample: - """ - A single training/test example for token classification. - - Args: - guid: Unique id for the example. - words: list. The words of the sequence. - labels: (Optional) list. The labels for each word of the sequence. This should be - specified for train and dev examples, but not for test examples. - """ - - guid: str - words: List[str] - labels: Optional[List[str]] - - -@dataclass -class InputFeatures: - """ - A single set of features of data. - Property names are the same names as the corresponding inputs to a model. - """ - - input_ids: List[int] - attention_mask: List[int] - token_type_ids: Optional[List[int]] = None - label_ids: Optional[List[int]] = None - - -class Split(Enum): - train = "train" - dev = "dev" - test = "test" - - -class TokenClassificationTask: - @staticmethod - def read_examples_from_file(data_dir, mode: Union[Split, str]) -> List[InputExample]: - raise NotImplementedError - - @staticmethod - def get_labels(path: str) -> List[str]: - raise NotImplementedError - - @staticmethod - def convert_examples_to_features( - examples: List[InputExample], - label_list: List[str], - max_seq_length: int, - tokenizer: PreTrainedTokenizer, - cls_token_at_end=False, - cls_token="[CLS]", - cls_token_segment_id=1, - sep_token="[SEP]", - sep_token_extra=False, - pad_on_left=False, - pad_token=0, - pad_token_segment_id=0, - pad_token_label_id=-100, - sequence_a_segment_id=0, - mask_padding_with_zero=True, - ) -> List[InputFeatures]: - """Loads a data file into a list of `InputFeatures` - `cls_token_at_end` define the location of the CLS token: - - False (Default, BERT/XLM pattern): [CLS] + A + [SEP] + B + [SEP] - - True (XLNet/GPT pattern): A + [SEP] + B + [SEP] + [CLS] - `cls_token_segment_id` define the segment id associated to the CLS token (0 for BERT, 2 for XLNet) - """ - # TODO clean up all this to leverage built-in features of tokenizers - - label_map = {label: i for i, label in enumerate(label_list)} - - features = [] - for (ex_index, example) in enumerate(examples): - if ex_index % 10_000 == 0: - logger.info("Writing example %d of %d", ex_index, len(examples)) - - tokens = [] - label_ids = [] - for word, label in zip(example.words, example.labels): - word_tokens = tokenizer.tokenize(word) - - # bert-base-multilingual-cased sometimes output "nothing ([]) when calling tokenize with just a space. - if len(word_tokens) > 0: - tokens.extend(word_tokens) - # Use the real label id for the first token of the word, and padding ids for the remaining tokens - label_ids.extend([label_map[label]] + [pad_token_label_id] * (len(word_tokens) - 1)) - - # Account for [CLS] and [SEP] with "- 2" and with "- 3" for RoBERTa. - special_tokens_count = tokenizer.num_special_tokens_to_add() - if len(tokens) > max_seq_length - special_tokens_count: - tokens = tokens[: (max_seq_length - special_tokens_count)] - label_ids = label_ids[: (max_seq_length - special_tokens_count)] - - # The convention in BERT is: - # (a) For sequence pairs: - # tokens: [CLS] is this jack ##son ##ville ? [SEP] no it is not . [SEP] - # type_ids: 0 0 0 0 0 0 0 0 1 1 1 1 1 1 - # (b) For single sequences: - # tokens: [CLS] the dog is hairy . [SEP] - # type_ids: 0 0 0 0 0 0 0 - # - # Where "type_ids" are used to indicate whether this is the first - # sequence or the second sequence. The embedding vectors for `type=0` and - # `type=1` were learned during pre-training and are added to the wordpiece - # embedding vector (and position vector). This is not *strictly* necessary - # since the [SEP] token unambiguously separates the sequences, but it makes - # it easier for the model to learn the concept of sequences. - # - # For classification tasks, the first vector (corresponding to [CLS]) is - # used as as the "sentence vector". Note that this only makes sense because - # the entire model is fine-tuned. - tokens += [sep_token] - label_ids += [pad_token_label_id] - if sep_token_extra: - # roberta uses an extra separator b/w pairs of sentences - tokens += [sep_token] - label_ids += [pad_token_label_id] - segment_ids = [sequence_a_segment_id] * len(tokens) - - if cls_token_at_end: - tokens += [cls_token] - label_ids += [pad_token_label_id] - segment_ids += [cls_token_segment_id] - else: - tokens = [cls_token] + tokens - label_ids = [pad_token_label_id] + label_ids - segment_ids = [cls_token_segment_id] + segment_ids - - input_ids = tokenizer.convert_tokens_to_ids(tokens) - - # The mask has 1 for real tokens and 0 for padding tokens. Only real - # tokens are attended to. - input_mask = [1 if mask_padding_with_zero else 0] * len(input_ids) - - # Zero-pad up to the sequence length. - padding_length = max_seq_length - len(input_ids) - if pad_on_left: - input_ids = ([pad_token] * padding_length) + input_ids - input_mask = ([0 if mask_padding_with_zero else 1] * padding_length) + input_mask - segment_ids = ([pad_token_segment_id] * padding_length) + segment_ids - label_ids = ([pad_token_label_id] * padding_length) + label_ids - else: - input_ids += [pad_token] * padding_length - input_mask += [0 if mask_padding_with_zero else 1] * padding_length - segment_ids += [pad_token_segment_id] * padding_length - label_ids += [pad_token_label_id] * padding_length - - assert len(input_ids) == max_seq_length - assert len(input_mask) == max_seq_length - assert len(segment_ids) == max_seq_length - assert len(label_ids) == max_seq_length - - if ex_index < 5: - logger.info("*** Example ***") - logger.info("guid: %s", example.guid) - logger.info("tokens: %s", " ".join([str(x) for x in tokens])) - logger.info("input_ids: %s", " ".join([str(x) for x in input_ids])) - logger.info("input_mask: %s", " ".join([str(x) for x in input_mask])) - logger.info("segment_ids: %s", " ".join([str(x) for x in segment_ids])) - logger.info("label_ids: %s", " ".join([str(x) for x in label_ids])) - - if "token_type_ids" not in tokenizer.model_input_names: - segment_ids = None - - features.append( - InputFeatures( - input_ids=input_ids, attention_mask=input_mask, token_type_ids=segment_ids, label_ids=label_ids - ) - ) - return features - - -if is_torch_available(): - import torch - from torch import nn - from torch.utils.data.dataset import Dataset - - class TokenClassificationDataset(Dataset): - """ - This will be superseded by a framework-agnostic approach - soon. - """ - - features: List[InputFeatures] - pad_token_label_id: int = nn.CrossEntropyLoss().ignore_index - # Use cross entropy ignore_index as padding label id so that only - # real label ids contribute to the loss later. - - def __init__( - self, - token_classification_task: TokenClassificationTask, - data_dir: str, - tokenizer: PreTrainedTokenizer, - labels: List[str], - model_type: str, - max_seq_length: Optional[int] = None, - overwrite_cache=False, - mode: Split = Split.train, - ): - # Load data features from cache or dataset file - cached_features_file = os.path.join( - data_dir, - "cached_{}_{}_{}".format(mode.value, tokenizer.__class__.__name__, str(max_seq_length)), - ) - - # Make sure only the first process in distributed training processes the dataset, - # and the others will use the cache. - lock_path = cached_features_file + ".lock" - with FileLock(lock_path): - - if os.path.exists(cached_features_file) and not overwrite_cache: - logger.info(f"Loading features from cached file {cached_features_file}") - self.features = torch.load(cached_features_file) - else: - logger.info(f"Creating features from dataset file at {data_dir}") - examples = token_classification_task.read_examples_from_file(data_dir, mode) - # TODO clean up all this to leverage built-in features of tokenizers - self.features = token_classification_task.convert_examples_to_features( - examples, - labels, - max_seq_length, - tokenizer, - cls_token_at_end=bool(model_type in ["xlnet"]), - # xlnet has a cls token at the end - cls_token=tokenizer.cls_token, - cls_token_segment_id=2 if model_type in ["xlnet"] else 0, - sep_token=tokenizer.sep_token, - sep_token_extra=False, - # roberta uses an extra separator b/w pairs of sentences, cf. github.com/pytorch/fairseq/commit/1684e166e3da03f5b600dbb7855cb98ddfcd0805 - pad_on_left=bool(tokenizer.padding_side == "left"), - pad_token=tokenizer.pad_token_id, - pad_token_segment_id=tokenizer.pad_token_type_id, - pad_token_label_id=self.pad_token_label_id, - ) - logger.info(f"Saving features into cached file {cached_features_file}") - torch.save(self.features, cached_features_file) - - def __len__(self): - return len(self.features) - - def __getitem__(self, i) -> InputFeatures: - return self.features[i] - - -if is_tf_available(): - import tensorflow as tf - - class TFTokenClassificationDataset: - """ - This will be superseded by a framework-agnostic approach - soon. - """ - - features: List[InputFeatures] - pad_token_label_id: int = -100 - # Use cross entropy ignore_index as padding label id so that only - # real label ids contribute to the loss later. - - def __init__( - self, - token_classification_task: TokenClassificationTask, - data_dir: str, - tokenizer: PreTrainedTokenizer, - labels: List[str], - model_type: str, - max_seq_length: Optional[int] = None, - overwrite_cache=False, - mode: Split = Split.train, - ): - examples = token_classification_task.read_examples_from_file(data_dir, mode) - # TODO clean up all this to leverage built-in features of tokenizers - self.features = token_classification_task.convert_examples_to_features( - examples, - labels, - max_seq_length, - tokenizer, - cls_token_at_end=bool(model_type in ["xlnet"]), - # xlnet has a cls token at the end - cls_token=tokenizer.cls_token, - cls_token_segment_id=2 if model_type in ["xlnet"] else 0, - sep_token=tokenizer.sep_token, - sep_token_extra=False, - # roberta uses an extra separator b/w pairs of sentences, cf. github.com/pytorch/fairseq/commit/1684e166e3da03f5b600dbb7855cb98ddfcd0805 - pad_on_left=bool(tokenizer.padding_side == "left"), - pad_token=tokenizer.pad_token_id, - pad_token_segment_id=tokenizer.pad_token_type_id, - pad_token_label_id=self.pad_token_label_id, - ) - - def gen(): - for ex in self.features: - if ex.token_type_ids is None: - yield ( - {"input_ids": ex.input_ids, "attention_mask": ex.attention_mask}, - ex.label_ids, - ) - else: - yield ( - { - "input_ids": ex.input_ids, - "attention_mask": ex.attention_mask, - "token_type_ids": ex.token_type_ids, - }, - ex.label_ids, - ) - - if "token_type_ids" not in tokenizer.model_input_names: - self.dataset = tf.data.Dataset.from_generator( - gen, - ({"input_ids": tf.int32, "attention_mask": tf.int32}, tf.int64), - ( - {"input_ids": tf.TensorShape([None]), "attention_mask": tf.TensorShape([None])}, - tf.TensorShape([None]), - ), - ) - else: - self.dataset = tf.data.Dataset.from_generator( - gen, - ({"input_ids": tf.int32, "attention_mask": tf.int32, "token_type_ids": tf.int32}, tf.int64), - ( - { - "input_ids": tf.TensorShape([None]), - "attention_mask": tf.TensorShape([None]), - "token_type_ids": tf.TensorShape([None]), - }, - tf.TensorShape([None]), - ), - ) - - def get_dataset(self): - self.dataset = self.dataset.apply(tf.data.experimental.assert_cardinality(len(self.features))) - - return self.dataset - - def __len__(self): - return len(self.features) - - def __getitem__(self, i) -> InputFeatures: - return self.features[i] From aed2595b8776c15530759c81fe7e197e03600aab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20G=C5=82omski?= Date: Wed, 19 May 2021 15:15:53 +0200 Subject: [PATCH 11/11] Add configuration for MXNet 1.9.0 Proposition of yaml structure for MXNet --- lpot/adaptor/mxnet.yaml | 65 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/lpot/adaptor/mxnet.yaml b/lpot/adaptor/mxnet.yaml index ae67015513b..e5e5fac641e 100644 --- a/lpot/adaptor/mxnet.yaml +++ b/lpot/adaptor/mxnet.yaml @@ -125,6 +125,71 @@ << : *common_patterns +version: + name: '1.9.0' + + precisions: + names: int8, fp32 + valid_mixed_precisions: [] + + ops: + int8: ['_sg_mkldnn_conv', 'Convolution', '_sg_mkldnn_fully_connected', 'FullyConnected', '_sg_mkldnn_selfatt_qk', '_sg_mkldnn_selfatt_valatt'] + fp32: ['*'] # '*' means all op types + + capabilities: + 'int8': { + '_sg_mkldnn_conv': { + 'activation': { + 'dtype': ['int8', 'fp32'], + 'algorithm': ['minmax', 'kl']}, + 'weight': { + 'dtype': ['int8', 'fp32']} + }, + 'Convolution': { + 'activation': { + 'dtype': ['int8', 'fp32'], + 'algorithm': ['minmax', 'kl']}, + 'weight': { + 'dtype': ['int8', 'fp32']} + }, + '_sg_mkldnn_fully_connected': { + 'activation': { + 'dtype': ['int8', 'fp32'], + 'algorithm': ['minmax', 'kl']}, + 'weight': { + 'dtype': ['int8', 'fp32']} + }, + 'FullyConnected': { + 'activation': { + 'dtype': ['int8', 'fp32'], + 'algorithm': ['minmax', 'kl']}, + 'weight': { + 'dtype': ['int8', 'fp32']} + }, + '_sg_mkldnn_selfatt_qk': { + 'activation': { + 'dtype': ['int8', 'fp32'], + 'algorithm': ['minmax', 'kl']} + }, + '_sg_mkldnn_selfatt_valatt': { + 'activation': { + 'dtype': ['int8', 'fp32'], + 'algorithm': ['minmax', 'kl']} + }, + 'default': { + 'activation': { + 'dtype': ['int8', 'fp32'], + 'algorithm': ['minmax']}, + 'weight': { + 'dtype': ['int8', 'fp32'], + } + } + } + + patterns: + fp32: [] + int8: [] + - version: name: 'default'