From ba5d18c4ba97066d5db5984c8d2a6fdb67255b1b Mon Sep 17 00:00:00 2001 From: SparkSnail Date: Thu, 27 Feb 2020 17:52:07 +0800 Subject: [PATCH 01/12] Handle exception and kill restserver in nnictl (#2086) --- tools/nni_cmd/launcher.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tools/nni_cmd/launcher.py b/tools/nni_cmd/launcher.py index 8571cf6498..2af1c609bb 100644 --- a/tools/nni_cmd/launcher.py +++ b/tools/nni_cmd/launcher.py @@ -525,7 +525,15 @@ def create_experiment(args): nni_config.set_config('experimentConfig', experiment_config) nni_config.set_config('restServerPort', args.port) - launch_experiment(args, experiment_config, 'new', config_file_name) + try: + launch_experiment(args, experiment_config, 'new', config_file_name) + except Exception as exception: + nni_config = Config(config_file_name) + restServerPid = nni_config.get_config('restServerPid') + if restServerPid: + kill_command(restServerPid) + print_error(exception) + exit(1) def manage_stopped_experiment(args, mode): '''view a stopped experiment''' @@ -553,8 +561,16 @@ def manage_stopped_experiment(args, mode): new_config_file_name = ''.join(random.sample(string.ascii_letters + string.digits, 8)) new_nni_config = Config(new_config_file_name) new_nni_config.set_config('experimentConfig', experiment_config) - launch_experiment(args, experiment_config, mode, new_config_file_name, experiment_id) new_nni_config.set_config('restServerPort', args.port) + try: + launch_experiment(args, experiment_config, mode, new_config_file_name, experiment_id) + except Exception as exception: + nni_config = Config(new_config_file_name) + restServerPid = nni_config.get_config('restServerPid') + if restServerPid: + kill_command(restServerPid) + print_error(exception) + exit(1) def view_experiment(args): '''view a stopped experiment''' From fadac07dab1325be40c0c74afc584df77bd73e8a Mon Sep 17 00:00:00 2001 From: SparkSnail Date: Fri, 28 Feb 2020 13:17:48 +0800 Subject: [PATCH 02/12] Add pai pipeline (#2092) --- test/config_test.py | 2 +- test/generate_ts_config.py | 23 +++++++++++++- test/pipelines-it-pai.yml | 6 ++-- test/pipelines-it-paiYarn.yml | 59 +++++++++++++++++++++++++++++++++++ test/training_service.yml | 17 +++++++++- 5 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 test/pipelines-it-paiYarn.yml diff --git a/test/config_test.py b/test/config_test.py index 91136a8a95..bce0778d1a 100644 --- a/test/config_test.py +++ b/test/config_test.py @@ -112,7 +112,7 @@ def run(args): parser = argparse.ArgumentParser() parser.add_argument("--config", type=str, default=None) parser.add_argument("--exclude", type=str, default=None) - parser.add_argument("--ts", type=str, choices=['local', 'remote', 'pai', 'kubeflow', 'frameworkcontroller'], default='local') + parser.add_argument("--ts", type=str, choices=['local', 'remote', 'pai', 'paiYarn', 'kubeflow', 'frameworkcontroller'], default='local') parser.add_argument("--local_gpu", action='store_true') parser.add_argument("--preinstall", action='store_true') args = parser.parse_args() diff --git a/test/generate_ts_config.py b/test/generate_ts_config.py index fb5784d3b1..efa24ee4e2 100644 --- a/test/generate_ts_config.py +++ b/test/generate_ts_config.py @@ -12,7 +12,7 @@ def update_training_service_config(args): config = get_yml_content(TRAINING_SERVICE_FILE) if args.nni_manager_ip is not None: config[args.ts]['nniManagerIp'] = args.nni_manager_ip - if args.ts == 'pai': + if args.ts == 'paiYarn': if args.pai_user is not None: config[args.ts]['paiYarnConfig']['userName'] = args.pai_user if args.pai_pwd is not None: @@ -27,6 +27,23 @@ def update_training_service_config(args): config[args.ts]['trial']['outputDir'] = args.output_dir if args.vc is not None: config[args.ts]['trial']['virtualCluster'] = args.vc + if args.ts == 'pai': + if args.pai_user is not None: + config[args.ts]['paiConfig']['userName'] = args.pai_user + if args.pai_host is not None: + config[args.ts]['paiConfig']['host'] = args.pai_host + if args.pai_token is not None: + config[args.ts]['paiConfig']['token'] = args.pai_token + if args.nni_docker_image is not None: + config[args.ts]['trial']['image'] = args.nni_docker_image + if args.nniManagerNFSMountPath is not None: + config[args.ts]['trial']['nniManagerNFSMountPath'] = args.nni_manager_nfs_mount_path + if args.containerNFSMountPath is not None: + config[args.ts]['trial']['containerNFSMountPath'] = args.container_nfs_mount_path + if args.paiStoragePlugin is not None: + config[args.ts]['trial']['paiStoragePlugin'] = args.pai_storage_plugin + if args.vc is not None: + config[args.ts]['trial']['virtualCluster'] = args.vc elif args.ts == 'kubeflow': if args.nfs_server is not None: config[args.ts]['kubeflowConfig']['nfs']['server'] = args.nfs_server @@ -94,6 +111,10 @@ def convert_command(): parser.add_argument("--data_dir", type=str) parser.add_argument("--output_dir", type=str) parser.add_argument("--vc", type=str) + parser.add_argument("--pai_token", type=str) + parser.add_argument("--pai_storage_plugin", type=str) + parser.add_argument("--nni_manager_nfs_mount_path", type=str) + parser.add_argument("--container_nfs_mount_path", type=str) # args for kubeflow and frameworkController parser.add_argument("--nfs_server", type=str) parser.add_argument("--nfs_path", type=str) diff --git a/test/pipelines-it-pai.yml b/test/pipelines-it-pai.yml index 15f24b591d..d0d04afc07 100644 --- a/test/pipelines-it-pai.yml +++ b/test/pipelines-it-pai.yml @@ -51,9 +51,9 @@ jobs: echo "TEST_IMG:$TEST_IMG" cd test - python3 generate_ts_config.py --ts pai --pai_host $(pai_host) --pai_user $(pai_user) --pai_pwd $(pai_pwd) --vc $(pai_virtual_cluster) \ - --nni_docker_image $TEST_IMG --data_dir $(data_dir) --output_dir $(output_dir) --nni_manager_ip $(nni_manager_ip) + python3 generate_ts_config.py --ts pai --pai_host $(pai_host) --pai_user $(pai_user) --nni_docker_image $TEST_IMG --pai_storage_plugin $(pai_storage_plugin)\ + --pai_token $(pai_token) --nni_manager_nfs_mount_path $(nni_manager_nfs_mount_path) --container_nfs_mount_path $(container_nfs_mount_path) --nni_manager_ip $(nni_manager_ip) - PATH=$HOME/.local/bin:$PATH python3 config_test.py --ts pai + PATH=$HOME/.local/bin:$PATH python3 config_test.py --ts pai --exclude multi_phase PATH=$HOME/.local/bin:$PATH python3 metrics_test.py displayName: 'integration test' diff --git a/test/pipelines-it-paiYarn.yml b/test/pipelines-it-paiYarn.yml new file mode 100644 index 0000000000..ad5ec3b305 --- /dev/null +++ b/test/pipelines-it-paiYarn.yml @@ -0,0 +1,59 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +jobs: +- job: 'integration_test_paiYarn' + timeoutInMinutes: 0 + + steps: + - script: python3 -m pip install --upgrade pip setuptools --user + displayName: 'Install python tools' + + - script: | + cd deployment/pypi + echo 'building prerelease package...' + make build + ls $(Build.SourcesDirectory)/deployment/pypi/dist/ + condition: eq( variables['build_docker_img'], 'true' ) + displayName: 'build nni bdsit_wheel' + + - script: | + source install.sh + displayName: 'Install nni toolkit via source code' + + - script: | + sudo apt-get install swig -y + PATH=$HOME/.local/bin:$PATH nnictl package install --name=SMAC + PATH=$HOME/.local/bin:$PATH nnictl package install --name=BOHB + displayName: 'Install dependencies for integration tests in PAI mode' + + - script: | + set -e + if [ $(build_docker_img) = 'true' ] + then + cd deployment/pypi + docker login -u $(docker_hub_user) -p $(docker_hub_pwd) + echo 'updating docker file for installing nni from local...' + # update Dockerfile to install NNI in docker image from whl file built in last step + sed -ie 's/RUN python3 -m pip --no-cache-dir install nni/COPY .\/dist\/* .\nRUN python3 -m pip install nni-*.whl/' ../docker/Dockerfile + cat ../docker/Dockerfile + export IMG_TAG=`date -u +%y%m%d%H%M` + + echo 'build and upload docker image' + docker build -f ../docker/Dockerfile -t $(test_docker_img_name):$IMG_TAG . + docker push $(test_docker_img_name):$IMG_TAG + + export TEST_IMG=$(test_docker_img_name):$IMG_TAG + cd ../../ + else + export TEST_IMG=$(existing_docker_img) + fi + + echo "TEST_IMG:$TEST_IMG" + cd test + python3 generate_ts_config.py --ts paiYarn --pai_host $(pai_host) --pai_user $(pai_user) --pai_pwd $(pai_pwd) --vc $(pai_virtual_cluster) \ + --nni_docker_image $TEST_IMG --data_dir $(data_dir) --output_dir $(output_dir) --nni_manager_ip $(nni_manager_ip) + + PATH=$HOME/.local/bin:$PATH python3 config_test.py --ts paiYarn + PATH=$HOME/.local/bin:$PATH python3 metrics_test.py + displayName: 'integration test' diff --git a/test/training_service.yml b/test/training_service.yml index 2a00acca54..040342da67 100644 --- a/test/training_service.yml +++ b/test/training_service.yml @@ -52,7 +52,7 @@ frameworkcontroller: local: trainingServicePlatform: local -pai: +paiYarn: nniManagerIp: maxExecDuration: 15m paiYarnConfig: @@ -68,6 +68,21 @@ pai: memoryMB: 8192 outputDir: virtualCluster: +pai: + nniManagerIp: + maxExecDuration: 15m + paiConfig: + host: + userName: + trainingServicePlatform: pai + trial: + gpuNum: 1 + cpuNum: 1 + image: + memoryMB: 8192 + nniManagerNFSMountPath: + containerNFSMountPath: + paiStoragePlugin: remote: machineList: - ip: From 6164207e46a48e2aa49df576e16dc7deab28864d Mon Sep 17 00:00:00 2001 From: Guilherme Nagatomo Date: Fri, 28 Feb 2020 02:33:41 -0300 Subject: [PATCH 03/12] fix invalid typeof check in ObservableTimer#unsubscribe (closes #2100) (#2106) --- src/nni_manager/common/observableTimer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nni_manager/common/observableTimer.ts b/src/nni_manager/common/observableTimer.ts index 4563da4843..a56b185e20 100644 --- a/src/nni_manager/common/observableTimer.ts +++ b/src/nni_manager/common/observableTimer.ts @@ -19,7 +19,7 @@ class ObservableTimer { } public unsubscribe( subscription: Rx.IDisposable): void { - if(typeof subscription !== undefined) { + if(typeof subscription !== 'undefined') { subscription.dispose(); } } From 03cea2b4e1ac2d2c9f7269b1d79be3b5dac42d13 Mon Sep 17 00:00:00 2001 From: marsggbo Date: Sat, 29 Feb 2020 11:58:49 +0800 Subject: [PATCH 04/12] fix issue #2091 (#2107) * fix EnasMutator LSTMCell forward bug fix issue #2091 * add comment Co-authored-by: QuanluZhang --- src/sdk/pynni/nni/nas/pytorch/enas/mutator.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sdk/pynni/nni/nas/pytorch/enas/mutator.py b/src/sdk/pynni/nni/nas/pytorch/enas/mutator.py index 889c0f35a7..8cd107ec9d 100644 --- a/src/sdk/pynni/nni/nas/pytorch/enas/mutator.py +++ b/src/sdk/pynni/nni/nas/pytorch/enas/mutator.py @@ -23,7 +23,9 @@ def forward(self, inputs, hidden): curr_c, curr_h = m(inputs, (prev_c[i], prev_h[i])) next_c.append(curr_c) next_h.append(curr_h) - inputs = curr_h[-1] + # current implementation only supports batch size equals 1, + # but the algorithm does not necessarily have this limitation + inputs = curr_h[-1].view(1, -1) return next_c, next_h From 134368fa745380fca5559c4c62d6aeda573e77c3 Mon Sep 17 00:00:00 2001 From: George Cheng Date: Mon, 2 Mar 2020 10:38:35 +0800 Subject: [PATCH 05/12] DLTS integration (#1945) * skeleton of dlts training service (#1844) * Hello, DLTS! * Revert version * Remove fs-extra * Add some default cluster config * schema * fix * Optional cluster (default to `.default`) Depends on DLWorkspace#837 * fix * fix * optimize gpu type * No more copy * Format * Code clean up * Issue fix * Add optional fields in config * Issue fix * Lint * Lint * Validate email, password and team * Doc * Doc fix * Set TMPDIR * Use metadata instead of gpu_capacity * Cancel paused DLTS job * workaround lint rules * pylint * doc Co-authored-by: QuanluZhang --- docs/en_US/TrainingService/DLTSMode.md | 49 ++ docs/en_US/training_services.rst | 1 + docs/img/dlts-step1.png | Bin 0 -> 85661 bytes docs/img/dlts-step3.png | Bin 0 -> 57351 bytes docs/img/dlts-step4.png | Bin 0 -> 75625 bytes docs/img/dlts-step5.png | Bin 0 -> 79332 bytes examples/trials/mnist-tfv1/config_dlts.yml | 34 ++ src/nni_manager/main.ts | 7 +- .../rest_server/restValidationSchemas.ts | 9 + .../common/trialConfigMetadataKey.ts | 1 + .../dlts/dltsClusterConfig.ts | 14 + .../training_service/dlts/dltsData.ts | 8 + .../training_service/dlts/dltsJobConfig.ts | 45 ++ .../dlts/dltsJobRestServer.ts | 77 +++ .../dlts/dltsTrainingService.ts | 503 ++++++++++++++++++ .../training_service/dlts/dltsTrialConfig.ts | 15 + .../dlts/dltsTrialJobDetail.ts | 31 ++ src/sdk/pynni/nni/platform/__init__.py | 2 +- tools/nni_cmd/config_schema.py | 26 +- tools/nni_cmd/launcher.py | 21 + tools/nni_cmd/launcher_utils.py | 12 +- 21 files changed, 848 insertions(+), 7 deletions(-) create mode 100644 docs/en_US/TrainingService/DLTSMode.md create mode 100644 docs/img/dlts-step1.png create mode 100644 docs/img/dlts-step3.png create mode 100644 docs/img/dlts-step4.png create mode 100644 docs/img/dlts-step5.png create mode 100644 examples/trials/mnist-tfv1/config_dlts.yml create mode 100644 src/nni_manager/training_service/dlts/dltsClusterConfig.ts create mode 100644 src/nni_manager/training_service/dlts/dltsData.ts create mode 100644 src/nni_manager/training_service/dlts/dltsJobConfig.ts create mode 100644 src/nni_manager/training_service/dlts/dltsJobRestServer.ts create mode 100644 src/nni_manager/training_service/dlts/dltsTrainingService.ts create mode 100644 src/nni_manager/training_service/dlts/dltsTrialConfig.ts create mode 100644 src/nni_manager/training_service/dlts/dltsTrialJobDetail.ts diff --git a/docs/en_US/TrainingService/DLTSMode.md b/docs/en_US/TrainingService/DLTSMode.md new file mode 100644 index 0000000000..43ba44ac1b --- /dev/null +++ b/docs/en_US/TrainingService/DLTSMode.md @@ -0,0 +1,49 @@ +**Run an Experiment on DLTS** +=== +NNI supports running an experiment on [DLTS](https://github.com/microsoft/DLWorkspace.git), called dlts mode. Before starting to use NNI dlts mode, you should have an account to access DLTS dashboard. + +## Setup Environment + +Step 1. Choose a cluster from DLTS dashboard, ask administrator for the cluster dashboard URL. + +![Choose Cluster](../../img/dlts-step1.png) + +Step 2. Prepare a NNI config YAML like the following: + +```yaml +# Set this field to "dlts" +trainingServicePlatform: dlts +authorName: your_name +experimentName: auto_mnist +trialConcurrency: 2 +maxExecDuration: 3h +maxTrialNum: 100 +searchSpacePath: search_space.json +useAnnotation: false +tuner: + builtinTunerName: TPE + classArgs: + optimize_mode: maximize +trial: + command: python3 mnist.py + codeDir: . + gpuNum: 1 + image: msranni/nni +# Configuration to access DLTS +dltsConfig: + dashboard: # Ask administrator for the cluster dashboard URL +``` + +Remember to fill the cluster dashboard URL to the last line. + +Step 3. Open your working directory of the cluster, paste the NNI config as well as related code to a directory. + +![Copy Config](../../img/dlts-step3.png) + +Step 4. Submit a NNI manager job to the specified cluster. + +![Submit Job](../../img/dlts-step4.png) + +Step 5. Go to Endpoints tab of the newly created job, click the Port 40000 link to check trial's information. + +![View NNI WebUI](../../img/dlts-step5.png) diff --git a/docs/en_US/training_services.rst b/docs/en_US/training_services.rst index b5b520f4ed..884d90441b 100644 --- a/docs/en_US/training_services.rst +++ b/docs/en_US/training_services.rst @@ -9,3 +9,4 @@ Introduction to NNI Training Services OpenPAI Yarn Mode<./TrainingService/PaiYarnMode> Kubeflow<./TrainingService/KubeflowMode> FrameworkController<./TrainingService/FrameworkControllerMode> + OpenPAI<./TrainingService/DLTSMode> diff --git a/docs/img/dlts-step1.png b/docs/img/dlts-step1.png new file mode 100644 index 0000000000000000000000000000000000000000..47949767e5f85ffce83799ba17a38fd7aca18e70 GIT binary patch literal 85661 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≻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 Ktvr4C&VK-+>AVF1 literal 0 HcmV?d00001 diff --git a/docs/img/dlts-step4.png b/docs/img/dlts-step4.png new file mode 100644 index 0000000000000000000000000000000000000000..00528297af6e6d51b331994ef834115ddacbd16b GIT binary patch literal 75625 zcmeFZc~Dc?+AmBW&XMju0(RSqGDX_~l>j0#57D+;R1lm2nPd=DhR93;NwhhFsDOe5 z5D3}`CNo%2U$&R!{Ki?09UM-^84A{za{3Kz|oE@=dprOHH2_jn^b`Lr;aT^AA9<{G=dbr9_ ziJ+>jDxONF`rG=wZz^3vhvvgu;YSkUB7F_e9_-0cTXD&BYn<_hNA!RN6fo3x@(SE& zKGvySuu6c)7A@BAQ`_fiSLGHuHos_B98jSu9(Y|P;Kq(ALRCth$}r`G(JD&L1i9$B zSYzB~hUb%|Le*N9>dT$C-j!K!V&jVkYKOhz?S|b#Y4g-!FL8-t-W_!>w^xfiJfCEj z;cg_DFP5`WN~13~J$Uff9g*A6Y=dli)xsOR9oU@;KZ#iB()lv#V02~7;zF%Maa2VE zuz84zt?E2=L1T6_00tk2!@o^}Yp8_&s&)3OsqHcXR^G4}TMlgd%W%1X611mMT>wnr z%Vw2kIUVp7F;oi1pHmNQkGCVcg-&V``S?2nNoNi6E7B@F(pY3Nxy)XsQW8|!Uq&8x zvm?5NP5{reW4eV-8|$JyvP`jtUKrDRn3@%SAw#}bJn+r>&?^_7uRp(dph?579uM;b ze$K`mUG2`$Yf0GnWvp#7DaUyhL-nA{jQ(2;t zlni1Q-Z=P@%3z+RC$o32{|)VUoU3A_VX47NU%s@Rj==#1G5i5 zDQgHPM*r7+o}vGqLszzuyywsBK_vk#Unb7peSOgaocHJd8HiNYpL0L2qx}0hheg}b zE`MI{cKU1b@&9j5F1K2k4&^; zg*%(df|-z7UYbNPQa=mV1SWOj)o2z+x6z3Zf7q}PZzQ2lZxSOE*L>P9wmh9v##&}N zNsmnB$O|Tybr^R`d0D;c!%srKGI1!PBmh=MK_ywbQTjXS&Y%YxF6wR4Gr>gv-Tq0j zP<@2r92hBuv9^}x1;D~^ZjiCHCsUFm`U-aMoqw$MaI`HQF&FvEJ!{XSOE{%f<_&c# zw&81IE!*zYt&Zw+3@tY=O)V!DS(y13U*0g3(EaRA@73S_Wb#l1V_cgIRS0^<+sdMB z=>3k`pnJnA2l((IB7f|%Ury&Std8|@D=_r@z{Ax6d+py=7;j5Dd(sL))NeI!LUDge z-?`fvE4A`VKGLO>x@Q4vgISs@9}~4BSwALTWCWh7aH9$V_E%;nrGZ zF09}ZqO~*gJwKsj`omiDQf#oK^6H<0{e8GAptezbXracHII*kKHf`m_)hQjcIJLH|+tJz;XJH23nZNiaINx_c{K?ns z@5AM(EEjASI`y_QJa#RtZmpu1H7&*BONR(Qrft0k%F{z@8xf;lKK+Y`)3x%2fBN@q zv_F^CHIK|oS{rVQx^}27qZ3X2-aoUeHrWPH+JF2-rM^@ad1cc7YMMd%OBdtIzp%#U zNxHZgHaaolWpuz%U}^uu114(a(>5aq7n>~%;fOm-k~e#1r91+`>@SKi(ZA6=P@G_R z%J4Uy(4v`}I8A}1C-|47lR(IWhVNgsVK9BGbuUY!qMwyepGo4vb#)P~+wu;G;~1&k zGWcAfh3^tauonC&xG!1otb4N$+AUJzxmawkb6+9fdjv-%u=w;>U20HIB7c^5e^D&2 z4y$m%#&MH=h82S#G_GH^cwftO*!iqIydHH{PQ|70wfh>(x6b13unKIq5km3PLXM5( zCc^$@Cd#Uiftv-v5&Xy^xssmv>eI^fW^MWCv6Z!d8ZoDx{N8ua(=3yHV57>#VE0pU zw)SIsPAQh4S0eXd-81}(I)W4Uu;L`?7m?*xJrki@6J0)0l#nTS2b_glht{_{Z zGZAs_4y%f)o_68G09avwB)TYR;jQ!xmc_?M=62*Yt*#pGv6|9h%n1>?v7Ua22<7W- zMl=WP@phE9=3%gZ7ishZp(wLm+p5H6Cn;4Uh&Rt?z;Mi3`52_+LA&=M$iBJV2opd5 zMMYzzHCVMeoeyiyllgo~GX7#d#$K80q1j*}e{Oz@mYGwQJDu=Tp6S)8_Y>zH5&1lf zONZzPliE5_3n+4&{qAOAnpWC?Z$<%OKEN~2vH-s|cXO2~LFws!O!P`4oB9XmEjg8C zOXMdoW639PG~}_T$R(GSm6re%+|E(%difU6?-IXzp-(7#KKy3JIF_PC_TWTalsU9e z{E*u&;dUCzWeH55CGE4{M|Q0fG^$2LICf4?8M3!V6#YB9C_oaH=hkg4g>!SnB5(Hh z5W<}=2;B<>lmce7HnoY7h}hz+Go9M#t9#9p{_6GP9zZ-2LT3tQ%XD>X#2GddE8hyn z0wk%L87U(R(Ha(J&t2ROo075{0~UP19mysoAy)1 z_T(>WJN)RJa1~oJ9`~AMhcNj^!@Emip32-F4Lie6Q-==s+dqpqAVE%McaRv6?d*Z# z2c>3x0K5wrRZB$1H)$-CFk|ibaL_A+HWC+So7sx(e0Ik)EBs-AGejlrF7D)bS+Mf)V&iRDS`ZCk>}K7Fp#XIxwN=^8@@&x zHrUWmP2|@D*nQrJd1GmZM6mXG%ECAE*7PQCrShEeGF264Tr+E6+sbMjaK2viQOILk z`u79-_QUO5l1ZQ0z?N=+8LeAzr<~H=o%%bzDItv#pi1t)l4hRiWQ$);KcV2ZG8@sm zsGz$AVY%cOy_=qxC`zfbfT~nt-Didv(CY^1cnLTAb}DN-5$RLLcswx_Tli~h=UG0f zy>XRtCT&l_7zYsQ36i&eTMvo(xzP$sukc`g#$84+%LNSB$%r(Tfs!63dji>%Mz=Qo z;s#wG(Io`HmX@*MME~SnA(x_OaBf>1!6nIiP2(KDmjZ^%O%PVU6VoyhnV(|~W^7{Q z`!2#?2e$@ys+-`dr#jMh$@4;Sho>W5@J$z54w@19a^C&emSSZV%n74&H4tC?wCnwP!~oc3`c0eC*Z5A? zBK+%ZC9C!5h>GtG&N{M%rbg-OO4)?WJj z=bH>Pd;ra4==i6H(&w${RU>?<q>He2ni?7)G>X;cXc{rdJ`(>SK(gRa zQ&ie(6#6s!Ct?dr@!^J}7TFt_I2QNa%W!g3y~KKlReYjmj3F)crAvD|?xYFBxu=9U zK#17+6_>m(_nj+UXX zwphJZwY^}~IgZP;ceyk=(KSL1yubM8L;EbdiTqO~KKBZ+INN#e%%rBp4C+=sRW(rk zJ^)5IMn{)6S$94Y8|PV$*nxS?7k-H`!JcHZ_CDcoV7h1>aq{KV`(Ithu?RYGguNmHE>syGJe;U8}I zHWn8*^u@;?0(?flc>$wC&!|Lke2O~zIicUmq1oC^8Kb?KE<6n8Nv;hCXp=p7mgZoy zWyvEZUYQ&TT6t3vnx+eb+e9&1bW-{@naP1H--fH-gX#%)73a$_g8N4;X2a?)gyA!u4WRaw@CXTT+Wx z9TmetXq=OB3syDF*_6DE$PWlV_C-yqFQ>8)v7E_yo7H81K~4-EN#t*VC>!@Ilk`)xqGCb^LM&4BOZo7!X(-P`~PyC-a%uRyXL>sW;rQT(a4y0xC_ z<~U<^g0WQB6cBY~bE^41!FwAU1Bi_7f1q0C7YjErZh8hN|=#2l&u= z!=c7IYN;@T%0aa+37x?19oQth?NQq8sC`z!xP~W2Yd(Nd>Bs7Q1*1{j(f}CeW>W2^ zGo049EyO3lBm6U|%E~A`3HDKdWH(6+y;l?zlY)-kdR zD3tgMxt@h090)x`3toxxaVlcwvZ(^w|Nr|~HVfX%*G1D2`% zFOibs)B*dtlE`8N{u4YQaxP)6^?M>8EtU-=&KCDxbsV#EU0Qa`!H;F2a86q*Ak{u& zF}Ru4(us)JwS<6g0dJ-xLb{X0z(lIrztB8;3~#QBW^X)TwV2`3Zn}SP2=LdBg?Oh= zl}37R0Bd)Lx#W`JFG|WbTu%K1VHnZsK*eh%wY}~5FZv*q-GvB;-@`0ca#Lw~fE;F# zas?^Xp;F0yp8@S(HW;FfD60j1qH_upZ2@fEOfd$F{Rm@(6x?c?e*aKd-JTd(CYE^% zl~+NSUUfB7g`D`}n!eMm0`|SUG?4glB0=WU@ze(%97>Mu_s8)9(Q~rUj*;+z;m_Lo7?Sjnv}36%I9H zyZ+!66{T0@y0i%;_LSFhUz@dBe4L5U-g+;EKqXv>$#mt3)CpB)P9G8#nvA~l6zLLvNDE0$udm&$iV1;%d747>60AS3T)1N<%6;4|835+gFl?@>Q9VWjlQWmjht@ zG3$~|R)fPq(&0e-7(d}d>XI`V1vfQ>!cFxIUHt?#1oKrGcmP?-(!gYuaFw?VmK395A)+b?n7()HE| z+)fv|+I1qMW9xmGwFK*3ml(O%P%7x=P-C~n&T5g*>lr?1$NFb@t1h+#mIFD0{UL@O z6)Y>g4p}cw%#m68z zHuMPHaU1h_50VTq=`MtR3G%cuvPBA)i9-SK9BSDatwcu1^0Om-?YAd^!V!jga34yW z5SRR8P8O*L6)CVDukNyujHH<0=HZVM3j<)4Dysap7UaR>FRJjHC4il;QMNi+Y=cJM zD9Y!Qm5=_8KV_j3_F^Qn$9hz#gNE0v@orhJDp{oS)>-X`Lo~YhW6H|-i0dNaa0^9) z7iJ(m%mG}dfMWu+1x(@&m}#R-b{}7g+U!e>CbXxqB**IbNx4X~=Z2h6?$Q>kABp_W z_OhW1g}j_z@%eenf^fxcm#Q>7IXWc(#=suETv*z9zKlRJoYu2g4=V@NZ6Tk-4i^EO zHV_~O&W>!+Q~s<#!oZ@AT5Cg>4(Oz^UCIVRZ;U%e0>jJ1)kNNiV-AZz3iUhTxH$_l zH5-@jO0S4?F2%{)Yj;ZQtz?W`ws@Jj(w6|rO0&byBCyq8^Lt~@SM<>piv6m&cgm^F zS^XyQiY@Zj6s$ppNkmq2f1at)O+M(!hB(AVG6;gO96(fEKh21QUv6SqfzTC1Tqt2E zw(U4k7aV`%a4PF0>IUxBDGij;vnW{=>p))>hSFb#H|zn-l_ohSI`7WH%41GEY?yFDSSQ$)Z7thVFa%oGyy@Q_&$)qbswx4Upl{Z0zq#M3$IG;m8mWj;W}CJg z!2U)_;miiWgtDhRs^1q4%eWB)0b_*~tY)~hQNHJ&R#P5+@Sq!e{p=BzPTQ|dMj4|i z?PIMQdrKf^ltM*IOkMtVPi-eK_Hm7oLh98!=E zwWP*6Z)?-89-Yg1zwBo)*4_I2B(1c}Mm@AgWxHsCU^axZ&nB3K==vpnF@1+CfgI4# zc%wd6aWQ1M(6jN+tnZLr50KpLu3mR6`kp$BQlV*IOu_Ey)b$(emIM$Xp@Xyze3xD1 zdQp?Nzovz+_W`-$=fcSndd9IFZ6FFoZkVm+mi>iF9ae8N0=Ymf{#s!t+PlLK&0&2W3jSE)b4aNp}K^x z9WRpS_0E1Zx!n%y%fU_GbAdVE7tc^Y7W=-Yx_g6x>>&s5tx7&5ifl1L?W50N#t+3E z+G=wCt4fYM1V}Z#7ont8MZqJX4k8cjZiL=~I$smDa>Ze7j&38BmU+rwM_>K2ncbG_ z6Mhl3lmCC7{r~(50rWJGZ*_6`QkFSbzmfcI z5}Y$uy{6!yKp>RdTutq#pQk~m#V*5&;n4x7gKBD}S4_mWWf6<1ta-gh;=SLf9eFgQ z7#Z#T^4m2_@okSGPfapJu{u0u4O9e=A8jSrEiS67s~zmU6a-2v%jmD28a%9~_DX~3 z(AvFz7w7E@{Q#$}ME+_WtNlAQwS)JD6x5>)+8fp0zz;n@d_1amKcHpT4K+1S_tdE1 z48#>RwXvMDHm%9vRMx-B&|b?r&djbwpevYOhqt0`#Q>Ge;bcA^E; zCa-xaYFV6?xh z-8y?IHkAde>-P;N&_dv|_ufFQKCrb9PgxP0flsf&?PztiH{a^M#z*{3?ae-8J7P5O z(4+s`9-eiN98(uTYZ{NA3!i%5rmuFZ?hvqaA4X*7#IYV_bUno(!IlTCX{-&{nZG8; zq$^ioG_(Bd<#@5yoAZpKdnsn^Kj@(99;5Wumwsq{qhVp=MD8bTvYF~>JyyAz-kco+ z^Hp?cW7;3Z0K_7m0f<73R|M)KTkD65$0gN>I$i;yE;LjaX3u<+Rnyr&OUrmuEAO62 zVUdraW_HVc z^!I7@Cv?X^u21$@0ZiEu1hBBBX^dK%OlwkK88hQ!*I8R@1@I*KT4cA}*xtqdgMA4^ zj~as&M1k88Hu2^5Om{0qWyLy=w*yS+Z)~KeveJ1SqahGFz{x8nKNSZ^YQeq_h-Jl# zZqCo2o05E3bNrS&eN>tzxozWMmHk?aJ(tk&V<_t2=JUCw(>U(bW?4CVMlPF(8|K(6 zt7m7TpDH@oN~(?Ro-e=O-R_Kj?0KM1pH0&4~Nd3anCw_c?hfRT`@O zP+N2)kzdvfM*?2od4vNkP&}-`T;>R(n5DCvG3|5vBRJj3_?4mCePK6u_&Ft6q7P7g zr7#g}>477|ULV?*ux(u2K+sY<7`RTg6On%K7QBVjwXO7QC3{0 zN;p7Uo4o)l94%f>jkt6HB(u=CkyZA*Kjs^?r;%UyX9whmx<^5fyzSsTu?I&3=AZ-h zZJ&rO?0X}X^hUVIo)_dSKnqVTRY~Hxqp2)Nv%WRTNzXmEDeB;WAG9MfEdUk|vNdq6 zki9P;Jr;Xcj0Q+L*r9>`ttCD!zFdh>InoA{hl<@HaL}WqjMTphs$?kQ0G&P$i0!dx zMacn|e0=M>J+7&CV1gkOVXc7IZ=Q|~?h}^i*Ccn~>a6uTLIGTnZOG&50Ky98J;4U4rW7B8z{y zQ9Le8Dj|KL8|Vd!C7yry8r+NTfZ5NW4wTb!r;4)jhH2%_Z&Z~^;TG;+J9wmvrjGrF zIuOE6@-M6%u9fq(dqJLj$cNpPS%R|r!eW7GmVw$W(HCr~>aUgSK)X6(K~bRtlBmjT z#rvU;xPWwGJW<>_sSKw-v~{LCB`aQt*@wt=v3eu8!4OpQFfw6*Usb$hJVAAbwpFj_ zpD>>E*HGJL4j2xn-K9Wml;>UWvO1?(HeZk^5jI4v^a`5`Vt#*CDYEAZwKaUNfPhrn1& z^2_Q{9ZuHpI*4_B*}gHV*_Xy%Y|ipD_%<|8*9%A}1;U*COcmuJ!e5^lq_U_Kk}IpT zs4V5eDRTVmXTT)q;py>yC z9%dxTI>;?-7nNY{8(ZsPspMKbS0l$^9m|cazQ9|AXlIgbz1$;|c?Dh^VMa=hD2dZd zW2MKouPj7^*(gAi=*mv%o(MzA4K1?lFJglUP9Tf2;<0sXk2b}gjE^*Sei=;NroT`t zpFIaQ*~53Mlnjn0mPXH%&I&iuc&{r6_bzwzcYwrEzw7|4*R3sjfF0Jl80U}WzWrIa z`J&avn;ud9e2(hEM2W$betv6N^V+$qw&*;!&UAbLjI^0!G`Xm)jq>bx_ZJaz7N2&U zn=Xh%4HZp(mx6Kl!J|H}Q4ehP8FA)C>vkQ2KYjne(h0^|PYf%fQS$IZB)e_cEs5)@ zbl(g5J3n=KJ`On@duZo>u4k@2V();;PQ_O*BR9Cbe7@iuL`a?wndMv*S`sV#))i@X zZV6x797dboYQd%zeXc*r4uH*2b(lBjkL0zcBIYK4Se6j^9Ee}lRnJS!|3>iqL-xbx z84x{>y!t%G13f`Z)YIq-FtXPIO%n|rAd$<8KYg|;6aHxIue!FsM!wd<-{JU(FzmFY zL8maj7V7gLuf)|X>42M0PqO1#+Vv8r5{pH`k46|%Krz{j8_AJ9K`ru9^`4*!14?m! z@=bHV5PQ}E&eH77(KM=wEX{g0Xeo)Sz5AuXk$OQ(B1f@^Tsh}cy~JEfww={W?GVbs zCMc&x&TLdOHo9%1Z^)!L&H!yRq<*$iHubXmn|LAaIIin9(_?`mDAkLS2&LbNsezsy zfBc&vfTt18{X66(|GW5lqlqa4iwbO$;mUMCZL(tZ?WC4;0D2XNvlk)QFNdgw3D2EFW6*m@z_oJHY)iHGO1=jAPNe4ii;*q>xeR_~j z|JvAxt8S462e>k)_BIiNhQVY=@TCKg4lmF>4`|+^hucM5qu@wU^HA~#EY7pT3{zO6 z)h#~E4Ri0BV&O|i1;RsMcb-@-V7*0E!U(c%N#B`d+Q7J=dO^V0uF>IPTaTZ|{pt}4 zBt#QX25MJ2*IkesYjel@N|3f8hlumBYYqZ!XqP;rh*M#wh0f95%w`9ci{9>SA52qp8&*sGP`4iCPm%npe&a2rbZb2q4wHhy*>-ujoz>%DQjeQ#1 zA6Kkn@n4Zb^u|L6L9u5r|2;ss7PLoQ5U{cLJivqh2vQ7IEe?Xlo;q0mx8MWlw*Urf z{f~nk=MEnPiT}OFM{k=0g8;iKr#-SAplu%xQob$SQt8>!@|Fr(7&k8)}f(fay!8uU1t1yVKY+De= ziM#WY6FpoB@SQb?{Dd~At0m4xqcTS)(EXGt9{)~$E1_e$-xI)B zKF^NV@lF)ApizY#u}KC8D5{Tx7EuwlY!Sx|K#lWLqXMzno4FHdpQ~*5xv$4-9>0kg zwX%wtUnnZKIEGoz66dtTVH!zEE6A^ODz`76aizyydTo}Q+n&|giLq&Xhbqb{_2 z<~_YS3M$mNyI=jATRvY@Y;YAi-VOD?%ffRS=b)Y#a`RG*DOIA!)P-mCV%AyD7;*M<*+-BqU-xgQ@Q0kfZO z5VZ*B2rqB3027#D!*vgnrnRyaIkG1>CgZr;6;~htm)uASwX?by4>#IdnevCPv2x34 z-K&rz!q%ehIpb(gjAQ-vpZ5|F<=jE?eYS{HK6$2{s@-KPg&W?F`t+t;`X=#bNyCVs zcI}r%3Yc(ibVat;-iH8znME&!t*l;9*cnT1`HV7wJ{0B4d?{J_0XFk&mob52`N-nL ziL3`I>6rA&nFIUjiSix&AUA@q68<=8LFD%e;mRYUtYgF*m0nIQRZh*w z#Q3Y^-&Yk5Xy(Y0G_CivMZ3Xh$FjVcuWN;aCxtio*&>YWh zf{SYzOY~H-WH)4I#N4)rG1FG{Y^x{vFL)jj+z)Eo&I8VEN2O^eP|J@`bV=##JKKgM zKAG6{rWPOFj)9zsQ=XYzhIfpRH`xgsk)%wUru>9r(@T7Z`eO<|H&L!cGb=;GwDT8t zhIIjZjN6ItiFtu+E?%_TcCC~_{Uog$KP$W0&fk2my5o*b4(4&AWCci+#I6II&z1=k zu_?SprNAtO;qzh?Pn4;woSvpIl@bMWUxFr&RI5qWNW>Kv$cbLy9)BUt5~Tov=_L z%hg2*(luOEnXIaS?K7=$IhpOLpBx+${tBZPGDH6g$P>&C;XzSx4rOa-rdyY&&uXa^ z6|VCf{i%X6`Gbam&>o}i!q&=NHdcH%+%ONO^#n?M2Z(Wso0fA8Kdg92k zplo}+bDmF01~=EBPNo$r+0uX308MzjxG5BUYhRD;IV4PZV)4D#uGSj-UeOMtN&ThT zWOGx8HrtqY-N||?(~0YpcErTHsZ4av2`k!KLPOnhfGEBcXpF#lDQ+G5Af+ih2L*GV z)NgjZa~b75$}o?sksqtVbU+72{qNVHE;VGau^!E-Yp49u7Jm>KY>*l&8azSW30{7C zlOj|7+^>XtYJK<;fnR(r*BeXJdMq;f8!y2VBQI1Gp06{G82~O&mje6)ev|F+Dv+HJ z-)Vq2@UFsIKQkyjy+Rik-0CWnd25}XMat!8pL`CQv~sJoLm3jLIGb8MjO^_k%XdQ7 z%EDqCVUR=YxSkjfen%|A7cPILuJ$T(olYhN#jNhQf9eE@KJ~hJ;7Tp?uw8eo-Me18 z&6eLdRyn1;QPaMYxE)P(zwpFx<*aSx+N1JP6T1D6-eI8NYy&Qqm$i#T&VpGvo+^cZ zKJ~Nj*3wTM(u4T*4%3OYx~6LTCAxlFNJtn~u-HHgsuv|sLS|Df@xwXWmK=X1oub)n zbrxAh2K_Pr?Ppe3mnYa3ZF>Xmu3v>f)U6PE?>T*plbxAN{IRdi5R)m%1y>Jc~sJX=h z-fQIzg|O(ZBQ)OoSkt>&{iju5@knM3PRd?PuQo5V_||{0)|NUM35Js`53<@{TprZG zFM&?w<#0nFQ_qb;rtsnuMNgo*W7_2JG0sHoJ(GrKizR}=Rg4zO6NB)z$A=cbnp5=E zY%fV5q$s`mlHGBNXTQ$2xUa5EY5p4ytW>GKHfKQPvs(K+Y{2f?PPU>G2#^hG6OE`G zxu@jz_f9zr-!lzSAEs^wQWl_stEeC_Z)IZ;%2HOed9(IuQu09jtjZa&zc_sq4o+Xp zj^-MYXXeWh!)Q{D0-1le8s_pHGG8(ESg`%oeB8VoP!#trA$ujv#Va^IGs&-h z2kG5lb>r<%oyQ{3$oGqDnh~CUUgDNd{0EEa_o6k{E>37~=4M*W|1A5a(&K!nH?Cn> z9?a!0NcpgO`&AQKk0E#JlZRyk)?L~9u?uLEh{)sSH(Ogd)h-(d=5Xvuwprzncxm0bw^U4W=R)G;sg__~@(gJ)4HH~}WBzE^qw!)Pt!PV) zz~NN@3|lXzwifrLr`efk@;^mKc;$9YiK~H@r<3#ldYD0In%tqVoCtOAYCM`Y+wPRb z@VPHx%7S7-CLT6HcK0UV8nBm8xwc>^5|XwWYHq)gB6)VHg7L@v`+qCn+%j~W-{K~F zH7$0rQxy8D-X{LRQp^1#l{JCVqbbfn1o#GZ;cbw+7fCUS;RSu`(znOX2|b*2Bada+ zjv4(FRWji{?ZIs_i#7=XRfaP&9WyBV8x(Z6Xl=Du=yij>6sLxJ!wn;?Tr= z$&{f+7KId8xa-~|=M&MllC5p;H4S+@>s18V;w<9cJq=3{$PzlkhqEn0MOMBQM~7p2 zowA%_k6%ksn?_*ig^L@ol5)+6)Um_R3T=;kqo)_1;<7A>Ny7eRUg9DbyQ{0 zr_?J-_=D1S+ftK|Yq`wa&uJ!13J|fA)^olUq;rt(WCpf`xDU^{%mFQ7;f9a43$hnn zF|m~={zk3p<$6uSv?28-so^7#KC^(nL^loX`guW}oatix98O=+8>)4%SL3+EeZcVp zBsb&3^O%#g2J`l$Bv-fDqBHwS<2*5Ir};{D(H&Kd%sS8g(yFM@5Iw!UJpeYwmxX?f zGPoWkn>Tywja~Bx(uaAwCOJlmC)Z?QZygbgRpGP!D)p-(?mc?89$DNlA-m+ntME0T?R#^CDFL;U4ZGlbH*A7o=h-7GaEz{&ykEMl0*3^r z&P@k|`h*O-|?2|1lYGz8!kv%Q`kMe?nK@gCV&qD;2x z=%qDG$T;x7yDnJ&GwM_SL_YG5xbJcR8y+DbX2t;tC&lcA#me@`>9MZJpx-FFD7`BA zXfO|wRR02+AQKe8D0W3tjitGGtpZY_v*5exRB5O=EF!_9>7rh51$p{H#~2k#y0Gmp z0x0y*O7C;31b7f1%HcmuWoZ)XV5DEe6{J--peveq?V)+6Ls|O;>RhSzlNfa1q9`%7 z4$zU9r9)uQyP%iaA0c{eyVs@F>NO<>km*i<2A~)sQ)o))IkCj5hzU>^&&=ZlhlCskq~uj6tQXK=qv z$S6p&gd~K8v!7V$ci8JK=ZvbO--{FT9<`N2usjbdv2bq0&QUOb=>{P9n(AZ0D3MQm z|H@H75`qaYkY{?v@)3kI#mn2`qjn5uPP&r?TlyX|-cU(qZtqpzpio)_waMGE_9S_= zl?iT4IxY2O$0{(8J;n`+qfY15)KY*#F@I<8(k8Lp2`;ipet~Jn(z77%h|Qf|;UtEL zfs;;psda6aA%EZKMFBmvTTq0nisRRGW4?JThoZ@tj=xA`@3jOxix5f}57c8;M>mtR<#45GG|YONYUrp&(li2I|S2 zNQy&DO&|v8c}?MuCOUx1>`zteXt7sNg`VCJ=2cKSNHb7Ohyf|+agzkc$%-=*`?YHL z@o?GrZT!ur$k$=i%@6ltX}{nszqj8uQ0tJC-RTxO=$~ofK2qXMu9ZuqYVo8MEoV}& zKW9q(oCNp~bI(^jGPB~7s`Wss@`728xUIeOgb8t|&5r)tRe{I+vam~8iqWfHc9{3g>cr?3xjNk{KX&kZ?3cCeJj_- zcD;l`D?#7oUX#QP_6`1$FhwwOYlLC9hAd>Y#9b-Ae0Nu4>8orpKK5^~tm+w{9!@hm zv5k7ge6|Fu`oPLe_l-ILZJS^}>@X{K`*kw;cM6FbDS32MC`Sx0WA>s5&8cwfrgEU=EehzcA z^^Z4N%}nnZS9ZqEf3_xN8a{B$3cuG@nK!x-aJ9ikRLar_UD#+4wLH2IRFpA4?aie3 zZNjoG#|wiYM=Ze=gBO{bY!LVf?L+g->GPM*?Q#6wO6JMA?4QPJUgj=_A)w*YNxL&I z@&u#vCDu~ZnN&nG!o?*9+NtRgo^Sypt^oJu@9%NzhIoCfF@Hb#M<=JyzA1- z4&GmqcA6;UP@{*32;q~BFnHqrzMvyZ7T+b3Suh~!@t^|@sj^V>w%0BA;3YMzc$-mfNFF3ULPHblD+;}Rb-j|)oFHwum{4>Kon*>D5t5FDF&pWxxU`K z2&vP_&yGvt%)@fEkQo>6poMF~hT%G!db0^oM;jbA06i1pmq+iQsNEVpw_bpMWDB=X z;C?i%cc}}`a_wL06M=at-M{GfDB`G<>G^WeBb)Jnw8TC35AU^<>UCd*igb4ZG&jMM^lVcmn$giM)4 zd(soHxRNN}J33%)i#?K%13+%|<)=k^qneU?j>RH-BdabYLRfYQunOXkq;XWcaTBEv zw?qi`(*bIR`z`@3McQwHQ1ynh!>jPb$G-RWG|WcDhc<+GR_6f!3w+kuB_I`hQ%A0*n%7f>7ZnrLNqD`CZlG@1gnRLG{ ze-%wt^dQF?NP#;Y_m#3q?c4rIstBSIClsH0oxJO-CGoQG0oqW@XAZ+@k$w&)2(aUUzqCL$h`j53q)BW{>pbBaL`K z5uf`9VAB4p%`D)tkIfm|bT}=CVb)#HUa+|uYWpMga*ev$l-qx<;CK&z;$?ncQDX9W z_&F)FVrHbUVrG0$QHQ99&wS|Y95Z!(s;-7h0b;QKYEk>w*#-xQ0gYFq)kkXvq6dl@ zn{KGq+z$Ae|L$sgP~PQf<75Chiw}~joS(Q)>E(FGcPc&1I%m>gxNp=Vo&R&Q|L<#& z`(G~f{ogbP0{w`Rk&&MO2{HcfrMv&=FYFHdcN_A#ivK6S?qk%u1i1-XB)OV=N#cH; zHT|GOx%>-Qk;H<0ZAyLq!~0sFX<6(}KNx>7kuUjYl;D0Z^?9zStKTMRc`$MHWBMAa zA{YB-Pxe26ewx?lnZ?qq?ow{D?2NuGa;MW;L4>9Qc!eHdaZLH04c z&yiq;c{RlzdAlnLcuR|Xg<$OAbyD@n0XuX>5Dr#2v%)m1hZG@FqP^K(n!?X#t(Jes zNjAek>D=e}-ra2G)-|E3;CHzxqRf$Sp!6k;mq;9>a~JgCl3(Sk$1AA*h>MbM`by1| zVC5Of*S_)W3&DVisf`0p-Jt|In>=7k7fqu6oWt8MG z7U8K(3p1KIBa||nE+AEhXg0CsRB;U*#IQftfr$1Vm4RW^!=PGycw0%rm-iOzK6$wq zaF8#>c_MdjRaW!MQakQKl>FGMn`FUwKA{y#u->?Q_wG2RxiFU|>_TEs! z{gQrk8rN9&1Tyj2YGR2)W4`?kzeZU<+98#yE z>8@-8UPIuI+G>W5jM?XA?eOgLe#LA!x7{95N{DI+fVC+%L6Xc7r)i3Uujc}?$4rU* zXQMz`a7mkYX+y6fgaD52{Y!eRL;db2<#AjZB(Z&w%lQ^ z-vCW*5+4W8y4^G;=Q~J$kuUli`!AHCR6AyexNbPF^Xt6x7Micx7DZMA?{CqjN>Br6 zQ`9Q!&gaSLN+|ZCQhSq=jH?kPYSanzznUR65)(GXggV`22wVCPRQ5T0e#f--A%H=0 zenr&G%@WQBlqhYDC22_vKuOZHSaDL6?)_rFS7uw88LO@8iZj3cd1Ap~orKIXK3 z|Cgqyp(sx!7II%_h})=lg9KuxOz^q(rbmY^w053?7xEgtk6+CN)+)KXsLXS`UX9yD zCaf(stbtd4E4+PEXbG#Y*wqQSUzT9nc}Ji#vl`C2yQ<^aRl%=sTPOWa122ph@@n0o$BQ;#-luEqOdNE0Nafpc8XlY! zSrtnV9^iP1VSyAm95_F@)UfukF;n6+6>bW+^b{FTztD)e=KSyr5mI!|AZ>uS8c1Bdn@8GL9_n*BE)wj za!}jX<1{D3fYYrXbi1FhGRW8q-m(A*DDcw>c%hcdi&vuBGJPo zbpu5H!b#JT2b-zHZ9V^FjYr3j{}*|0 z9@SK~^^0Ozr#N*~C=ZsXG^w(~5(E*YC(+UpTTlT3X(Aw?Lcq`j0wmF*6gxmg`c6ea zq>1!RNTNko5Fi3VfB=yuLh#D#;4s9_mQpf?9P@>(eh$3C;d__4On=f#I@Lal^$+}at5EHo8+3s@IcV( z47ep!;!gXa5nGA%)qM74kSNTx4hq|n#Gq71SasuXdSC|$K$93`-BmBt`Ban=F%R^( zNe6=RNrcNrmv%g6|H+)S?G~gjscSpV^m^}!NZPvhV)Fde$nEWjX614=Q)s!TM9X>Q zxL#GdBe8ioY1<_9`pZsPT2DE}tYPc5FlTp--C^N~ZQDSbrXJ8&zB3`zey5IK{FxnD zf&KRR8VauvV6+s|eC=zfr_t+agfzm=fTrhx`ApowtRdv`pe;9gV*1H0lyZT|`0uwC z_$oSF|AH7!*8DPl{h4jOv_Tn;lX%{wAuiPftEpP9G~j zHWIutlgvIO)F3>Xk69cDkx=+}-52>q&Z|HtXa|nEx?%M1Oyf}Z&X?v}I*`Bb!i#(K zt#5LKF;1pW0?K=yMIM?n!IexsT54MPX2y-{>*TKW6tOL)b3J^IE!R|!sC|+(7h1Hh zE5S1B0ktry*hQGLWI-_j81r;=RRugUT}dV=?*)Z&ve2j2^+J45g?y*u&P^G zG>EA<{smn9fdC2oD-Ud=FT>_(e7jZncKBiizo~+bS4R}C$0IX%fj>RYm6M{fGvhXE zFD>Isx8(PD%)YmoaV`cPW#3jm+^Z0B$T@gX%Q=mtYOFg__ByChlt65xO@vD44aVc;Zjy*Wb;4a${L}h8TgrF7gakQxAtsE*#673;&VP~M6iAzm z0j$dp!SnXmKhm|IyTZH1o~P_x7U#@QjAs8rY}x5}T6uH|WnmC=U`VR90;CAgAjrq% z5xG;fA@Ap|Bx0Aj^s%OCzbE>+<@>G&|Id!tm99zm3b@+_nkR-mz)Q!BH zV~5^@Yfn_ez*8-&kLD7q^&9>vM;qyD>LZC5(w!FmDZML%{THSls2VgrNW3M4`6Tukb0*dwW6pjL2Cw7hP^BmQmW@3r}R!)bxkH z3?wc~l?{^v^10>%4Rx%xpq-h0bM9#1Of0m%Dc&A-GEuZVI;b)48R|qv5U!=s@zNt1 zxY^tNj$6(1qlL9bu!CBo!`5=jCWO_Br%$B8#gXJ`@pTfTPG~hdRT?ECEq-iwMeRXu ztD;72$4BHeh*82TWwu#!^AowN`muZZic^VkuC*-hD4o}ITU8f%spGYd$dX0>2j>zG zhaL~P_5%;MiWR&74#kBynERf6jQNRp^_ZX+rwdByW6_N69q(i0%q^==yg&utI*M%U zmS<*to_{~6=~hxY{eYFBzij;Oc^r4_3xA$pf=8{n70Np00t^_b(HEB~ z5v9zu#V@TJ1g)FH0ex&KxoT-f`i|h#+WK>@KXRx@rZjm>#_wrktO6Wg{%WM>7>HXs zQa?YAZ1zo*KfO58u{Z_hshjG!Vz>G@NF)8PfpYzKSOxh%iV1VL?JEnua_TozU;4N6 zWDX#>dh{0*@1+n>ZT(OCFIIJ#lU2J&53FpX0c`5(5ceVPu&q8=%1ZvFJ}b=jkPzll z{b~bFeQ9S!)z&2Oxsk&biOGL)I~G&d(<1i-KFj>iZGUR=-iG}rH{8yXTtHH!-KKFh zpZ^yC5*lf&DtkkP9rn-d=4AI0$heQ;e=}H6|CdBvTLvr%N3m;X?cM*}0iRT?9MI!E zufK)aYw#Bo^b+B})x5t5E&qSYIr*nF{;#9rq?7_qf_PzHvCF)IyLM&KO9{IA4x1;O z^(~)zi21OMcZ0{Rck1e7D2O>@2a*CX4$JlR62T;m31<3X| z{XfDk!FM(pWR7kg2IxsG2Y{9Y`O9_3M?djPw$6*x#h5_=X(IZfLlx)B-9 zB*~)rm?b{IC^9cOLM}US|0NWqP1`c;AxUJ2n<&0%QNFe_dK;dEHD~cATvwJ7hfu}3 z4cbW>6lh%D9)^N7XCKbJv2;Xzj@;k5vX{eW0{DBS6Bg%*S;Fq_;hyRODfO-df?{CB8(YRuU}j zUTU|XaA?xSf`+u6iCXb@#Y=CT$(3iSyecRD;yE4TIOc;%uk?_-3!jtlG2O6(9~f@7 z)dJTHau)gQi-tF{C0m~ZN0<20^Mr@7L~b{`daQ9?4Lz2~87t^!E05h+V%>W{y;?oI zbm?;?EG|k1t4WJxK%#sT4Mi#ZUMvI5QEoEr=y0a;SmV#4SM1!S?{FNNA#4AM;=n*= zC(bZ({`hiPe9{;{UZtYeEj~gsiM4oAGCwOD0f1Snukcf+G8O|Oq?zc&+4kVqAY5uk zboDn)i$5=V zoJ(x96tDs!>%Uj-CgvkvUNyfjTS7RD#Oow*(j^fmlXvt6>VvaiPt14}=7zjWXe#fv z%WW|g<}g1t7C=+<=4Y@vDZ2+Z(1u7|eYdYhNjrI@=0>WI{ zGw93!b@F-|fDZXjz4|mc8rqbo#va9BP5@44gy6VqhkNx-TnW2n=s>^z$*dkDHcbQB zF#QG`2+@b6!uv=h0MfQd>@#YfNFvflF|#M;tILUDy@{stS0t2?>*m+%1#jw?@CU8# zyhdinr!G`k_tj0cuZTwJ)Xc>CAK;}0O&>i;nQg0sq-RxBylmi((14?Z_cy2w5AOku1iLf6 zzrw?9zQ|Z7-?ixw&5uhUcBipT49G)lGtQo?PIyUg+iZkWxvWiWOcF8b$?Jb$Gc6b6@HbnVb* zp}pXf6i4lFUeuK5_-E6{+RxqGVNwM|?Q-N#B`b@yZQcegvLF#gC}mcm_WBt3+-U*$ z_$XAz>_DPc>k#dYC7Nw*$8LGg??=St(sah&cA`q9JbGWeC6sc-Us7PlC?c3!d^z3G z=KVlaHtG(uvgPx{s40QGBFsf*#1HU3*op=Kxs`lvLTjme%62u4g#f!{cZy`=1((8Lz8!T0YD+;Tfafd@ZOiRUcp5;*6a0& z=`bl6WRtLV1VFd(#Sl#Y!LBf@n%SF!%8_@fW zcCr9O&s4S)HT0>hvAJ8y@d~Jee>@vg0Bv>~tP2?S$}MaUQLMyd?6X*8|4k{$>w1KK zC(>;c5aIwuQ4}JNnFfBZcw8kzk4o9(XhU@+EQ8SBT0mNmEWn@W3UJC=0?0WX-uSKQ z_wsbRN{*ddkE~$aaa&+Q9p2ErgreGt%XYe2XW1o#=QH6cmdz54Vg|GnRyBQ5@UZP? zKnwv^1_d;D_$=Ixfq*-C~d5XqyD19W-ti1UA%OkM$7)(Vb%!!7^~RhV6upWTu?i{{(^Wj~cY zxh2{E@4oOXFZ-6{&A_B6+ z!G!B-iw{D^70O>SV}Hg5!k&?>d{{I9X9d^WJ^%Gcs&-VGmgPqL+)-effS+yfLr;DJ z@$h<+-FtNg>$xi(rCu!mb^~J;5uxzS<<)MK&G}iw8`F*G-Vjm*$9r6J!z!4pQj%%)Pbq9* zCRgmLrZkgZ5BQ<4rHh*Z) z7%yov7G={&TF~&Uw+7R!dba1yJlU&sd}W%zxfrFIBy9?J&8Se&Z3e^^t`HcXw(Vr5=yninyZ@gdriA{EWXv7+^m;QLH zM4QTs%gnrvJ=3tP4ed)ej{~8B9~|%)Z|-;6cEh%j>=M@WE~f=VvdayZ8$~JYD!#BR zl;w29E2cBxqwEEMyB4(`L+NCJDQ4mF%-|(NkY(l6yj5xCLP~lTjp7!&cEP~5DYZVQ zl#npwI~ns$7;51A9%9z1s>f-u(a7xfAy#w09MfrB7B8i>`ng0{DsWOyt0A!45V_Xl zGl4}}XU05PANG`BggThUQe*3S1CtH8yrsdoej}rFe*mMw zVjTU8wY1!m|c&yBa@S!WxM32V=SSUNIO4}C?U{Z{jS zcU$1W?AL+Eo14CW0ZDbH|I&@2XV|g z!%S=^$)-yC9&Yq7?z?(4FV!ia&a(1Gh#&5UdZ~S9Xxbf_nu}Kiob!q&i7qX`Wb&Wv zjAb}K^oswGd~+b;An$~zc~m$dz%pvTlTux4vqLQCbdKfZ!qxd1)vx$0T2rl7CAT6H zvDy7K6|NRcmp3~ zfW4l*uq$?L|Gl9#m;rvPTP1fm?3v{1-0hT=>!B8{%av}*mKIT+8bQ7>Y>foQ%VGW= z8v1&eFe1Tl*`|k0%lC8)wLoU-?=$SVf$bFz^t+SYLt_^n&ID(LPdLp5DnC6$i7V!L zPh!KO5SsyfW*P(L=8kYqgUZ9Svt>6G3h3f_MI$l&DTJ3{4ww^ll24U+w3y1w%1h<- zp?}W^^$BE0+_TCT5bgLYkNaJRCr6AVjH$iS!B9Y#?&hAne5A1rN$Mo_r0Tqm_kO{} zT|u@aakDaCYZ;C#Mc+BjSV8-tEMj@qqDp4Zd?lprZ}vj!{%DW%k%d;kMdc*m>$eE> zUAV*6f;HR)32K7D(1=U`)k8YMP7BVD6q}Rd2HxRYU?*M<;^`rgv(VsP4u#H~@4?Y| z>(ZMj$tBnDP;-ox<<t^U|QE<(q&LUk*+hjF91yjF}?yRuJ=52 zhaF2SUpd}pgJR|^*GgvnSw67V+5V|k%dr;bc;{YvSEo*cG%|Nr?i4^4- z6sZ{{{8>+LcRWZl%*k}mZ}(QSLq8uay9lUsb-d_^towF>qO0)8f7<@%rh(+cb)|Az zB8^fH@(kTrkw8N+e7zz=EnM=vMjJrT(VT2E&{C&sb7=j53qY3|M3L$=J8-4)*=AbA z>OQ1M&WXAT=$zJjJFRqvoT(xey&GIlOSYYR?Up32V}#-}r;pCvE8*GZGYqY!DG>t` zf^{2`g$jZurJJ6A4S3rd*^oW%+aw)x5Tps-EwYb4j0BOVO2W`3lE2`z)}0=nca~iz zw?a0VH7G~}(x_+b=3f0slJy5xOFh0RuFX5_%*=he4X1l5E=2|Y$f2y70jrr>u^!m0 z=fM8I^Uh`bkH}Qo>ypg-1=J$#g3LCT!7E@S;#ngwu|st*p)51Q+ha2!Vkc$@NhUTr z<_Fu1VSf}cN{lwTWM{{-lel`ji5@uIg8c}$|IM8xtk#~#_Gjcl60%^KS|f#ER!+NKgL-;tqoX85ozD^ zN=(3NThviU(Pu3~US11_Qn-Pu2g;vXogzSHuy_OfAj9OhlC-;Ll{*_}#)a}Mb4CS( zpi+C#TD99nc_387tEDjCbBS0eCm-@5vi)^R!-H(k*jx!@?y>qul$_NoC(UwG>W`Pp z8CN=0RkjTaKZmqe>-9o8o&Vu|Ak_dp+cDKI{Cs7|MN6MWHZ(>A?T;guJ5ekJ87{UM zvqleWt7kh^f&1JN=1^h*<;zh3E~{XDGkvC5^+Q;uYd{^Y8G?TT_->KbNxY-~P}vR@ zZ3Cjgh=7~o1n`t+cs5m`gvk^933Q@+zJ$;H(57(XtV&mZtI9*E(aSj9*TpW>s5#!w2x~l^cRqNIaD3@ok_(4I%1Cwbw0B8MEb zQmWC6h8{z(?K4-6T!@RyW0RhKV`-{eDULrf_;^wcnf9lFhdOf9l;3!ogvKvtnCE3+Y>M#QC*yNO`WhR3j-r@YqQD*GfvioD$nP z>MHKN=p9maKMBZS<)Fq{FbgEPZTZ{O%Jdrr9p4_8Slq`%W8;P(>mJ}x(q1q4v<>=I zk#bJZZxS$`JuOM%-H;7`N{m$QP>}nz?ibxsMu8!Xdj(P>vj9jlIPe&{FQmCa>%Fje(A?kY+TtH7c}sx@c_ zs0KzR6V8p;AZNnAQYxy00J)#t;%e)cn_dUnG`$1NM-p*zP{3()-S&i!+&+mUf9oDiQGFf2ZP00krPiVIF4jYi{E8i3r10}{dCeCF1KX$N37q1&YIpKrh zKBwaa1*Yg-9DY1-l+vHVSiqH)M8#lB7Fd9nDz^~5qHJU+?BIuD@Z7_i_~jtIN`D=% zsR&m~xSEtwSze#ny_6i-2r>73r8M-7Vvrb2uubOz+97sCD$^Z#pVmro$I3s~nzAb5 zs}~+TKyNQ6)=Q_NTUW-Th8ADVM>vyNc}u38rjAA zs8@9_ZR&<>B&A_PVZ5V^Q4G8M=xp%YPODO411Iy&Y$bL$M%cgR<|u?sI@EQ|32&ru zlEwMgt#x!CFRfZjfe4BSYxHA=_(6%!2HR~&cZQY8-kktD?ZK8bRZx1RAi=Q5&Rq;;8}SzA-@N}X#9up{^z z^{{`$Udyh>XlEZQv&D-z>f@h#Qc->1OyA4rjGYRlR=+T>7*3ucCMQ1!5tV^uS>p%Q zX>N^y8(!B&6R}ap9jl+U*A4%J3aGnSv-n^MLjvs3OHuF9UXV< zgI&pqqF-nVdaRYuNxp{qI9rF7+>sGz*$v9D6t530w>I)+ptCc1zBh*f(H(;mts)K` z*J0DQPX$w+SOt9vfTTkKkTv-9$``YsqzT}o!yWJ6s{BN(D_!bEAZCi>yZwF!qP*ti=cuey?&#kAEVeMLV9EF^YYTv+FT}E``ya?g^cXZS_7q< z%(tr|2F`>;XLs_fouO1vkE@-AqtbKbcpTiPo9RnPNB>e?0FT4XH}|%O>UKxFoQlUW zZ-wgy{}$FOgz$UG^FY3~{)PhWG&n)wJbfTarTMSo-rDnH8O z*t^`>MaVxi{3D?@BD-?O&DZqu;lWQr&cNp_AONuz0{o7059lE&+R3)C60KjBhiuED zhxDt9fnKrt>1ros>{LL{EZUW4Szj_6h9FQp5Y@xq)#NQXUuOD`W0W}aGBvtwxkp>JIR zm_FI%b@G~E+fIxIxpe?+-XY_~4> zjpUXt?{J)pDvb#jjkZ#WBXN2}IA^96q))E7jDvK!$E&$;<09QG2dl40*EpfyGxUg= z$F6~Y3{GY>+*+u!&ALkyl;^gjMIPuZfw>;AXskR$;}X-$d)F$B`zW+YA;=Rfo*Hf$ z?AEP4TisH62_(#3`s7#cgPr3<8;c!)6M{)MA-|?n#d7cava^3syoEg12X^H1_F(64 zILG`cFt}=Q`f#ncvgN?HdYOrM-*k@~bw#b6-JK(fNFc(-*b9up|r|ut8X%F&xcMQR*3ylA}rB%dm4W657v3=*3(#`nbKQx6F@GcNpS#4 zv{AckEK}V_0&gZi}-KqJKpIs2(Bv|I+7Bus(|cduHXnsdZ}(p%xNT` z7$uBMK1}-J`ld68BFX5K$ruP`4TyK}1V?8>v*E0U zab=~xOOQ+I7aS*qBM5Bjo!L4X*xoWEZd=RCH1ka$&Q8om>5oJpNNa01ITB>9?6NOJ zu*)c>F<@advPc3kCz-0)A;5%Gma}VVTM|L@G{<*u{I18~HC4zV~IQj$|CEV=YVp0b44{(ncu(D5OaVRN%)oQ9rZ)_B1Rp6l*Hanpu zS>%^4#;TbR&~Oz*=8PA?S6|;(lqjWk?1eam(2G&yN|7KcZRk)pQ;cqgO!B$FP?qDH0!S zzFwtt3NT4e4OhxXL?LBfX^~$qbS#~#k0AMqn(RYepiNY_!y>3Ra}+l}6cXBEVs)>} zex}4bmXrGQ=_H#_rzS7A= zekWjZeU(*Iu;Q9eA(V1$0Awq|NFhJHp!S)x%@J?E_0?_+zObI?%A#l8ZwNGjogC(& zE@GWlLdxXnvOwtD3{BxoAVH{kcKXY0gs| zYcK5YPvvI2=t5IOro`2LNfQ)M^)X;;g=R&m)Lhbnh$V5$ZOWqusrX(7jwLXvd>0;6~Bp^%q*C58`$)ymgG_98^r*u3LKupXHJpM0Xlv`Wl;7vdW+sm#e4 z-6Kq!?!1AGS!o7^BNiijp9|(v(g1a&qq6CR7c37+%js5?8xhOKr{BCh;FpAy3lqb+ zFqJ-xQy8#o`P|{RNg7*fYMx!-YbPBfsGK1D{SiDnFK4*$H8FBZSZfFitPgo62D_pnQ^=?$ zU0|3#cN1g2<{U#U>MNy35Utpi*()(@EFc~jt_^CkSSiI z1ss((SAb*CEV>qL=#y;&|2jk}bcQ~qK)rGXJ{u*aWhhzc;>#)lvdl;&Ywm_U7IOML zGk^|deR0s!0gfkmX20@3DC5U|e$}HefP+A~@cKitegpRY& zd%+R>e=5`bRw7w+$sdFc_jIG!!`22+x0RG0aWDhQ+6N&w?bQPhRo6vFoj9?2xPF$& z^niuGhZ_1r%MY;4-z-0Rv`STZSci-Ia_3oWVwBUC=9u50ofjlB)H&0oZ=-!6pPbf$ zz{A4{_RkBeXt+;J5yM|z^yx*i>beHmUymd9jl(}bgTkqYRn2iuTSR|7ZmDzx_z5~7 z$6CZwv4_VY_|BKB?S4-bRsfFxh_#j)8HToKp{=je9@ivAYMGNDI!gBji1Kj`usroX zS$^~8(qGMRx+g4C{H`Gc^ZLNsnah9o14_r9Vo{0$e0xVj3hK5`1 zd|T$LP1h*(RV!2q{7&uzPML*Ft<)>PP#jSW`%e!B=MldiE6w%Y01*a=RUQunX33~N zUOxsFB(wd{Gxu6kUcv7GqA%unw?2^VJF5?k$$b!PF>N963NAbSA0G%vmpyv~>m&>! zFx)Ac`4~pB`#{Q!SE&1#w^`@RuoG#ean7!9Kj5LVR*b`vR+4`9eYajNtJL?*ovb_T zXb}3oiQW&E86XnGJ=>S=6)cqJul*e77X!Fg2oR5CwU~W{nxCH#<9>Hp>{w0uDUa1k z0>Zkw+Z6CTrhe9xaHr#EW_T5Mwzy;tN-}Jj@r!ZKL60D~J zFEcY9ajT`@i}d1;7MKQ!E=}=oCmUB2^inxhlB<#>$$^H?Lq`I@$Ti(IH7?h!A{;9J zhCQs2&{D6P$Exp}-}Bthx{%I}OEFK6LZRV4;sBzRm?U!hgFJkADMAjYf4=bVNgtPx zYrU;!5D~i}8rAgIpzzg_>=Ff0)$*ZG-LrJO3KXPRrBFO8J-m({_*x!TMrGj5l0aG# z6tH6s9_*PtA*`FBe7wTgF|l{U02US@yY&yH{pQeGE_~q)y9&7E zE)J`w-YvFfZYTstY+}>Q(O#O4)Z%9ud53Vv5G+kAfvo~$ld2FSKpr}P)Lp*5a`MsU zxPXp~msbx|@ql-G#Pa+6k&M{rc>lHxA;D{gKoW}fF;B#sj@0R@Xj@$zY+g{a@U)x# zWAe3U40|>kxO6ZfdyRdyd5t?-JPmy{20SE<$3pjKYNMdZlO6a3^&xH( z9nrQ2lnjWONU+s>;Ad5gHoIBEBSeJQ<8Dx=-rgQMA|GqDN>xoJjoH(>1ZNxg7-Em7 zD0Esx0S9ZyoUh$GIJ4Hx^pRPY&)gan0HI{~c=?5rhLd=HkKN|p3tYXtYMWrp(;_5*JS*w9 zp8}jHQ9+Kgc=KX|+Qn(nppdU^+wd45%HXiN0G^yF9j8otP4l^L2P)w%ghyb2vB(*- zVUKUKwDFNZY|lc+REMqr*j35TlgeVCok}FnVnJ%1*ywb@f|VW*^TGVj_573c$a8BH zBcT&-`pJ04%EZB?&P#B5;N5kY&Udq-Tb>~HXvGC_`v$&MzJ`IzA<$o6(M7`H?xmx# zn-yVc7vu;`eVaa=;)o@pFN$LHTcv4XUz;oKf3hmN4e<9tW!Zx(CX>)Uc~xngYr|O()=Z1(k;JoW|$K^v}(T0p$82pY*V>e`fKMt zcxpzzj94x3I z$)Ka7TxFUL%KyerOz5TQojK@^S@@M?@h9%u=1&`F>Kxmq$2bd5fuo6!&6UCHxb-w6 zx|A5dP@&nDN>1dm3^&?w%94d9dQ?%oi!NhU1OkdVS5}G9=GN}QgV^W?(XH~+e}oRm zD}2C_s()2n*%d%~p$npC)-tmB|M}sbvXA0@f)V zKEEn#i_hNIXj)6v!hB-LOu4mS}@Ak zSAW^NpOagHEx;)Pa|JtDxgYWXE9%7TjTlsRkFz6jb!u3Pv+G(ZZq*>>n}T~4A-U3` zHc}l<%wE0>^09w`PH%R$uubqdC`V$4-|oSTdLvRub6jmx5-l=e>wA1?9jzM%h)R)B zp^?jYAM>JK3P_2#UAtULKn|1!z}}lvWw1DqT!`quv z%&Xr6!=MIMUeH7FNSJ}MCj?}c%9lv>e&N!Q=cAvdVnGZ%gIiBV%oPR{jvhO){D*dI zf_%-(l6a_GL7WPp`ieJwJyJy*cLP}ix7P~QE`&;-|006BAfE1`&(2SG*^Aj!jx!|K z-Y&&DzO%kgBMTHtau{Hm-vk+bj=uowEmk>p2rZB6KkwpL-$7wn^pqAvEF0)Zt!QS9 zYmbK+0rK<=u%Bls6mcT{+|_E$ld81J5B1gGt%FtJaUMQ)vxX?k8_(JNv^#1B>?o&` z@KJKTu_&JFAh=Rba#sA0Fxe@!|79N^SObW z?Dtt_V_8|5fpvfnkDj$t*IKl8l2NyeS);W7)avSs$9?3lxZ}){>M+Wu4(?+;@uxD6 z>=ejzD6n2s_By3iUu(k$hVt#18$wEoIn(^JKb-GV)$uueKEJ2*&~jqmgn3IEv1fr> z5@|E3skICg@wLVGPsAQ)ZgTzwCaGQo}$YaNsiyK@OKq}MF#(kd;ji#;|% z>{9{NOSMPI`J#FqPrteI;C$o5YaU!4MiT^&KLYy87`4)=!*v&Zxx&r1w!PV`#u8Hp z^EDSQVw|$vef`eRIqTbd#5oxIUaivQ-b8)QTBS`MigmX%sBBY@a2W|J9?5$-{Km5D z4Z>ohLa$@3QwZVvY;ciwIJwz?bs7pHia!qF;8g9hVVjab8U(MMqb(#2nf^u|FX>3b<>J!t z#xNYpdL}jXtUJ{UJu%=`Dm*;^`_8|8`JHt~WO=ETK(iaA6;;W{Ya{+CrNUEdi5TYf zZAr-o$wX3hGdWfDxtgjL+8!(xqt(U2dt+I8qXI2|Jr4emL78bNJ!4TRx9*$gaB2jh;g@|{AHwx zqsOZc?MP^0hJPJV{)Jx%Z8ZxvlVcHAgjZ-?$G1+M7wP14j|yXaT&j%PJ+RkDdz*6x zg3tQjU*<%ZMo5k*whSz(#(#CxqW*bA@~p%l}3alf9y ztfR5)GZiGSP7Ic&EDgG)GA9(oLwr0q$=`eo;a?gmepUSktrc}$hwo2{vL1ty5d*FL zv)VOJ;uY*Wd*d?5eLdeER%e-QR)!5|Y7|-<+YY&qaN}KVneZYYn}v2@h)s7>4sK0{ znQdtqIT=G5m@V>maxXK5l}`!C)TLWjx6fq`Y1>)T8!Nj9XIe4W*N%ZKt|>=!jFp~2 zEgaV319+i`0qwGnqb2=|y=~*uQ@`0cJSe>p1*IlA6A;}f)WAlmAH6iQ-Om-bfk}2J zcDm%NDfJa7Xnvglv+rZ8>Qex>cHhr8zH#MBHs*kUiDNH!EnD%_!BHj+sP&BdGmj#z zHC@^v3*65e3=>GURa9XsFjb3uZ7n);rV{ZxsNr)6DvtGM$u+MZ+@`$+BSu<;U6rF( za!9rkp@Y)RUsn&Uilek-b`D~Cyu^&$Zmd=Fh>90RT6_ATmU&Xo=_0l5Ym_!YQ}XA# z0wdQMN^b~X#!t1z1946rFcnS~G=h`WNQRj+8?1BM#!n#d8VfoGg4l{GaIkd`(9`!? zmk#LG?)K#0vy)h7fxzeF>d9152reqa)j|!TD(w(6;tx`}Dy^)sB8E>}M3Xn$A_9c7wmV~*;-#|lulX()_ zj?dzpC@2sZyC1z-ah>25N0jZn?=oa3AZxu=(>y8UOE&NwQBzw7ak0*AFsT!kWe zO)m6;I%3ShAEfDaxodFIfwM)ZV5W)hm`rF~eMphu|It`cvn~N}T2rJKUFl@^-`~`S z_j1HD10tTChKc&5=j(-)*@tTp8Sxq><;kEjkf6Mhrb-<)zsocKyaM@kC2j9zQ?)fu z6jYm_)zyWNQJ0nnTsv>$i5yo04=4Bf?-5k)d|w!i$NK;hO0>hPjkezzV74e6du z79RZUPSGs4P2%ZT+%Q>f8Ag$aY6nY#0fc4N-eq|-y6W22*e6v0CuBUhzKgj&yfC^e z+BdMV#bk0kHd&=HtL>jZT2Mp(tt_EL;hR2G1>q_o|8fG;yUOuNEs?8mJ^Of0rwdWq zcq_u$*|ZQV-nfy_`xlDY_sYnK9g~3-?j{yW6s*v{-W3~VLFo6M6@%(mtpam<Px-1T<`e=P#_C&s2PESIHIO_3TkReFbl1*h5*^>tZ}qB1VR%oV;@ zI@K(FjaWe$3YV+L_`=*8tiVSv)G~-PvlOp;l$Wn+lC>Tx_ElZC$Fk^y(ud}Sj zPlu1}m&oZOa!NCnB^i@TR67wjJ|k|3I)?;iwHyDjL31YZS542&eG_+nF!lWL)!py+ zI`lkw^}%7-%Ny}+cXCZoRbqiI3H8*x=ohcQg>BTxNxA%eX5zKmH-4pW-YhZ*$8plK z84pXg#=blqK}%~<)4uFivehlvEhp74OOPe5R8yTSXC2yjnmgOYmUxN}>oAK-q!&1irRj zLh_6+*;>bhaR_lB^8HseE*Ja7$d(~g7~$NF>omS^sbIomXvNuUFfA;wWqc^+`{;ne zz920zv6k?fE58<>K!-9`4{wLwefGSwdnHL5)_aOQRR2*2Bx>&qk1P^{SCYa}NFvTf z+E&bc_O~0=eX`Nzl4>*w{?$qZ>I^VdLy-hX@Yl$JO4TEx>%hvJ`3?;eR)cK#MW2_ALNIC5#Y z%R0$~=g8t#RtaarpUNd0Y5aT0Fk2(RLG6*5`uL87hax>%x=#Ph0rBXCp~xDC@UOaZ zlGulA8RO%sM7GW6wAA~4FuZ0wU+1U-FQ)d?yp7+zpie8Wt%KqFGve4sEUg6I*mxmt z^bMGv9kg`uxQ?5S#PW*s%7Q9SHJton)-J4Hn*LL+ULba}g-iVS==8ADBHC*(c~aT@ zHVxWE-p-$dI#!YqJ}YT7H_; zBdp{{qW6(T68&nWZWG~c{7ib_2O`s|_)Ky!3t4QCOl7ewA7o}`zJ7r#S5wsxa5qsNy$F)GE3cau&DPbb1(8pdVqmj zZcp_eYfzqFNEdlq#Pgb@9cFp9WqQQ4#gFcm1J{tE8Qa3r&%;aJ4EXSMq)yMaBZOT( zc*&b_xFwfQ<+xuJAT65{(|c&AjZQebC+c2Wm+vC! zA*cAc!wuamM#sFRDJ}0!`E`6JjXz@?mem-I+*ff*dXI;Eph0E2d=+-p zHQ(){O;dZM$=JA%%)PO|uZhs$!dha*e5QX%T^u)a2U8GtDTy)D*w@g>av^0S3RdPU z3L~vEOrD|%Mz*lxi!hy8Rl79i;#_n~X%83gE1F54IeMqrtLRH3*MLSsVypB+Esz(5 zvHQps46njKJfR=@6a;I*wKLhgZ54m{(D)FHhV)l=S9JEW6KH#V2&oh^d%=F&->{{pTB1MWttCo51VO;7!PoJaiIRdwgXjzhl$ipdWsRE7@Q zZ>ZHucN9vY0_RtBJ}LXb>2(^nX;+Ll>sKaeP|0pzhG##t3#4KkJ?qTAKka>RGgZ%N zlGd~Zwa-XPm-IK9;yLS6q38Px)N{^Xd$9Rz39nKU^_+m9&``b%;xMh zTzb&)J5%wCEyhrThXab-NFVgu*JZ}j7>xE+hL`t`z~Fe5NqbMV+r~Eiaa&OQL?*la zcuRU(d?FFEP64V{+F$f9wS4}!EbguM%c6XI6sP8!c6$F#>l{6w^x*Da*TRD`*JFQO zci?S78~?htF72WH^4Gm9pK|on|GM}1dHlcf4~?FBW2;K!l5F`_!ZH^*E)j4Pl9h3@ zD50ZnpeV-*wY9FylDY903k8b)WG@z8!3*~*G111*p;li96^T%%RjXYvNs5}1mRps~gS^}iG7k;8?WLZ%(7}sg8 z4cg8oyP+2jQ1$4R6hXtKjh6E?;n-_msl|8Y_9WABZM~T8^B+mS`eIq{k#Z#BYwrAF@MIVXQc7l7#jeQ#Cf4dAnv% zrp3ym}>zoKMTxbXXiiB>4nVF zmB>KE-a2<79A2jmBQsp7NAL>dT?R@q0hhfc0>tox!b*11*mBJigzz7@&fdjVOCrxiqHZdQEZq-@vuEYWl+2&k?UeZQ}3tBBJ9sbz1bEIr*1SFkZUmCIJ|P~ zkS)!RsQz@n3W+B zdSRjUu|-AE4`8(U)fT$hmHIc4v-{Tdq-G=T7B~NWDlK2tW!F+AE36}IWd=P|;%2;& zffv-|oM8>rfaw>!46~75us4Yu9Pg$j@pQtVwMwa{QYEPV6m@AV_!KOhm zPT$_y$^?=>2R(!;YKf(b@d`>QrOrCV0pWdFl_S_1+ zLwoX6U#9XpA;_BQ4NpT-Jh)=U^Hli4ZTI@%}7FIbEuKbBczckp(bjb#eaUEFp| zuejPexf*XBN>(wy5Wy;w>A+eNWOTo*rX_?o_q?zt@^94ga1w%r-6WnHiY0|fUt1sQ=Dps+t3(!ZWjjlSc z9K0w#)77OP!MPaOadw0pHLVIzOX?qKdr7I9l~z;JZkQ&=2(e5{$$g8bt}zSFtiCY%Pt=bc2{GhB7e$N>8p3 zenQby-mGfS-tCY)2#FMOATLC#HZ0GVHOuqmNuZ3?q_{czGu+s%S|_8z*FfFuU?m*^ zTRB-v_XSFO_upV5Nao!iGIM4H;EHnNCGYDp1 zCEb45zt!K1hpSc^9M`-hO7dLe``yf9V*<(=eNa8ZxSfpZg;DP{+Hr))w&DnGD<`5G z;2!jWdz>AI$bwJlnr9=j*+Y}^iU#c;%BAzcRsm&}2hK1)X^BHT)>4E9yc($IsNKEm zE}*t|rLyJf)EjkX8hf?vm5I8K(B$Q93cf|YVn_ig%ZXhVooGRk2pG%@ z_rDa^>>bs&4X znD2tvT!wDeq196M+PJ8iL|2~rz3u_;vkSQ<0-y@P!4sVWp%9huo_(8w#XMBc2xVatB&jx2D`9&ORct_NJLIQN~eT9UOkhUBV)fyke{2aE!BohX~JwT z*H?$^5Ib;T*_WYGAlkh@P*6R4c_YfwmFzl z7}0hUOTAPLI~>VfI|lT=8rPpgse-ZZ07X*xh!$6ZKd!0~5JiIL*9DAk;Omb;yhstl*K0324+n>!!M_{H3&H`M zO_%g4m8ZKRaWVG6bcSUco2#?HJQM z=X{A~S1z&77UMh5z6=D@NAI$EtZ3PxMUO0sK!C~17}kSNi~+_ z*odKQ({?tF6Ck57Mg)^r;}5!R-6QouPjq+WsIS#YlSe8JR*K6rN&fE8(q>{d@p75+ zrI2$md=@_%7e11U>9BGdA3u}EgwH1XB*RVi=#)zK4$!H2M*#JOHVbgmX`~+I{lCF2&8BkFr$^Dz7z1U;J3(F}&ZS z1c7ivH!9m=x(IMQp>n?&PC_Z<(IyO>xIyD6N!>IGkFJw1>L%jh7e5WNCZ{jHgvR@m z_$u3OI(|or{}!`lqCptVw4swvNGkBCuK>!w9NNt$ zs%h-v8V`A`!gBg+M9_M2*@7l_44nsngHlewe9!5ZBabmtIMW6vl>6n;$LUj!8N_gs* z;Z8if{Si$Ef^Y0$NL#Ld8(%e{lC(8>c$dx)UC2w7^5(8{zO#cp)jqzw=CmsMw&x~H zulqH_!pt1ibMkfdh$Oi z%C4Xzj5tgK-qKH;?&87OrZk(cSfSa&eeuXk1Jj7Y_JiPm?S z?Xi3G*Slrm29)&GAe%$*2x_OEsK)P?a-A3AidR;&&lQ>!*2Gd*X;f*`rf?o?g-xr z(NIEhq8>DUOPwqRcYPwUZ}#+A%WOmgRo>|6@4jX8M_FyFuzmcB6hY0{E2~`!=z1x6 z+-GkU(TRWXhcf!F+GInHe}$R1-D$#8fI9tHRa`ERbb-^8kiT5O&A&Oxlb%z>3F6NG z_m2z43Y+))Z}(Tdir+R&`pCH3Mha6A4ff{097^N!`M0ZnJ?uU!@y8q!n?13el@@Mb zS<5UEsr(6wP(`=)-(%&nZ#eEoZL9eA<7(|=K!9s+*e}#I-#B$1GN3Ds#eY?PQGSZ~JRCGl8iJgN_1Y0Jh1(`lHqbRD#PfqWrC`Nn6 z0uxnI{bCdUmf@WBrA*uki&fcdrGfFyY_@_A{?`e^0X`}GPoXe=SoEjWW#ZwDkfD1g zz@8X>bxZXlSWaznn7vzMPx~UzFxnjbO>bmyu?f@n-c%l!+@2~ZY{!Kz0!wcf2~Q@(DN z%dlqgDfcYgOe9%89b@8$O|4nS^(Gm*ZY!&Dm2N(uwU0upYH)hx?${nMJKz<54cOT? ze^3I_#>NyJWU5NSgrbB!Y8_sen!Hr{oug(Ts9xF%1xL{ZPyWa zZL77}WwID^Q>!e*JVlt6gKj{{if=hctzEu)Fw1!Fr~GhFRdW3965}+={vvY{F|Wms zF4@~Rf0|_}>1wgR;;C8^4Gl!(x11+H>4@wh6)Pas|GLSeDN{~`-wWWoux4#bI==ev`oQaP7)es)M#tUJO1B3IN7sssOj)F!|n|n!X?wTnP57i z$~FPWr26`1Kah0%Ds9$f;oJgLg2!r5T5)#|J>hDnaO8z@p1zu1sq=MKE+g7!9+j9> z4*}cqtqxs;I3OQt>JONDKM-apPYQwI#%ywLX0Q*_Yfxd9?fy`8VJFTpV5Xq0iMg*` zKDvUZHNa&^IPD`fm7_7d(9zXF~8a%QZ%>gKAuYCucPtM5`Hx zbur?Q#MoSs_+~jN^SVaks%WnG-%Z5`AwqTJ$X$}EoB5ukrm8&^@x2z?C{H^<|rsV35IOyhL} ztB(&Yr@mer0j*v3(^mW+Nq7bC>lEhZZqt0^3g;SNeJ?vaKql_{6P~6&|MTA`v;RjB z@Ba?YQ_p7s7`2LvRs}DKtI=s(Dt5X3QQy=$Tk?YH(k1nQlbynFr_KCbU%SY8pue!> z`^%mxka`|;%XYQquQwiL!y8L?A9u~tyRZypN6GWEHgJFpLNL#*{wr(mq!};D73l1k zqOU6b&eeUtG$#X{Kl6RX3ESmo-&m+!Zw0^o%*1T?s{((%CLy&9Gyj|ANPAMg^zxfx z*r$CGp@$2%`Yt!RRb%0MQjUE!e!-d?9Ohl0Q;odhR+D`85@C#B0l-9lK(OqKDW$Fj zPN4mJSLxF%r`pQ?TRG)hChV7{FMJUX-ub#+^(^l=lT)Bk%snq7hYjw~s&-Z>)hpYc zK6Ia~J=?AzC5}YvuX+B}m>*ou0wy*ZoY^GqWx=qKTg>{%ePRr&9dWSo!j!9^r!9?X zpIXVN3RFiN=F|q(ZkX02+Oa{dgtKi8wwQ5Gv9OW~zF#qvlq~fcL4G#nft34O7Ty}wyG&2GW4SkhX0?%gRh^X6cvs;UGUH{S6MG2gq?pCgMzDyz<( zCHKqd^J5hSJ%I;h@MM)qpyx^ANoqV1-2@dKd%qS*k1K0=eVJf?m1_Fm^| zEZ)NzJC7IHcyN;*W}T@Z!q`BST!Lc!EEv6e$MY$}hv@kmvsnj#h5RK-4`hN>ke9l; zv+_(Gpr9=U9d;zq&MnEW+AT#rLl%_lVdJ-siFp!>Emj6`TR7eDWICwmbekbQd)Sao%J=UFOH}Q!3v$W*oxS~*N-;kcGHa7dPOC+ahnm{9H@6D89y=?pXl1Thksp?XcY&I$RGcvL2ZlO2QOW|2DL9G2zTa|x+=7XKBZJpM_ zu`X2piQ0_Q!-?M6rX_yP6aAr`yz!G$&83#Ks&QLf`oVts^p0brqi#458%^A#4emlY z)C6Bcq**VU@UgQR%+YAL4Hmqz6_Z968(rXl>YdW#6Jsk-EtTKkFaL(#%9WQRUKA!hT9mrRlryD|LeLsQv=|E7tL;H zThu0VMS9|Inp~_ICU5hKKG07r@ZB~Lf^!`L6A`&Vy$oW(XrRnF+`h~CXi`I5z{g<= z{iIAbp59{w(4%O0VzczyqHTZo*>WzctuJBj7-2=Bg8PPRf|lQr@>b84nNn~8;+s92 zZp>Z|@Eoo9J1Tjdt4YB>v{Rjnd)N(R#Y(03og8(5=vcNXAuh!Eov@c=SD~Ky6QG&# z&WOtb_Efd;#gz$?bC99O$`arVV1P(Eh>rf(Ec;tkr~2bxiU}OZpYf4!KQ-9 z4A3<&sablh)VpFH1(W~nM}3_Z-E|4E73R%LotL^8iMlST5#1mB#Noll29=}fQgol) zcfm><9w|Jd7g%fkWZU_d(L+6tv}zVu^1y>6)QDVmDx2-C_95|&1*QOmoyIQYzkhTm z9x~e^rkj)k>;;VAGmiOQOF9lnM*ki>A4`}=?AbGIIFh;SK1J=(ptP5~=dYm3FZ?9^ zXb+an7Bw;UwWXZx22bV>uz@yl)siRn2s=xMyNu&>K*-D+PYk}j!UMC667z(Y?yXwVkYA*K0fb+;Mj3ZzKjk1AwK+bI6c%7prR)o}8|pB1UG@oHdjf;~f6y`c z|B=`OvTUK(5D4DM+g7}emL<{5Km0hv7)@tXr62Wh9QKwCoJ?)C z*;-dYUrh_4`xe}LaGGTUHEDNzV{wSz-YMNLj#wIAO2JjCV!%O;+j;)m(%Q_{Wo^4? zrm9)|$#0Gf?Pw%_}`@Pd)Xq^P4x#hZQ`rOc2uGnAv zMQrUFUvP^#}MwR=wNV$seZuC{i6kKc};F^`rd{{uu*xkn2GA zLKTt(uHJxhbj&hh6w?-OK<}4(-v&0*E!4 zkBe^&7XlBK{$x4vK>-2pOfo(z$?A0{cW*4uKX;ybKMc~uae}UQCdGa6>{UY zYm5o1tGdBV&cc}t-EpjB-R`8A+Z+a+N#k~BdW*~vaEo@;RlK_9J-2TDG@vJc(*P0G z8tQrG&NgAC3{3prxNy?3niTn z{#s_iz6oz8Iv*RJ!wLbIl9+|O9_wK0?|<-9Q)%2&d1rsa9~A#@^=jNpatLm6}L<+ue>yTV%{N8*4|yUAy8n_9s@ZvG{M6so~g4w3Fcik6}T~z zeZ$QZpNe%!exTl#WSC{UurJ^eGu2F-1e0huY@I3%u(v5CzP2~wj5!#NFZxJYEb`kO zbc*+OT`=qBgLWcvcM#aj@3wG86<0ey5^dDISwA01Ui9M4)92cfQx7%HN1?C(Vh5n3 zY)$H8?-@@AJ59H4&SJM*7rPFD`jnl*C5ZZM?8|VzZxHL9PcG`Jwn-9tjrS(1mx-K;v(*1N;2Ur(HODT}fI zu&Tr8<2&jgh)VHdL`MXK8?3t?=%^Tt!T&AK%FN1dSMWGW6pZa^TjcR&S9!4hs)byd z7*&xPqW?Owntaic8v#QN&U`-&?RozRq(CD{1cwvDyo8^ImRj7&Ea3+V{~5P9#at*) z=wbkXRF$G1tuNQD_pv{GYv!MTdO%qD&2R(e16@S0`7FAhcNBjGO^Y;EH-&U*C5Itz zR9D9P+t)II+$zu#ICL3|;5;yznmm&FeDF0e=q?mZNTbfT_T$G462>Nm_ zE-by;K%rHjA5~h{P=V5#y^v@u_fAQ;YD!i%B06&!=3l*#JYuQOpQocB(Z8^Xc!8m9 z*_T^#!OPZ~&3SkQz2z%Vf(wkE_rd9(fm%jZG^dq-r$z_(pT776+GVXH00Bxp=~MGO_KWeYy8S~dHP7BVu-z>veliE9N5FCvDx@{RSBVW9 z`xGz9Z32@vN9?A#A0)TSHyVP6TMWpx)n}Sqg3oYSfjD{l!bK|g&goP)?D6~1y$4?} z^`#*h3ZQ7sfV5Y#Ms>a%nuGeRYs7f$_8f^u5X(j?9fI-{6WzrsXl*&PB#(}!em6(V z!JJ8>NEB=1RnTe}eIiAn9DU&?@P-kLeW~unFDXOu^|U|ANiWICSk~=kk@C{~dea@$+_)Xv`d*S!-eKmR;taOC1c3EbV27+ptA+`8Y;oL0A@ z+umyHkuvTd9LQn%9(~uV{hz@Z;Vzp#HA)jJTS>*j^fs-;?yCI>GuW;${xJiRwYL<~ z2nntISy^b7jxy;RcIc;x)A-XwfyfS}vR##)0|o$BHRSy_#o6qBZU(2rufp`e%t0sg ztOrDJM{*hI?>fBSu%A?654ypo2&+Qaq0^u_X!1utW=V~=7P9ive~n@&=tuwbUaQAi zUR+iTq-vO*cQ6`%_~J|2vEzyr$cePxfxLH}A3VAk(D}-QulsV(^JHZH(3ir#hdcE)A z*U+BV29b8c?=p zXyh2^r=-+@jiC@L4&iAf4SkrH&mlgx4O16{zjsky z^A*#}W@KI=Q;O#2irB`@U~oQSD(0-|HIb7$2qu^Sf1W;En|LrwSnLz0d&vI4lt_xE5@mbyJ z7;)TV3KmU>_PaUFJEm%AjQ_6L7wJ1YLX&Ytk4!24vO68}Q@LiT;TR{OxIydzwtreRUW5@iI)TQQ@CeZd$`%<)5!c5FJyWma+jZ~cNFqvQ(~q4 zM;30U-jBV2wYKb>l9&>A1#aVbZpI@6C9$G80@|DqZJ(>^FhMkMR z^d{B*VVaI3*G(`+yQ+|aba)(lc7FZF%x4O=t*oDFojhpram=HzX|G%$*trg zDeWa9;Ap%gn!7e#w8ve07WLWwoBi?MyoF_#Yvr6!`~=3%88=+mg2*l-J{=yp%Wx>F z$ALqt$wV!T)36nrQCb%$V6n%gS<9fa80#B$=c= zD{X`oaBXfXbE6#=2lCIATsp4@;$W!$_oiHU^q=(t{nVaPR?Tz1_vA0+@qTU&hc|!B zF+v*iy@GQE!|Vz&X;`!MGFB=_o{5ULZCn>w_Y#uZLp~>NtbNddOo#Nqx6~*}_8*x? zi+Z$^?F!UxLv77P*{1wqhX2g}BqdrnZk!bSph;~Pk_OCdAHGi0YMmm3*#`7*t z>gsQ*R6x0?LVKMND${ZyX?7<0fjWpKQa4RuH&%dyYAo z#T^-0nS>zXB`e=QEqGdM2PO-Qq0;S%E!ZlhvVd&>{Ue3~&yKz6{NOJ=#)U<3`|p>S zht$3hdkNxu{Xk~^Br@s2_lrv(%>@wYDVY#@vSb~xd^k(pEH4cCHS~1C5~tuHP#N_{ zwZa;zR`i+%N{F7r2ZhZR8x1QTHo#<(JRAWXoQI7~Uu!G9D30@@_TkwuP|xg8A)QyV zs4c6mau2xn+;@qIS8mwUv3v3GT$8{Q#g)g0hxjrm6MojE(mwLiZT@ zbJn6A*xoHnzXgQZ9l`wdPC_e3>MFJ_iORjX56RrIFz=m7k5kwSwipXMN{UQK=NWr% z;W8CS-)ePfwFwp3^og6p1LzSf;BzqAM(Ej$LJVevb(J~gCI5i_WdI*x>(3No6zSZw z5qX1@XRa9?_vO(+U5LPV8GTZ>)z_s?CC(+h_*wX))@1*xX{2`4`tg0MLm$*ap;u%P z)*nuVC2fmBbJ70Pn+rA6y-2Lzj++jCwGYa>H$Z*qzesOi zo4BbQ{cD1mx;27X1V-8B->$WD|HXf}4rwER_A2)!qts{O%?psPUU}&K?(b8@#rnsf zgAts#%J;>feivO|kLx;-RE7sB7u)=7`fY;p&sVqDNvC)*L2MlLi{Cooi3wtntH?iu zdGNkGAsw^^)dPC{JLS)jW(mT1A50%HwV365XFPF2`(k}PrS+Igs`n>ikMF4TX5l+K ztGx*hQ1E&i^;Wv#S=hDc<<2LIaF=K->xSx)uxR$hE|lfip}mBRR_J=wVz=aS-3z|e za1&-DCeWrZJ8At|n=uq6g7rK2$~5~mrf!6VqYC+?6wL@6P%j=(ao!B~bIf@gXvN$( zDxXwEJ{qQuM?!c%QNQE@2Mzo^0EZ@zIjME}-mH&uo?byvZmIWkUAyDs(}5NY(C_D4 zjLnCZ2&;Mm-NRoi8LpPTr&x0Co(iqRq5PYK1L$h}%oPP%x(^pUiG;OEr(Uh)KA({c z1qo`OmLxcjMH0B*=`YQHDqR0u^uuu#w+|cV7hJtWd_lZ7Dfbfgf=Ip_GmHyj`hM7q z>bH+aYnf1_>8(K7IksNlJ%=I3ZMlMz08`z zC|p$#olbfVH)32+XFXoa@iKiC?>BUNqu;vWzL86yRSFa$%#f2O(ia#Z)47XJyOcxf z#nPjZzFONoiNUi|4=0>Q^6W>sf|u`O$jMWPQf5ddSb_SBuG;f)jLX~?;;*CX);ZDz z6Ya!OF+wkTQZn2gEHxo`@dlL%c7>$k=9{3D;AZekYxQtKSD;8L&C7b0ebGNm8?fs3 z7=0nMu?v#>rjWJ1i5Wvb65=_^^4Q&{di{*NIc#^R_Sm2;7vYpkWZvr))lMZgGj&FC zBbN#Rpq6H7Hz#t`)|)X!D3qYsr;x0_i^dFaz{t`<~~Fg70O)FQ>66Mr}CQvg+bDe%_5+$*AsJ~1)FCw zA3R9kR8W9M#t9ZS;MxB&05OVsOktqYch#%;q?)OfjqW7c{7SWfc2f^uY%5pUEt;Q* zJYD7o%i>b+MfvnaHx%YM21MFRMKj;t!jtZC?wM!n40Cr6O1%>+H`HovyZZNvfwF(0 zcHc>M0C$E(@a7L+pX&D_&yrd5O9%SJ3ccCA3b*sUGSlfc*-W3ECjBJ>B2Qcpv-?j(al?FU}iu1B2%_TG>KuF1`>Y5qz%RzBS>~l_54U(x(X%aKG7#$?CYRKgg7y?co#y zS=rGNp3cncW9FMmaaF2ELt|hcF$|jzcAt0ic_Zx7ViWNF z0T$0Bn{D0V&8vxl?QyidFRDr$O37$|u2(L07u<+phTQ+JIlay&OB!q54h5;MJ=V%3 zP_L_I2Ts|+Tz$mKtv&wba)sWMN+bAkf9xk9dZj1j%<^>;!q#hpA~^_h*;(=HX4gd?om^T15#bKlvp zj_O!U5EXeKkyN%j4xAgw@0+K~-UiH(c`N2bPKvWi*H1+2C06{wtkLU{H&{L^k*M~M0^9G$G>R@%Uy@1+Q~Jd_5|14PG*1;@d2%g$)6p9Cc*oR(Lc`+ zfJ2w3@C+c2WA^yJ47EP2%Qxp3(oLOGWS2zIA_e7e~EEK`5DUK0Pe% z=X6ObYuW?3v-`g^{3LBM10a_O{&3rXU>NbRo@I^FeR*>Peh`a-wBmnGeN$vd$37h?~=#%$ehpI~NSuqUz zW;OrVAtjE>>6Vyd{%Ly)Isjb@iyEc42Z+I>GOzi_a2@3Cs^hc96Wr5_!wW#So+{TW zs;s{4uD!ifu#kALse!>tJGw0pBL0DLt3BeH|GCx%>KmSa}4zT5Yrzer>=o;i- zK6OmUsl55j~Y00VohD4XOBw)AtKNb=G(#LZ+^LW=`7PY`3_wwB8{z?#o{ zgcf;k1K$b^n^j4S4%dYkK5i4?*Y(w0i~?JhLnj~a9eKHsLo8B0zUa$In}!Jrk*^rh zg}=Ym&WfwxjqGnWYhQq_b3~x=x{z+!nF=}*e+7BQGrMm3L-lGImw7rBb>6OYNdhgU zlS*HC=*|h|Z&c^q+;Zxs_4V9WD1z+EQMl}&`{fVH1z9wUdeZTha6;(zurDzFGnN=TwkDcx^+-P&UhwC z303QKsLVQaz$JyYpb?EmbQCc!XMr(&8T`ike zWG^$5)e(|@vY(dMs#yUtmOAAn8@PVf&p(yTY5XARrL&^(60vxk9Nd!eA_;(qEN98W z?jc=t<>W-50%T{9SHiRnraDEU&LVkrqjqB!i`IJ4wk{+?2FFpVoH z&cSX^%l4)UGIa?TnoUbXn!?D28B$atK~l@i07+F_SoVE2)cJdg@3QZf1G8Vl+I`h5@|ULG_@) z7i(dh2>LYSfF;518v-tG=rR~ zoEi{u!g=trP`JINRDa@gy#Y)X!D!+SjaWyo=|j!b|1fv19^AuDhr|f>N@lo?EMp2K z5pa1)%a$}&!%En8iNut$FVbF&2i#0E7`|I03ALTAlS4pT_@EG=KRZo$A`QAZV65 zj>{eV!+OvKjoAPPUslh&juje^4k#SfyF_M;`vK_*oC!7jy)fQp-Di8#RPrZOQJ}hZ zYaN=7MqJYH`6b&J4bg~J*1J-|8rd2rz!|XA7dMP0&Y&E0vhxg=5M>TQTWOfZFa7s2 z0I{EtqqoqmtCsW|u7SSoZdXE-f3@za4B5CIAzT0u=Tm`+ipBWzWYJ3LlraT{JNV)m zecQsKw3uRUHHlkH7kVKk$WVHFWyi{Nk=)Mj^pPy<%K)oH0FQ(4hl|s-f0hGb$Tci2 zv!v(2(qXN_3Zg04-Y19zd;0k@8a+!7JF(yJQO!a}G7l(0De`7ASvo+T|H zeB4LfEn6y%3W5v)D)`Z4g*w{x1M!2)EJ5t-FR`KH>yc z|Lqapr8|Xvjs;t9Qv2xMrxaeEqYz}L+`E&}^W=VHa9E{^pD`W%v5<@udyI{d$9}kz zR0-@7^gx86n`8&KUp*Q|#uA9K;h1_zZkk}Z0tv~_5HIK%37tPS9ex0T8F z&AwSHF^GfhmD`PJnGTuvpCz#{Bh2T2Z-X&6blPt~$5#3!wA4t7I4^_-BEHoODiO?y z>HAh+^_;%Btp0idh4q)K?$Of*q*4X|N4iWa`9mJY zK6`?hP^XLt(xZ>7j{5$8x9_;V@C7J&XtBmd?k)jaPb{n;SNUEe#EAcNjQ%ym4&_1` zi5_18`0Odl*}1HGg#ir9s@7jJ*stdXeX*FD z^iA8OuJ<0GJyON4$QYL!kVYVXWylXm0(1SbX^K@?UDXO9bA2a%v7mVH$YjU$mAW<> z24=$5eU#sKio|g*nYo&R8uEvek-M&y06QK)@_4lFOpT;Jq#$Ya*mHh6XF8zkT-s%< zp;^0ZNyNd6qUZZ5<|w$i+W=;MS0x@OyQqB7v@{TIf}DN+YQ^`OGW})p=l`U0)4B@? zIMWt+%%yWn3iT4U0j+c3QO(Gd*ZLJ>C!!ycPiEEmlrRIG;5&Gs)|3JXd&QtXFl3bf zfi6u1-sbny-ni6j))aOYT_m#D(bw$N6H}UGy<&}~m}29Sn532trvym86RH(pb*btb znH3s>cO~vv@j7MVdHMsBVEht5qBeV%62m+YjJL>Mb>$aLq;ZUYYaXo@45;hSG?b)3 zP>+%bii)z%wEBqDD^{32W7#;?=Cjv$R+*TMBXRlcP7e*ATCNENzf&nmN?d{ppi(P> z`_aMR3c}G*yz3(+JtibOObG&-8hcjL)RChU{R>9CE7lakYf6Ku)>pXXD!gGXSgiu9 zoOo^XOf^ki>oX3}!oLS@gMGhz+iQycv5@84UTA3FN1p;S<2VP%dXJ7CAkLgsGZY#7fiWrVoj#jy8((#YQnPsZ4g5$wV_ITR_8A9dr?n04^WeNk61GX9%M~E= zF%X6Y*DUq@2Og_t;lvww%raj{kA`3DQ4SvP;5^Uo6ZMBBWGbyE z!cIniZ}^1dbH66rhE_)J3VkE?8VK0q{t9brwNa$V;E_xJ_x0qAgWTBxhrHP`F)066Dqxt~J0%xHwNDI=W@Cl<`I~3Pg;+H}<>)dWQ59!0<59%VZZTVyyT0 z+xjyaxh$c$;kj#8ICFd0mUm77y-+H`>nh!L_gr=i5K0`sSnTDzpt~;<(|*|utaH^L z=d-kLY6{tAxtdZuub_VbtQX)7w|@nur~i+g!mF>grBqu=+8G7nu!)gF}fS*6Uc1heeXyQ{c$myn}iOnnk-Jq(z zN~9j2Z3Ui69?4&3Csn`a&=A$knD=}->LuuG@Pk4u4*Aywxt_V&3nrWQm{lZbDoe+X zXzJ<#87Sf}1E$3Y>nq!mCV`@&-6c+s*>N=~OwapiC^q*eX<3X>5I~uUjn|M?sO`n4 zX<~1n_)L_;9dx)|o6HKr@?E#x!&BEqLNKnEve1MLDNsxBhrtR93!SjlxC^lTox*7c z&!*4KGoRi%V9GS59FU<-soTRIZ;hnEvI3A_EraRY==6{jYt+sx#Ar%iii350rh-D~hg}7r@MX(>rbzPse$tx5b-sJ2`$h*N2tj zcGdMfg@Pk+Nbp4Aldv|8)Pm`V(Wwz4*N<1|7TD6PZA06MNIpv%_E7atjj&26!FhRihkXcW%?jj>G=_bvGQwB@917>ue{u(Iv@7 z7$-@XYHq#CuVD1hhk>HQcCjQw zYz~mD@w`?y*Sfh58^7h=VR}EEFzCU_%<(+nu?IXZsVpA3Ny*w-dF zAjs{6D4Z2HZ$C0%AjbH4#uQ!;d9ZN9ydM9-IM*>im9i9wz;(P{vmIWjSBx5~ZT$k2 zcP`b2;irj_48f|d3e+Uj@!ER(U>Q)N1K{+^({m#k2bhI?`v3#rQ*S^r0n>7kz4jx? z+eG|rnwF^^Y`4_dopwLFW5SU+d*DSpgpKHaOHj8`!`%HtOybKbHuuDCeFqR4nMMDg zQuXY&8MmCE?7$RdvI(W>Lqz*gk3Ckn#BL)qig6JlMfjNSf#O}k<(+cP0%I61CElRd z3rn_($(DX0CUt^X;3$+8D|X~*I&8oHF2B@yZ}t|b)&V_wTd?#lClY@VPJJ<~Fz?%8 z?$AIzDe!q}jcP&63VqC%lGNH82C5F9ktzpTd;sM;|HKw|nLqr=GwB>GO90QPT>{EM z>nHR60l#BJbj@Vz7quJr-2%O?2TxkRZ`N3p^UF|7xU?=;l0R}e6PniE)t%|Voc48C zKYk?;V!8SZOxEEs+Q~5zM92``fi0)|%{Y+f(O^?0%j*l=TXlq4_zvOnG}o8{^ak9s zwA(+`a-bNoOEPbB+H(~58o5(bpg7jHpCSUiz7!yTG)n?lVyYla$Zbutk8IFbtu=IP z&->zmQ)cwq@#L7R)|QPa)immx&i3RBNk94)hgLneyITar1UDE?&z`k6&v}-BQaA4Vel8v`s`~?>e)iT!8j^EgBA5#>m?S9cx|H}3( zx2xXwa;f*Tt=D%7>wGk039S9dBKSrsBQqy5dDU~lsVL1Ozc%V>UVI51_Lx+2C~9ZY z$J!cu7Z_Y-mXbY0lfz;+!Fr;0Uwz+TjRU-;D&a=?0ol3Cux9ymj z-P?Dej4``hJODZ$$$JF|?>25n?&8HhYNoBoNZ_4?7*5YW=v+;c#F7u%*$? z1QTva^^u&F6d~#5`)hR{?Y=xZu%F@*?OABsJM%$HE04Gj$`2K-dj23s9bn}~q~-ik ze4$XsS5q(p=u`pg;gKY;<(j>8K=}IB+#5=JLk{6%Yfqal6Pmt23Ma?C$?vA)$lk>W zHZ&MgDYz~G+=2Zzr^bH@lsTFZd-s`D@DQI2k(un$CQWr`$1uENKT98$4mw#CONvES z!Sf)vHQ4nU0gD?f&HF6+*M8PGd^I_(HhMr&G6dk4Hru9{TOHnRX659JZP7i5-Li$! zQX}(Z*;vES%+}G9XVuSmLaHSsp31kwF#2;P8D`53QfB8Jv)QVwnx!pie4Yge4tA)I4+~V z@KTgd3XII8=k8_oY(i-?CH`{OxRn{k=N09t?2lfAVN%j@AR0S_{KKhxWqLta)3x~UO?&5g5y1U_}>Oq%FViFR=BEU0MRVF;us zfD}-T=P!&?bn}d~esMDzAd>n+Q&o{Z)fz9`M{hT=k!sT)0dR(wK6|8oc6??9Ej53r zk_UXb&Td}%AWVs#FXq4x0{r2Lm{foNbn&KbUw{hx#NJL}7pewmNzzWca4VB37Xu2>9`ard`X|e@ADeHfXloP$0f`B=tL3Utjhyo0p5KP65o( z{7p|xxPP*_d#KBS$zt=BM9uS;%L6rU{0_iq0ju|(jSGTIiTLBoa)n2>Xo4-xbw~dl zq%o~EU-G|OE&BgP^})YmLEnESie`HL->yIVpGBq_#sAp5|I1~R|1*vMGmZaajsNeG zKL5{}{m+{HpVus)ghB1<()7@ZsQVhZI?ew8@qUdS#y_R9H#|W7tK{~Fk52z8Kyn-u zi2tV=_ucQ28SH;*ll*M4@Na$N?OGG+fEcV6fmzjPWtbgW^|k$*{({$fTIIp(OAj1( z&kDAEc73k~d07z^Q(GlD6!@Z3s9waV_l+27{C0Y?05N?E<7Q43ZmMqkJ`Ox&j8kCbMqy47R#)JFLM%5@)x+dFjM} zl*oVo4(rmEd9NZmp5;jkEIS089|MS1_BY8NwdUrO`?`^qrmI11b#LiM_5{nKH3-P- zkD5acLNeG|!nX|ESCMYFSXo_yJjy62lyWwI=5E4QC57Ol09JAafVuuU=niD80r3pJ zc@$f7?v|1xoGJWJhAWZi2 ziT~B!dB!!Bb^kufC}RUV3J8`_L;|QZsj-Y8U3w=n2neA_?}-B{3PKblbWjkG-lT>A zf`Ax`fCM2xR0tu2BvL{kjeCOnf1YRVoBQH^KDXxOf#mGH_S$Rhv-UpU-*wKg4)a?c zGu`a(+r#(4@2G;;rMi0$wb-dI&Kdb@0@^sn{#Z>1BodMdE1OP!Om0v^gcH^qa>@Y? zb&KZdo=<515q6z~achsP!pBIN&nN_Z@2*;+_q&d)S-;SqS})=CgDQM(tYqf|+KfR^ z`obkES_8N(lGDUo%>34Nf1cF$iy`5E`HN{5;kAKs6p89I)qcc zb)m;D#)nq4LQLf4N?hS1*`zSiAf_RlM2rVM5ehd22q z_CMZ4lQUcd(LUGlOE#q&W?VNFE%~z{8F?jAP1;s;OZqZWjIuGA#^So8W=Fj0zQ>gH zf6^^1z7P@8YI)O`v*4}KkF1*t6%Q86D^-spx~|(Q z`O11jBK8{xXLr_2W`&t-03ttSL1S#+3fQ|v9Ap$>HMnxJkI}G1iw~(xMlWQPg?wKQ zyTZq|_a`~7VthLBOJl7O=1?YLzNxGjC%8XYf&}L&N<=(OZa?5lRY9t2kJCLTye38g z4H;uyhuc8!y5BFA5uha%>})UZ3AZ5-NXk(6&nxCDiW18DD>+k?#jn~HgFKIUv@HG< zjj%!LH;teOtB9UvU)4BrXS4eDQ@&{XSuDYhdhVJ6A1DjQ zkgi4C3mk&cLL8(t3X4~p;Du)?l)Fq{05=TqlTY(F(NWUw+1=CXkWy{}EJx^Kby#I} zGr+vXg$)FhLhaBWTNW$lI^w@^M}~atv?BM?N9F9!frm1X7+N=&?~|FE^dQ%J)f_9N z8hmPH&Wb@9nN=Ks;wpZN3_v^nhFGht{M}omY&|iovc29K$z}q>qJ-|tmU=VsSO=g2 zMMsEJ)tMw^_OKB?vBF3#!B1_{o1p+8 z+f6m~MH1t1Z$FG?RY<&>6eg+7nh#JK5Pj{9#{2B&ZUTxv%h#gpi<$xC`gk>jV@Ns( z?!$i`y-b;!2x6zjHWD;xsb}()#+$o&6=QcMDk$qdlFxzoKU_9LdI^1mgx8A-OZos& zEh%W2CrwamzLv3R~isf zn$^w4TBBRC&)7iQiqVWR9!dlmm0RjQML-igMGZa6WAt>Hd9Lg(SsprJLq!c6bdbgS{gz&`v*+MaqYWH zs4^?EsE!&ty1c$z)?g)5CiShWgToTXY;t-^AQg@{llFQ>!~#Fnx9!*@pC{CSp^pR+ z{oLlSi$1RN_3Df833hSK$|Rq`ZD{|vXv63G0qr{6vY^Vhw*cx}9TB!yAL5;};6K1+ zcd+Bhb|}B)JgZ1RSB+QQl5k+s#f!kMg96fRb#O#&t(Fyh92UR@81&weX^(|zok^Te z7YPgR@(A`N-VJq>caILoESeC6 z)%Ygd+{bEG?7oSr*A}-j1n60k#=(=TmKvPUygOwcS?`zF@qdMX1HfFFl7TJ8>nuPD zp$~w0x2PW*jvt({n!+3&_2`^WUYmn9=~R=9cBiXvab0dY06~i(nNJpI!Gu+LYz-M& zJ_09b<>l4`L?LY_I1%6gDH^>WLrlg3XA2O#2q|1fe#l$pCVntZW#7y7{zw?x@U3R7 z$EOxe8ZmhA*2N=5a%Kdm)n9Hk~?qbma-3`~Tyva12ml8>F<7-y;gHfs@@~13@ zgW21em1ZA-=ftw8hLBH@F?%*(4lrN04UAKqh4*ixZl}I#Cbg_*?Jv#C7y6J zK1HSvU-WLSFTNfBK63=Wx#;dk0U;>o{(zhRaD^g68e0#X)V2||(so|NM!B>AFlF4u zq^hNqD3|_uD&LhrZv4GAWdhhPfM_#&ak{G6;f+CuRh)05$vFVsLr$M& z|FXehz->8~A}>g$i00EfVX%=LFQJ>LicSN6(L^hfv-1Ekl+WzxswQN|*Op`ZXMZ-) zgx^F%?}O_P(yZ})aOKN3EsY2BYOy6#wh2@kR(;0DM~z03SN~-`K9CJ^ModzfL}~%{ zL8;1ct;@i^anwu}J6xV#DJEm4=}rIDC_W*Jb19SI21vX#Y#1T& z|M8RZldfQlNs$PKke$Frkedx`;O~A~eUl-!si#$x+|0*1_mQpApA&UjHnse|7;Nq= z@=wiQ;Oe<}oL1rLB~^3Y{~Ya2eq3J=xOF5Io_`Tw0suoNjYUTM$k;yseVzmfAA?rY z$BTIU1MG7s%57WZKU>!UfR|MYpsJ++$=u6}fBur>1MYNX(&wMb695zpuev7e-+uDq zjEm>$gI3}o~fOjOgw<42tMrG zsO*CE1Ggvu8{WH!{>ewUovvTKZvU5G^LhHlpS}PiN!paaLtD#jQirP$SNl)e z?ElK9&TJ5-j=iaSHXABV2(Dxc1#!1G`2;F9re5BkdRlGa`0u4C)g#q!PCT*N9rEtu zm1A=k()Jp(J#vjd@_YNoz(0q;+KUb&NYexppM{G6sm zSg|?r9J#8DE+}yWpC2ZHrLnBIE1%3j_mXvf=*|Oi$U5}?kHgMDNcz|1FSeoV)iTca z4&f^Tv6rodW>%Q3yInXH=%r-{HztA=gRt{puS0qML-nFSCdPhWoYqmjbm^&2%er=5 zeSONfc}}s9(soc=pHAPF7wQm5irYYT&m=n7#mlSj^C#vfWe>YA7k|h8EpC3p>(n1e zWu@&~{XuQHMQtxEOO9T9-~wD|-vw$Id#wtJ$Q!(~w2>z*ZtQ`Sgk(1#ejimS{@#fb zM-c0Pi5bRk1KqoBs<4oIBC4#QYb&Us5%|vYq@)AuOIZ1Mr;zjl$CA}8udV}~xO>Po znQbWBvlY~K(&52TINYqhTL9!O=OJ{jqvM)02%c83y6x2!;6HEeEb{^sJr7HZN`Nz{ zye%NCGdtA>jkq;ZH1?(7qq?#4L)D?_xSf-+iXCom$Hl&a^?*_kria~p+4)y@2d6K>c-)xK|aUH`h(UF>ElD6$DS{7 z8SW-%i^QcM2ivTc-H`B%u8;C86=Zd0{~f8Tuy7h;3n~_=V09t1551D=q_=ps@28L6X1b@hDy$8E03|_qxN#L z(O+k(B~smeo>1VGOQlq_u7+*t<&UAk-;9%ZXe&p49WUv4w6$=&I(|yUWf|27BKI{q zB;&?18$(FPic$)l!adOz&oC;Bi|S6QAaMUubo6;Ut}xVVTdhmqSW*jCaSG?M z{37S80I?IW@Lh{BwOKZb0C8F$xn`Yhc)@8M_^i-Fjo3ta1R0}uW<%M_*`rwaxR8_T z(`e~RM{~0)>p<0AZE?`8xh z1B+TNj!0r2n&*%C!@e3&^w`I32XL=eI3XAG(G7uWb5bWRo#6MuMtwFMMc4oa&5$vj ztAtP}J2nDIuTOiUfJ!|p6HGi)k z%@;jjFB5I#C0eYmMDCvm(t2vyNk}UMnh6l(VaS!WUj1)b{H z$ds~&_bF+o7%ICSVRvA+*!$NZ*MZAwaN6jegi-7dyM~Sv&6nPwgXv+IEj9dM&2?g& zOqV`uWl)1naan0Pd@wCs+V$NYN6P{{UB}xI;{INf9`0^*xyxt%+Zx^{*flDUe^YV% z+rO#2X_txo!7X3FrGXv< zr-|l{*2t0%Ms73u@9r@pj2($q8yJ9jD`Z%R)ZgI8)za6yIDS7 zeOm~dFW)EVB5iD|;AXA$oaT&x29mY=t$+V{D`55%$!5Ug&u zO~fC13A~U<#@*CBj)Xmni!`&=iWo&>CxA$ z5bKor=)_X?bO1s!DNNl>L!lKvSQLi8Xwy z(>`hK#M~ew_T{jK){-U9cPkd2 zjA!aPRk4@FEeqdPR;il8C!Sqdh$;H2<{$A!2_uoEs5vuI@1;vW+el|ngRZvf?VD~r zKldp=^QNSQKb)AY^X}98kMueUFOzwyB1e^QRDT1lH-j0A-6YG@(Rw?-^sgN&XMatQTAs*$6!H~%x^*^t z_2c}rfI34c^5eC@jgHv?=cDo3B4ua_wZn@LR0x%PAg~YPjiN#CAMdBi7D>Lz$5V7k zcdd$DWGBvRm9pE{CLQP6Be_mM0?1yO;07Q?O-t4kL`UFtue^5m)-sp8b@h_CW%@2| z%)fxwMig^AA4WY4RrtQe-uS1JIh+AY&RwZAL(TI#t1zRK8~%m4$Mp5P!jc-bOD*!9 zOZN%5TX4K4ST~Dwhf9PpM*8!@wXD4ghY#T-TF=6qs7;<)?z(PE%K`ZqXj1H|Q`z$C zV+#B9OB50mCUerCX*Py80?F`u zbBrn87M^9aF{wsl-(B zj-{lo0PXci<|X$V209R$Vf_X1q+#46TGEW@1sZjGH!s{689y2lO|NBDPNtFFIF4cz zZu+`VzH#ADnBp=0>#`=wr%c_vE%F2ZicLy>Uw46?ms#&3^P0?jqQKZC)~e+zON%BM z(;A}F?OPhy;FBCQ<4jwpLqyCOZ1Qbp{8z&@ji;-1yqDd?c0ZRU{qK%!$pk1rEzw)c<;is$Uzn_@(IwG?_ zI+Xhk?g5>tixz-!MI=y2;@bL(We^Gf9{k4Fc%H|5qF?nSpZShHU>9|Ie!o-W^6LY( zxu<7donUgfnpu;C*JgWG&B6waUvGFU3?KDGvkHGZzzVmCbr3zykw}dOfkrcaT5qh~ zk1Lm-s`iykCoI=xU^1lbPknP!!^WVVW2m^@3MFj9dgLGR4v6mz?^mdK2+~e$s@p2< zW6WT`_gu%^uCHnb!Uad~w@_VskC})7d1|;4F{^pjn^T+<#|;^fdHizJWmxdYOM+ML zfN% z;EtMd#T9&Cd`)}T5F1zV;94$&ztKIj$w{h$V|pMmegK@=qrX0-Ply>Q^^I7dtT{=Cx3#qvqjK=<$50LX|D z=T6t#M5kxER;&T%)If>+(U@_{9L_dt%wP?4>ndKkd}{mtcHY(QoWvlB^YUP0KqgWB zlMwDij@9%sOxvQa-CZ3)HpBjh`%b`yQgG>PJo6G($YmzMGieLaUHdOD3#Td z^tRAgIj63tw`A*zb2Kt%=gnL&VP(vX?z=xZpOMOC z2bM9`JqU`>z8bH83?()4JMkKhh%RFYlwOZoNvHClG815K}2L`a~@;?2diyaw1xxYScmzRIAH zy+U!kEsdHc@dSHzC^Vyvq9}Bv(_@qkt8)bva8+F&irj(yJEL#>0>K%*@fz&Z2#@If zbQ0^E-&&{ldSCWi%6LGZcdII}P_Pt^UFdOdf8r7;-c$2JGt42~<}Xj4bz!xq%n;&2 z)?EHZLhP}P=a9Fa%U7f$CXcx{w);hbmuaSVb!`8%E|1_cM&GU_jsp)~#286IUrgUrgc?ukHPpZWX@* zc<3-{plq@80ntM`TfSxO& z47GFFl{}JoI#Uq91Ef~kHkh!g4lThgGo^hSYnR?^!A zHTregj$CBt_wTsMZ|#1>nVCN}TYMmuA3A{L0Yz5sJQN5VEukSofnAAC^I63%Dl?$LDTZ9g*T9{kYS_I#MnT<*5>QRuO1Y!3ZPw)a zZ^UBR2aXM98a?c*2G-?J`@(ErV9^b#+YD|#vS5*;;+DE&q`1XKZ{!(t{@iZ_h?YVdwkO!?Ss|Mmo{8`fHCC>;EnXfZ~M(Q@ZvVt-w| z@S!*J1BF9$b0gn9Y9ThOtIn*-<3-umT_%(p1j1D8TX%{fB924yDl6@U+ejm8-#YxR z_iZ1j`6Dh&R^h|5Z9i5A%C)cBnqLDGn)orAo@i%nmKoM5*O8mESG79u`8Ts5l+zXv zY;z9RA3;%+R;KdGz7YJ~cFHM%N3U^12c%3ySiEH&@uKUyLLdHT^AGZye{P8NB~rD2 z{qpvQB=>{BqHdC+vJozvo_H|D7}4c-yO$MHou9VD>;#LEs}PR4RFVCFtR%hvi_R0m zr|7iB{`x`Tv$3KZH`S0F?jK0E?~zM%IsH8&un3|{yAr$5d2A1l7P||3sJ7Q&JO(~* zh0B93p?6Ryf`Q1!K9;FlE2XRd9QYhK#TU976-LJ`kIu%C-!kon z{U7OJ@2Us?W9SZ}&?!b0u;FPcH$C95sFA@OyXyRQo8S!Z+Zk8e{TL_6^~1ut^^V!; zu%Xu~du+2D$k&xcTM0>tgczsN137|2xN?!cx3tW#!?~k>)K3O?yQJGtaT7h^m&UwT zX>R$i7mZV#?UEA1jV^|(olhLj8-F8TpS*|NV}1=1lz*5Lae6G$`O=QZPcU{q#jJW6Zs&6l|8QQwab3{4=WjYgxzus0H)p2lgX21Wzr;N(kki`Gh>KW9Mchy zANesg)H-hYeRJ1Xo946Oxb^X}*iYHYuSOa}J{piJPoUMG6&4VJCh`rUuf7^4;f+v-2=2iHV)-IkQyLcO9Z^fl5@{$KPGog3 zUQ{T#$+&9U|HGQ1s1jJ+G%5d!5)4K$9dmE0y{s;WsC~I@zGl=4s120osM)4ApP?kD zD`H@np4w@+MTw;EiF)>O^I2F@o$_r;s9n?4z_4m5cD9VH^{rfU_*0EE=FGYw28k9K zEe2No08WFOZnf?6tf<4sHHQEG+Q}KU3A@=@cf)hzT2Zc-f_rSQji{{+QxF!@lp&4z zCgAlq*(Y4NGhk#Gd)4dpmg_(3UCZe^dqwu~my3ClVlSg42;$NZ+7Tb>a{R!!sXQg; z&+d9gj3K$Z_w^~T;JwZ&e!eI8S<6YPo9a<3{sX7bkY%^P0@P!@u4y?n9z? z?ZHZE2@}^|pQw!?k{z6i>C~n-P0{!F{;9j+xF02;@@vVPvJ0!(;p|S=DX;QQo|sEUE~$6D(`;+6W-3~KfBC}B&WSOB%B0I9#a?I>J=6l7CuY&(?Yk4Y`cCgWKQ z#OEYb(<%d2(qtKFo#Z++^va~Mi+(gqPltM{VY?OG*C5UP(|t?{^1OuSyj>fSC&1kde&zJmPM{nK)I)oKz=1}D7YJ`x(5wJTrzi2< z5qPcuXyKo=(8*RMNn6wG-XXu+D%>}Ipaz_$euq%vJW3)s`=_d9xspC%-jEHoh^tzKm;|Id1{odL|?!^yEZTd_k9=*&augS5<_k zlUcE);aAqv&5~$3J^~=?EuX|e_khyWWg)}{_PbuMoR#y<&97>~IU%QASw$CKS526A z4`A+T+&MiK-p!H$S?*F21l}iuJO)(6=@}BXE5aYDzrKDN;h=}#LpGV+QhL@y2y|-0USbv;69|6A=)QqDUvS zkOT-JBGRO{kV1k=Ckde@KnR@Z^Z35M^X+^%AI|?=zkOX?%nrlsnR{l|UTfWVCgQf4 zk;HcC?II!~62>>K-W3rMT@ex4bYklk;U_=b;|RiE8+`8?T@fknI6Nu5+3bGV^sT(AyauS*dY5c0v2^G?DJmfGam15=67Yz4RJWO z%!&>@cm}#tlP}KvFfc!RFP6Y%fE++20s*3SXi<0yq;otIB)s2FGiz3eth9dr! zuSETC^CJH@aPH;n!nv!{(f^`&`YIe(jFT#~p)@WsA(RyqmqwDpt{8Dz3;c_02~*Z( z*3KYU;9gKqWpaPX9TIGUl;mR!CpM`PLgf|KR?%q>y!C=kyJ_9Ryx<#vsu zCUSD;-?_*<&l-B5sF&`#oe<%O66Q0z8-BwcL z8tG#frmu~ml0C+EmU&6!GbG=gxTf5--Y5el4_5iIYx&djdjBqg*O3J5T6`IoMQdfb zbK*w|A2f+qk=K;Gc>*7d2>R5j4_w%7cGJ+SoF;Gx(c7>ruak z>#+#4QGtsfd_frR)M=7di5%3?v9}u@FOv$HTcooa^BJ^E^pUb49v3skpdF6%chK?X z?f$bok9NW+uRS`c7IG*qW4xqM!O=D81DTvhliY{AcB)HGe2%YNJ#n1h>bUlcu;b&U z4WnA)`W_Y>R>I>1x@4HFD;2bdW~bQDpS~CfYVaOj+V0$PV+`V{wi*O??0)sbGS7^($hDW<^#= zQGUs#GF5fH+C7LIL+g~=$*zkb>;kxXX(I#QHm8L&KCrCR_THs%r#3wX`e9JfmopU$ z=?O4pEu^31q^Rt%_x-kg&Zf}1y?;t21`v&JZ)_W66BG~T^K@MHS_eU|aO6#raU_|} zdfW?>G!AK6dCSQf> zD~F0H%6B=lV1TSSx=t>0Mp+eFGZY%yPr+B#+|OYQoCR_e$a*?Bl@RcN*S+HmmMqg5 zck&DXmZP!r&#c`R+4uDD`u77AT#Mu44eiCANE9cozee&`A}znZ#+5x%ER{Y3WQ+J5 zJTVH(cj&On)8geY++#yZ>F~WiCX8}TlbYGX_jKk_gHpTxv27!b>HXZgZ~Iznm62A= z;IS&$>c|0Snr$Kco|^WFz`9e%$Qi25oc!Qd z>e@vv{@3jvg|6+99pUYJ;;WSkX62~@#%(x|JSA+d{*A@Py$PPZ87JHRF~!?ER;r-1 zmqT!9d5_oAH1gR3+Mqp?jgIJ~2yhHf93z;ht1+tvB)0N9EXw*L0GVmLQ;I{#N6=1t z4q?M~BTAzJx#0k}oy@X4@=Q<`w%T6Ad2xWY&Dd+5n5X#8K>sqr(au+^3Ifry0q8<_ zy2+lRG(&?m^2KTS)c7!2ieSyvvQ4c5fWt6yOCqB+a5= zyBv@s`rO9c)^jKt9ff7}YOwTUYrJ;gsw8##iy-5}gxMk#0*+N$ZGdmu_tr&}zirOl z@<%A*+3Fo3>{+?{!L7@Ya)i!w+k;LqbG8BCmp}2#Xm@jC%bY$C7f&Wp zc0qM=u}E$DsloLF_(y?AX7{HTn8${4GQlkv*nma6lE2|b8ssZ zV}_Sv&s{BBGC00OvRM1Vea@m)85G?fG^>pFaf)3S)kAl+Z~hIV!CkPeR_9pIur0`W zk-244JRH*S!5F|X;y}7$=fOQ6qz+M!r~q*Ymu(TRz@2xs#;*Nr1(jwTMY@SfPbf6Y zbr)p$x806@5^q||pIn8QCLxz|W+|csKBC+j~qNURZk|nK0f0$+Y>klGvvV}o^%ISZn0rx)OI9YajzlQ|8D~azX6u7ZKc_rS$#8gSKA0kFa5Zvyy9_G-tlT zwU*llfi<)U*q>g?kGnU)XcHkxrEEp;9N_>ZLYFWq7CWCK1<>|6oL;-Qorwg?;ko&J z--<(@+5^YQ6Z6%y8DcCZww9ZC*min)P($pG7h~{l5&LND0#rv^qG;?gBM;56M(&Gp za$A@;DwMa_eArU0{Q0+Ysa2)7HF(m<6}D(kpxl!?^64-j|iEhhrZ>geh4;%;RkP*2?!QP*T#vCj=V1s{7{xb ze2?ZXeE8wJVHR9Y>2v@YmISrzA9p$l8q;;wNcV+snfDe`?BR=#Oxg>cP7E|~+vNDP zF1`iJ;Go`)!n-V}yTgNk_U7qr@$|%%2`Ru5hoYq6BYs5I%SLB4jylz_58hn~>%FzE6!*4O`#%~X z7{_aQm$!2p(Tkvh=tJHpLSlaWloqK7q`@S~b7#pHXRqUDiQA5h2V`J#bK%2(uo;P$ zW!jLpLv<(xTrcnJ6v}qwEe@}b2>th4VxU5XK|ZyseNa7ZsU^BCwK51Ss z3Q>VtbJEc$alIxQ$pi6M3T?R~a#`u$UoB0%c=vvwZCX@?GU0tFY}McFu*cqU0(6)| zIQa*zWjVkaQZ-SgO8E%i@q^;*fF^f=9CjFb^DD>*#L!hQIqwVZt)zk5+E#<#9z|Z%@qU%Y zj@Ir~pnW*Eu?D1{29^sN?yw~kfuft4)AR`SGw26c)6}VG_|&TRbGnbu*w<%MO5x;J zOtt-2-$!FK9Zv>FX;WVrWt0N9V6juKHL;(sK$!mZVKDQ)_@h{tI8^kkLH(~}=dA4_ z!L(OE6%47LfUCU_H+r3Wa>XOIq4z-3IUQT=#Y1qdv)>)vXTrFv^+|RE!BREmpP%(l zpa15CoD#*K?ao*!XyLY5)V&xeyS$EIta(2$DwNgPzq=nOQBE_`KWLRV7Z)u!U-zk8 zD~y$XW}YRmS$}}b>#CQKFM{si!2e)4=kgNL5^KG$0E_#Wl;#QvV=r@TANZ~%J#rLD zmjL>Z+LUjL{1W4@L)K3vm>1pQfl5=#zNOIiW zR||vUEXt!wc*?)Mu%GTeeKAiyE_+!}ogb-J+m)aP71AjHET*BNdQr$+pbzvZ#ScMkRDC=OB9+itRge==H7RLZCrg8lYS9 zgKf0=Jrj@^$SmGjZGUq`b(^xx`-b=f5?d&x^J7qJy6|i6WS)+YWes@(g?3HRaid`N z+3KsiRay2S^RA+_4`yMP0dv!N(&Ia>(}o<(N>BMcuq&s5!U)xi(t*h`^GfMD3NW;h z$ru@EQsbSk_N}@>;&W3scjJJ|t;dz4->8fvRm(i5{+c_E-wU2s*Q*!aJ?6FHSzmry znE)=;cP|HF>JimWL|6+Lv+$90~47C4vhk%*8=O-ruqwMpN->LLOa@lJLdOLJs0g#K~IWppR~NR;wcjp)Tpox zJQczvi!GBL-QL5RbLg#@_O=WE`r0%$m;B+&7&IgKq4P8EHHZ2lXr}q|c93t6T*x=B z6kwSVS=3Y}fGkNM(W=9D!Q2m0s?>57EdX*Q${y~c%|xtVU^Ia_4WuS?&h}&s=2@s2 zhpsgfs!q!p{aGnKIVn~q-{oiA?D1H13Zrs<*gbKM>&Y=y?vDXyXl&niQZ;PV3Un{} zmh8eFR%~L4Dev2ME_A4tu3R&71z3KfAT4H3@sHHrd?)*o%BFBv*3mDGG1AVyufe?v ziWjglLw|gMXl-YuQ49SZB7Lkzn>s#>0EW`+#`B|fI8Np`$-hQfbWyo_^mfb~^W|}&`2G?Spm1Hj% zxO^vVgZ}R*Ck2_dLBYz9sdauhepiWUayn_XD}0hmVou9U;7ral9DK|W)}SlrdhXq$o`LJD@S2eGoWdxY%%Mg{ zj%mCh4cS7hJy9S_qE;WFslPM?6`Jbfe^0~HB8T&Un8aqJr72n$lav$;L2JM+)i4=g||RgxPb51wA;p znwRqm6!>eGHcciV#|#^5os3G;)O6Nf9_L|-t5zjxf=LiSYbwyMW^Fh87w5pek{k>w zIBv(e5=={{ZklSdqc9t@G$;@2{}MwwkCVBd72`jDJp=?$m|PAQI$5?@fOE!%ntlZ3zCud+bZd=$g>_dS}@ajrb#S&Iz=Mo2<;$lRsGAj7d?7Epx7!{dBBcR|yn6Z1A%M@@B}WR3E)j^7s6*E>>LD7<4{_Pi}Wl z(5CilI9ivQ?>S-eqL{>adrN{CXh-wV{S}h*{<`TWJW0^(h4-j14Ir!?DTUV`hL5dS zKql(*8452xI~aoY`XATO_0OVnJuTI=`B>VoGwT(R9aY_@6v%yu#q_WXQU&0Yz$|+| zrb%M;v4d}hAgKpxT#%Ze%4p*m1^b`m9nSk6XHzFY#T7_1TQDsvUA$`+F-iJKS}%$3 z|B?``$Js8U8}IZd+m*RBzlT&U@rXq@^CVi#4~?~|(GZ4wpg)7ly7Fi7f-fF=h^$6A z35{IW@LBx$>h16qlz(jR1>CGMA>4LIM=$?2zKL>2aO�gU&X7&<7zMRn5xPBgbWe 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≦ZHpN@5BCAFO^ckw9m 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 { + try { + this.log.info(`POST /parameter-file-meta, body is ${JSON.stringify(req.body)}`); + this.parameterFileMetaList.push(req.body); + res.send(); + } catch (err) { + this.log.error(`POST parameter-file-meta error: ${err}`); + res.status(500); + res.send(err.message); + } + }); + + router.get(`/parameter-file-meta`, (req: Request, res: Response) => { + try { + this.log.info(`GET /parameter-file-meta`); + res.send(this.parameterFileMetaList); + } catch (err) { + this.log.error(`GET parameter-file-meta error: ${err}`); + res.status(500); + res.send(err.message); + } + }); + + return router; + } +} diff --git a/src/nni_manager/training_service/dlts/dltsTrainingService.ts b/src/nni_manager/training_service/dlts/dltsTrainingService.ts new file mode 100644 index 0000000000..6414f5236e --- /dev/null +++ b/src/nni_manager/training_service/dlts/dltsTrainingService.ts @@ -0,0 +1,503 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +'use strict'; + +import * as fs from 'fs'; +import * as path from 'path'; +import * as request from 'request'; + +import * as component from '../../common/component'; +import { EventEmitter } from 'events'; +import { String } from 'typescript-string-operations'; +import { getExperimentId } from '../../common/experimentStartupInfo'; +import { getLogger, Logger } from '../../common/log'; +import { + NNIManagerIpConfig, TrainingService, + TrialJobApplicationForm, TrialJobDetail, TrialJobMetric +} from '../../common/trainingService'; +import { DLTS_TRIAL_COMMAND_FORMAT } from './dltsData'; +import { CONTAINER_INSTALL_NNI_SHELL_FORMAT } from '../common/containerJobData'; +import { execMkdir, validateCodeDir } from '../common/util'; +import { delay, uniqueString, getIPV4Address, getExperimentRootDir, getVersion, generateParamFileName } from '../../common/utils'; +import { DLTSJobRestServer } from './dltsJobRestServer'; +import { TrialConfigMetadataKey } from '../../training_service/common/trialConfigMetadataKey'; +import { DLTSJobConfig } from './dltsJobConfig'; +import { DLTSClusterConfig } from './dltsClusterConfig'; +import { DLTSTrialConfig } from './dltsTrialConfig'; +import { DLTSTrialJobDetail } from './dltsTrialJobDetail'; + +@component.Singleton +class DLTSTrainingService implements TrainingService { + private readonly log!: Logger; + private readonly metricsEmitter: EventEmitter; + //private readonly expRootDir: string; + private readonly jobQueue: string[]; + private stopping: boolean = false; + private readonly experimentId!: string; + private versionCheck: boolean = true; + private logCollection: string = 'none'; + private isMultiPhase: boolean = false; + private dltsRestServerPort?: number; + + private readonly trialJobsMap: Map; + private nniManagerIpConfig?: NNIManagerIpConfig; + private dltsClusterConfig?: DLTSClusterConfig; + private dltsTrialConfig?: DLTSTrialConfig; + + constructor() { + this.log = getLogger(); + this.metricsEmitter = new EventEmitter(); + this.trialJobsMap = new Map(); + this.jobQueue = []; + this.experimentId = getExperimentId(); + this.log.info('Construct DLTS training service.'); + } + + public async run(): Promise { + this.log.info('Run DLTS training service.'); + const restServer: DLTSJobRestServer = component.get(DLTSJobRestServer); + await restServer.start(); + restServer.setEnableVersionCheck = this.versionCheck; + this.log.info(`DLTS Training service rest server listening on: ${restServer.endPoint}`); + await Promise.all([ + this.statusCheckingLoop(), + this.submitJobLoop()]); + this.log.info('DLTS training service exit.'); + } + + private async statusCheckingLoop(): Promise { + while (!this.stopping) { + const updateDLTSTrialJobs: Promise[] = []; + for (const [trialJobId, dltsTrialJob] of this.trialJobsMap) { + updateDLTSTrialJobs.push(this.getDLTSTrialJobInfo(dltsTrialJob)); + } + + await Promise.all(updateDLTSTrialJobs); + + // Calcel paused dlts job + const cancelPausedJobPromises: Promise[] = []; + for (const [trialJobId, dltsTrialJob] of this.trialJobsMap) { + if (dltsTrialJob.dltsPaused && dltsTrialJob.status === 'RUNNING') { + cancelPausedJobPromises.push(this.cancelTrialJob(trialJobId)); + } + } + await Promise.all(cancelPausedJobPromises); + + const restServer: DLTSJobRestServer = component.get(DLTSJobRestServer); + if (restServer.getErrorMessage !== undefined) { + throw new Error(restServer.getErrorMessage); + } + await delay(3000); + } + } + + private async getDLTSTrialJobInfo(dltsTrialJob: DLTSTrialJobDetail): Promise { + if (this.dltsClusterConfig == null) { + throw Error('Cluster config is not set'); + } + const requestOptions: request.Options = { + uri: `${this.dltsClusterConfig.dashboard}api/v2/clusters/${this.dltsClusterConfig.cluster}/jobs/${dltsTrialJob.dltsJobId}`, + qs: { + email: this.dltsClusterConfig.email, + password: this.dltsClusterConfig.password + }, + json: true + }; + const body = await new Promise((resolve, reject) => { + request(requestOptions, (error, response, body) => { + if (error != null) { + reject(error) + } else { + resolve(body) + } + }) + }) as any; + void ((): void => { + switch (body['jobStatus']) { + case 'unapproved': + case 'queued': + case 'scheduling': + dltsTrialJob.status = "WAITING"; + break; + case 'running': + dltsTrialJob.status = "RUNNING"; + if (dltsTrialJob.startTime === undefined) { + dltsTrialJob.startTime = Date.parse(body['jobStatusDetail'][0]['startedAt']) + } + if (dltsTrialJob.url === undefined) { + dltsTrialJob.url = `${this.dltsClusterConfig.dashboard}job/${this.dltsClusterConfig.team}/${this.dltsClusterConfig.cluster}/${dltsTrialJob.dltsJobId}` + } + break; + case 'finished': + dltsTrialJob.status = "SUCCEEDED"; + break; + case 'failed': + dltsTrialJob.status = "FAILED"; + break; + case 'pausing': + case 'paused': + dltsTrialJob.status = "RUNNING"; + dltsTrialJob.dltsPaused = true; + break; + case 'killing': + case 'killed': + if (dltsTrialJob.isEarlyStopped !== undefined) { + dltsTrialJob.status = dltsTrialJob.isEarlyStopped === true + ? 'EARLY_STOPPED' : 'USER_CANCELED'; + } else { + dltsTrialJob.status = 'SYS_CANCELED'; + } + break; + default: + dltsTrialJob.status = "UNKNOWN"; + } + }) (); + } + + private async submitJobLoop(): Promise { + while (!this.stopping) { + while (!this.stopping && this.jobQueue.length > 0) { + const trialJobId: string = this.jobQueue[0]; + this.log.info(`Got job ${trialJobId}`); + if (await this.submitTrialJobToDLTS(trialJobId)) { + // Remove trial job with trialJobId from job queue + this.jobQueue.shift(); + } else { + // Break the while loop since failed to submitJob + break; + } + } + await delay(3000); + } + } + + public async listTrialJobs(): Promise { + return Array.from(this.trialJobsMap.values()); + } + + public async getTrialJob(trialJobId: string): Promise { + const trialJob = this.trialJobsMap.get(trialJobId); + if (trialJob === undefined) { + throw Error(`Trial job ${trialJobId} not found.`) + } + return trialJob + } + + public addTrialJobMetricListener(listener: (metric: TrialJobMetric) => void): void { + this.metricsEmitter.on('metric', listener); + } + + public removeTrialJobMetricListener(listener: (metric: TrialJobMetric) => void): void { + this.metricsEmitter.off('metric', listener); + } + + public get MetricsEmitter(): EventEmitter { + return this.metricsEmitter; + } + + public async submitTrialJob(form: TrialJobApplicationForm): Promise { + const trialJobId: string = uniqueString(5); + const trialWorkingFolder: string = path.join( + '/nni/experiments', getExperimentId(), + '/trials/', trialJobId); + const trialJobDetail = new DLTSTrialJobDetail( + trialJobId, // id + 'WAITING', // status + Date.now(), // submitTime + trialWorkingFolder, // workingDirectory + form, + `nni_exp_${this.experimentId}_trial_${trialJobId}` + ); + + this.trialJobsMap.set(trialJobId, trialJobDetail); + this.jobQueue.push(trialJobId); + + return trialJobDetail; + } + + public async cancelTrialJob(trialJobId: string, isEarlyStopped: boolean = false): Promise { + const trialJobDetail: DLTSTrialJobDetail | undefined = this.trialJobsMap.get(trialJobId); + if (trialJobDetail === undefined) { + throw Error(`cancelTrialJob: trial job id ${trialJobId} not found`); + } + + if (this.dltsClusterConfig === undefined) { + throw Error('DLTS Cluster config is not initialized'); + } + + const options: request.Options = { + method: 'PUT', + uri: `${this.dltsClusterConfig.dashboard}api/clusters/${this.dltsClusterConfig.cluster}/jobs/${trialJobDetail.dltsJobId}/status`, + qs: { + email: this.dltsClusterConfig.email, + password: this.dltsClusterConfig.password + }, + body: { + status: 'killing' + }, + json: true + }; + + // Set trialjobDetail's early stopped field, to mark the job's cancellation source + trialJobDetail.isEarlyStopped = isEarlyStopped; + + await new Promise((resolve, reject) => { + request(options, (error: Error, response: request.Response, body: any) => { + if (error) { + reject(error); + } else { + resolve(body); + } + }); + }); + } + + private async getGpuType(): Promise { + if (this.dltsClusterConfig === undefined) { + throw new Error('DLTS Cluster config is not initialized'); + } + const gpuRequestOptions: request.Options = { + method: 'GET', + qs: { + email: this.dltsClusterConfig.email, + password: this.dltsClusterConfig.password + }, + uri: `${this.dltsClusterConfig.dashboard}api/teams/${this.dltsClusterConfig.team}/clusters/${this.dltsClusterConfig.cluster}`, + json: true + }; + return new Promise((resolve, reject) => { + request(gpuRequestOptions, (error, response, data) => { + if (error) { + return reject(error) + } + try { + const metadata = JSON.parse(data['metadata']) + resolve(Object.keys(metadata)[0]) + } catch (error) { + reject(error) + } + }) + }); + } + + public async setClusterMetadata(key: string, value: string): Promise { + switch (key) { + case TrialConfigMetadataKey.NNI_MANAGER_IP: + this.nniManagerIpConfig = JSON.parse(value); + break; + + case TrialConfigMetadataKey.DLTS_CLUSTER_CONFIG: + this.dltsClusterConfig = JSON.parse(value); + if (!this.dltsClusterConfig.cluster) { + this.dltsClusterConfig.cluster = '.default' + } + if (!this.dltsClusterConfig.email) { + if (process.env['DLWS_USER_EMAIL']) { + this.dltsClusterConfig.email = process.env['DLWS_USER_EMAIL'] as string + } else { + throw Error('`email` field in `dltsConfig` is not configured.') + } + } + if (!this.dltsClusterConfig.password) { + if (process.env['DLTS_JOB_TOKEN']) { + this.dltsClusterConfig.password = process.env['DLTS_JOB_TOKEN'] as string + } else { + throw Error('`password` field in `dltsConfig` is not configured.') + } + } + if (!this.dltsClusterConfig.team) { + if (process.env['DLWS_VC_NAME']) { + this.dltsClusterConfig.team = process.env['DLWS_VC_NAME'] as string + } else { + throw Error('`team` field in `dltsConfig` is not configured.') + } + } + this.dltsClusterConfig.gpuType = await this.getGpuType(); + break; + + case TrialConfigMetadataKey.TRIAL_CONFIG: + this.dltsTrialConfig = JSON.parse(value); + + // Validate to make sure codeDir doesn't have too many files + try { + await validateCodeDir(this.dltsTrialConfig.codeDir); + } catch (error) { + this.log.error(error); + throw error; + } + + break; + case TrialConfigMetadataKey.VERSION_CHECK: + this.versionCheck = (value === 'true' || value === 'True'); + break; + case TrialConfigMetadataKey.LOG_COLLECTION: + this.logCollection = value; + break; + case TrialConfigMetadataKey.MULTI_PHASE: + this.isMultiPhase = (value === 'true' || value === 'True'); + break; + default: + //Reject for unknown keys + throw new Error(`Uknown key: ${key}`); + } + } + + public async getClusterMetadata(key: string): Promise { + return ''; + } + + public async cleanUp(): Promise { + this.log.info('Stopping DLTS training service...'); + this.stopping = true; + + const restServer: DLTSJobRestServer = component.get(DLTSJobRestServer); + try { + await restServer.stop(); + this.log.info('DLTS Training service rest server stopped successfully.'); + return; + } catch (error) { + // tslint:disable-next-line: no-unsafe-any + this.log.error(`DLTS Training service rest server stopped failed, error: ${error.message}`); + throw error; + } + } + + private async submitTrialJobToDLTS(trialJobId: string): Promise { + const trialJobDetail: DLTSTrialJobDetail | undefined = this.trialJobsMap.get(trialJobId); + + if (trialJobDetail === undefined) { + throw new Error(`Failed to find DLTSTrialJobDetail for job ${trialJobId}`); + } + + if (this.dltsClusterConfig === undefined) { + throw new Error('DLTS Cluster config is not initialized'); + } + if (this.dltsTrialConfig === undefined) { + throw new Error('trial config is not initialized'); + } + + if (this.dltsRestServerPort === undefined) { + const restServer: DLTSJobRestServer = component.get(DLTSJobRestServer); + this.dltsRestServerPort = restServer.clusterRestServerPort; + } + + // Step 1. Prepare DLTS job configuration + + const trialLocalFolder = path.join(getExperimentRootDir(), 'trials-local', trialJobId); + //create tmp trial working folder locally. + await execMkdir(trialLocalFolder); + + const runScriptContent: string = CONTAINER_INSTALL_NNI_SHELL_FORMAT; + // Write NNI installation file to local tmp files + await fs.promises.writeFile(path.join(trialLocalFolder, 'install_nni.sh'), runScriptContent, { encoding: 'utf8' }); + + // Write file content ( parameter.cfg ) to local tmp folders + if (trialJobDetail.form !== undefined) { + await fs.promises.writeFile( + path.join(trialLocalFolder, generateParamFileName(trialJobDetail.form.hyperParameters)), + trialJobDetail.form.hyperParameters.value, { encoding: 'utf8' } + ); + } + // tslint:disable-next-line: strict-boolean-expressions + const nniManagerIp: string = this.nniManagerIpConfig ? this.nniManagerIpConfig.nniManagerIp : getIPV4Address(); + const version: string = this.versionCheck ? await getVersion() : ''; + const nniDLTSTrialCommand: string = String.Format( + DLTS_TRIAL_COMMAND_FORMAT, + trialLocalFolder, + path.join(trialLocalFolder, 'nnioutput'), + trialJobId, + this.experimentId, + trialJobDetail.form.sequenceId, + false, + this.dltsTrialConfig.codeDir, + this.dltsTrialConfig.command, + nniManagerIp, + this.dltsRestServerPort, + version, + this.logCollection + ) + .replace(/\r\n|\n|\r/gm, ''); + + // Step 2. Submit DLTS job via Rest call + const dltsJobConfig: DLTSJobConfig = new DLTSJobConfig( + this.dltsClusterConfig, + trialJobDetail.dltsJobName, + this.dltsTrialConfig.gpuNum, + this.dltsTrialConfig.image, + nniDLTSTrialCommand, + [] + ); + const submitJobRequest: request.Options = { + method: 'POST', + uri: `${this.dltsClusterConfig.dashboard}api/clusters/${this.dltsClusterConfig.cluster}/jobs`, + qs: { + email: this.dltsClusterConfig.email, + password: this.dltsClusterConfig.password + }, + body: dltsJobConfig, + json: true + } + const responseData = await new Promise((resolve, reject) => { + request(submitJobRequest, function (error, response, data) { + if (error) { + return reject(error) + } else { + return resolve(data) + } + }) + }); + + trialJobDetail.dltsJobId = responseData['jobId'] + + return true; + } + + public async updateTrialJob(trialJobId: string, form: TrialJobApplicationForm): Promise { + const trialJobDetail: undefined | TrialJobDetail = this.trialJobsMap.get(trialJobId); + if (trialJobDetail === undefined) { + throw new Error(`updateTrialJob failed: ${trialJobId} not found`); + } + if (this.dltsClusterConfig === undefined) { + throw new Error('DLTS Cluster config is not initialized'); + } + if (this.dltsTrialConfig === undefined) { + throw new Error('DLTS trial config is not initialized'); + } + + const hyperParameters = form.hyperParameters; + const trialLocalTempFolder: string = path.join(getExperimentRootDir(), 'trials-local', trialJobId); + const hpFileName: string = generateParamFileName(hyperParameters); + const localFilepath: string = path.join(trialLocalTempFolder, hpFileName); + await fs.promises.writeFile(localFilepath, hyperParameters.value, { encoding: 'utf8' }); + + const parameterFileMeta = { + experimentId: this.experimentId, + trialId: trialJobId + }; + const restServer: DLTSJobRestServer = component.get(DLTSJobRestServer); + const req: request.Options = { + uri: `${restServer.endPoint}${restServer.apiRootUrl}/parameter-file-meta`, + method: 'POST', + json: true, + body: parameterFileMeta + }; + await new Promise((resolve, reject) => { + request(req, (err: Error, res: request.Response) => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); + + return trialJobDetail; + } + + public get isMultiPhaseJobSupported(): boolean { + return false; + } +} + +export { DLTSTrainingService }; diff --git a/src/nni_manager/training_service/dlts/dltsTrialConfig.ts b/src/nni_manager/training_service/dlts/dltsTrialConfig.ts new file mode 100644 index 0000000000..bbf35d011b --- /dev/null +++ b/src/nni_manager/training_service/dlts/dltsTrialConfig.ts @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { TrialConfig } from "training_service/common/trialConfig"; + +export class DLTSTrialConfig extends TrialConfig { + public constructor( + command: string, + codeDir: string, + gpuNum: number, + public readonly image: string + ) { + super(command, codeDir, gpuNum); + } +} diff --git a/src/nni_manager/training_service/dlts/dltsTrialJobDetail.ts b/src/nni_manager/training_service/dlts/dltsTrialJobDetail.ts new file mode 100644 index 0000000000..2168102465 --- /dev/null +++ b/src/nni_manager/training_service/dlts/dltsTrialJobDetail.ts @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { + TrialJobDetail, + TrialJobStatus, + TrialJobApplicationForm +} from "../../common/trainingService"; + +export class DLTSTrialJobDetail implements TrialJobDetail { + public startTime?: number; + public endTime?: number; + public tags?: string[]; + public url?: string; + public isEarlyStopped?: boolean; + + // DLTS staff + public dltsJobId?: string; + public dltsPaused: boolean = false; + + public constructor ( + public id: string, + public status: TrialJobStatus, + public submitTime: number, + public workingDirectory: string, + public form: TrialJobApplicationForm, + + // DLTS staff + public dltsJobName: string, + ) {} +} diff --git a/src/sdk/pynni/nni/platform/__init__.py b/src/sdk/pynni/nni/platform/__init__.py index da3daf6c89..f4251bd5c7 100644 --- a/src/sdk/pynni/nni/platform/__init__.py +++ b/src/sdk/pynni/nni/platform/__init__.py @@ -9,7 +9,7 @@ from .standalone import * elif trial_env_vars.NNI_PLATFORM == 'unittest': from .test import * -elif trial_env_vars.NNI_PLATFORM in ('local', 'remote', 'pai', 'kubeflow', 'frameworkcontroller', 'paiYarn'): +elif trial_env_vars.NNI_PLATFORM in ('local', 'remote', 'pai', 'kubeflow', 'frameworkcontroller', 'paiYarn', 'dlts'): from .local import * else: raise RuntimeError('Unknown platform %s' % trial_env_vars.NNI_PLATFORM) diff --git a/tools/nni_cmd/config_schema.py b/tools/nni_cmd/config_schema.py index 4be463c7b6..b6df38724b 100644 --- a/tools/nni_cmd/config_schema.py +++ b/tools/nni_cmd/config_schema.py @@ -32,7 +32,8 @@ def setPathCheck(key): 'trialConcurrency': setNumberRange('trialConcurrency', int, 1, 99999), Optional('maxExecDuration'): And(Regex(r'^[1-9][0-9]*[s|m|h|d]$', error='ERROR: maxExecDuration format is [digit]{s,m,h,d}')), Optional('maxTrialNum'): setNumberRange('maxTrialNum', int, 1, 99999), - 'trainingServicePlatform': setChoice('trainingServicePlatform', 'remote', 'local', 'pai', 'kubeflow', 'frameworkcontroller', 'paiYarn'), + 'trainingServicePlatform': setChoice( + 'trainingServicePlatform', 'remote', 'local', 'pai', 'kubeflow', 'frameworkcontroller', 'paiYarn', 'dlts'), Optional('searchSpacePath'): And(os.path.exists, error=SCHEMA_PATH_ERROR % 'searchSpacePath'), Optional('multiPhase'): setType('multiPhase', bool), Optional('multiThread'): setType('multiThread', bool), @@ -297,6 +298,27 @@ def setPathCheck(key): }) } +dlts_trial_schema = { + 'trial':{ + 'command': setType('command', str), + 'codeDir': setPathCheck('codeDir'), + 'gpuNum': setNumberRange('gpuNum', int, 0, 99999), + 'image': setType('image', str), + } +} + +dlts_config_schema = { + 'dltsConfig': { + 'dashboard': setType('dashboard', str), + + Optional('cluster'): setType('cluster', str), + Optional('team'): setType('team', str), + + Optional('email'): setType('email', str), + Optional('password'): setType('password', str), + } +} + kubeflow_trial_schema = { 'trial':{ 'codeDir': setPathCheck('codeDir'), @@ -438,6 +460,8 @@ def setPathCheck(key): PAI_YARN_CONFIG_SCHEMA = Schema({**common_schema, **pai_yarn_trial_schema, **pai_yarn_config_schema}) +DLTS_CONFIG_SCHEMA = Schema({**common_schema, **dlts_trial_schema, **dlts_config_schema}) + KUBEFLOW_CONFIG_SCHEMA = Schema({**common_schema, **kubeflow_trial_schema, **kubeflow_config_schema}) FRAMEWORKCONTROLLER_CONFIG_SCHEMA = Schema({**common_schema, **frameworkcontroller_trial_schema, **frameworkcontroller_config_schema}) diff --git a/tools/nni_cmd/launcher.py b/tools/nni_cmd/launcher.py index 2af1c609bb..885177f313 100644 --- a/tools/nni_cmd/launcher.py +++ b/tools/nni_cmd/launcher.py @@ -289,6 +289,25 @@ def set_frameworkcontroller_config(experiment_config, port, config_file_name): #set trial_config return set_trial_config(experiment_config, port, config_file_name), err_message +def set_dlts_config(experiment_config, port, config_file_name): + '''set dlts configuration''' + dlts_config_data = dict() + dlts_config_data['dlts_config'] = experiment_config['dltsConfig'] + response = rest_put(cluster_metadata_url(port), json.dumps(dlts_config_data), REST_TIME_OUT) + err_message = None + if not response or not response.status_code == 200: + if response is not None: + err_message = response.text + _, stderr_full_path = get_log_path(config_file_name) + with open(stderr_full_path, 'a+') as fout: + fout.write(json.dumps(json.loads(err_message), indent=4, sort_keys=True, separators=(',', ':'))) + return False, err_message + result, message = setNNIManagerIp(experiment_config, port, config_file_name) + if not result: + return result, message + #set trial_config + return set_trial_config(experiment_config, port, config_file_name), err_message + def set_experiment(experiment_config, mode, port, config_file_name): '''Call startExperiment (rest POST /experiment) with yaml file content''' request_data = dict() @@ -389,6 +408,8 @@ def set_platform_config(platform, experiment_config, port, config_file_name, res config_result, err_msg = set_kubeflow_config(experiment_config, port, config_file_name) elif platform == 'frameworkcontroller': config_result, err_msg = set_frameworkcontroller_config(experiment_config, port, config_file_name) + elif platform == 'dlts': + config_result, err_msg = set_dlts_config(experiment_config, port, config_file_name) else: raise Exception(ERROR_INFO % 'Unsupported platform!') exit(1) diff --git a/tools/nni_cmd/launcher_utils.py b/tools/nni_cmd/launcher_utils.py index 9301e5bf2b..ee35a8213a 100644 --- a/tools/nni_cmd/launcher_utils.py +++ b/tools/nni_cmd/launcher_utils.py @@ -5,8 +5,9 @@ import json from schema import SchemaError from schema import Schema -from .config_schema import LOCAL_CONFIG_SCHEMA, REMOTE_CONFIG_SCHEMA, PAI_CONFIG_SCHEMA, PAI_YARN_CONFIG_SCHEMA, KUBEFLOW_CONFIG_SCHEMA,\ - FRAMEWORKCONTROLLER_CONFIG_SCHEMA, tuner_schema_dict, advisor_schema_dict, assessor_schema_dict +from .config_schema import LOCAL_CONFIG_SCHEMA, REMOTE_CONFIG_SCHEMA, PAI_CONFIG_SCHEMA, PAI_YARN_CONFIG_SCHEMA, \ + DLTS_CONFIG_SCHEMA, KUBEFLOW_CONFIG_SCHEMA, FRAMEWORKCONTROLLER_CONFIG_SCHEMA, \ + tuner_schema_dict, advisor_schema_dict, assessor_schema_dict from .common_utils import print_error, print_warning, print_normal, get_yml_content def expand_path(experiment_config, key): @@ -147,7 +148,9 @@ def validate_kubeflow_operators(experiment_config): def validate_common_content(experiment_config): '''Validate whether the common values in experiment_config is valid''' if not experiment_config.get('trainingServicePlatform') or \ - experiment_config.get('trainingServicePlatform') not in ['local', 'remote', 'pai', 'kubeflow', 'frameworkcontroller', 'paiYarn']: + experiment_config.get('trainingServicePlatform') not in [ + 'local', 'remote', 'pai', 'kubeflow', 'frameworkcontroller', 'paiYarn', 'dlts' + ]: print_error('Please set correct trainingServicePlatform!') exit(1) schema_dict = { @@ -156,7 +159,8 @@ def validate_common_content(experiment_config): 'pai': PAI_CONFIG_SCHEMA, 'paiYarn': PAI_YARN_CONFIG_SCHEMA, 'kubeflow': KUBEFLOW_CONFIG_SCHEMA, - 'frameworkcontroller': FRAMEWORKCONTROLLER_CONFIG_SCHEMA + 'frameworkcontroller': FRAMEWORKCONTROLLER_CONFIG_SCHEMA, + 'dlts': DLTS_CONFIG_SCHEMA, } separate_schema_dict = { 'tuner': tuner_schema_dict, From c80f2cdaa1b955faa677c56bf09c1590ca2c9aa4 Mon Sep 17 00:00:00 2001 From: Lijiaoa <61399850+Lijiaoa@users.noreply.github.com> Date: Mon, 2 Mar 2020 10:51:17 +0800 Subject: [PATCH 06/12] fix bug (#2104) Co-authored-by: Lijiao <35484733+lvybriage@users.noreply.github.com> --- src/webui/src/components/trial-detail/TableList.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/webui/src/components/trial-detail/TableList.tsx b/src/webui/src/components/trial-detail/TableList.tsx index fc5b1633c3..b3acf82517 100644 --- a/src/webui/src/components/trial-detail/TableList.tsx +++ b/src/webui/src/components/trial-detail/TableList.tsx @@ -241,6 +241,7 @@ class TableList extends React.Component { // support intermediate result is dict because the last intermediate result is // final result in a succeed trial, it may be a dict. // get intermediate result dict keys array + const { intermediateKey } = this.state; let otherkeys: string[] = ['default']; if (res.data.length !== 0) { otherkeys = Object.keys(parseMetrics(res.data[0].data)); @@ -249,7 +250,7 @@ class TableList extends React.Component { Object.keys(res.data).map(item => { const temp = parseMetrics(res.data[item].data); if (typeof temp === 'object') { - intermediateArr.push(temp.default); + intermediateArr.push(temp[intermediateKey]); } else { intermediateArr.push(temp); } From e862d39ee96aad5b9fdc3bb535df3a89bd1807af Mon Sep 17 00:00:00 2001 From: chicm-ms <38930155+chicm-ms@users.noreply.github.com> Date: Mon, 2 Mar 2020 10:57:44 +0800 Subject: [PATCH 07/12] Refactor pruner examples (#2099) --- docs/en_US/Compressor/QuickStart.md | 4 +- examples/model_compress/APoZ_torch_cifar10.py | 121 --------- .../MeanActivation_torch_cifar10.py | 139 ---------- examples/model_compress/fpgm_torch_mnist.py | 107 -------- examples/model_compress/main_torch_pruner.py | 96 ------- examples/model_compress/model_prune_torch.py | 246 ++++++++++++++++++ examples/model_compress/multi_gpu.py | 101 ------- 7 files changed, 249 insertions(+), 565 deletions(-) delete mode 100644 examples/model_compress/APoZ_torch_cifar10.py delete mode 100644 examples/model_compress/MeanActivation_torch_cifar10.py delete mode 100644 examples/model_compress/fpgm_torch_mnist.py delete mode 100644 examples/model_compress/main_torch_pruner.py create mode 100644 examples/model_compress/model_prune_torch.py delete mode 100644 examples/model_compress/multi_gpu.py diff --git a/docs/en_US/Compressor/QuickStart.md b/docs/en_US/Compressor/QuickStart.md index ed5ac59ecd..656d78c5d9 100644 --- a/docs/en_US/Compressor/QuickStart.md +++ b/docs/en_US/Compressor/QuickStart.md @@ -1,6 +1,6 @@ # Quick Start to Compress a Model -NNI provides very simple APIs for compressing a model. The compression includes pruning algorithms and quantization algorithms. The usage of them are the same, thus, here we use slim pruner as an example to show the usage. The complete code of this example can be found [here](https://github.com/microsoft/nni/blob/master/examples/model_compress/slim_torch_cifar10.py). +NNI provides very simple APIs for compressing a model. The compression includes pruning algorithms and quantization algorithms. The usage of them are the same, thus, here we use slim pruner as an example to show the usage. ## Write configuration @@ -34,6 +34,8 @@ After training, you get accuracy of the pruned model. You can export model weigh pruner.export_model(model_path='pruned_vgg19_cifar10.pth', mask_path='mask_vgg19_cifar10.pth') ``` +The complete code of model compression examples can be found [here](https://github.com/microsoft/nni/blob/master/examples/model_compress/model_prune_torch.py). + ## Speed up the model Masks do not provide real speedup of your model. The model should be speeded up based on the exported masks, thus, we provide an API to speed up your model as shown below. After invoking `apply_compression_results` on your model, your model becomes a smaller one with shorter inference latency. diff --git a/examples/model_compress/APoZ_torch_cifar10.py b/examples/model_compress/APoZ_torch_cifar10.py deleted file mode 100644 index cc3af667b8..0000000000 --- a/examples/model_compress/APoZ_torch_cifar10.py +++ /dev/null @@ -1,121 +0,0 @@ -import math -import torch -import torch.nn as nn -import torch.nn.functional as F -from torchvision import datasets, transforms -from nni.compression.torch import ActivationAPoZRankFilterPruner -from models.cifar10.vgg import VGG - - -def train(model, device, train_loader, optimizer): - model.train() - for batch_idx, (data, target) in enumerate(train_loader): - data, target = data.to(device), target.to(device) - optimizer.zero_grad() - output = model(data) - loss = F.cross_entropy(output, target) - loss.backward() - optimizer.step() - if batch_idx % 100 == 0: - print('{:2.0f}% Loss {}'.format(100 * batch_idx / len(train_loader), loss.item())) - - -def test(model, device, test_loader): - model.eval() - test_loss = 0 - correct = 0 - with torch.no_grad(): - for data, target in test_loader: - data, target = data.to(device), target.to(device) - output = model(data) - test_loss += F.nll_loss(output, target, reduction='sum').item() - pred = output.argmax(dim=1, keepdim=True) - correct += pred.eq(target.view_as(pred)).sum().item() - test_loss /= len(test_loader.dataset) - acc = 100 * correct / len(test_loader.dataset) - - print('Loss: {} Accuracy: {}%)\n'.format( - test_loss, acc)) - return acc - - -def main(): - torch.manual_seed(0) - device = torch.device("cuda" if torch.cuda.is_available() else "cpu") - train_loader = torch.utils.data.DataLoader( - datasets.CIFAR10('./data.cifar10', train=True, download=True, - transform=transforms.Compose([ - transforms.Pad(4), - transforms.RandomCrop(32), - transforms.RandomHorizontalFlip(), - transforms.ToTensor(), - transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)) - ])), - batch_size=64, shuffle=True) - test_loader = torch.utils.data.DataLoader( - datasets.CIFAR10('./data.cifar10', train=False, transform=transforms.Compose([ - transforms.ToTensor(), - transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)) - ])), - batch_size=200, shuffle=False) - - model = VGG(depth=16) - model.to(device) - - # Train the base VGG-16 model - print('=' * 10 + 'Train the unpruned base model' + '=' * 10) - optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9, weight_decay=1e-4) - lr_scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, 160, 0) - for epoch in range(160): - train(model, device, train_loader, optimizer) - test(model, device, test_loader) - lr_scheduler.step(epoch) - torch.save(model.state_dict(), 'vgg16_cifar10.pth') - - # Test base model accuracy - print('=' * 10 + 'Test on the original model' + '=' * 10) - model.load_state_dict(torch.load('vgg16_cifar10.pth')) - test(model, device, test_loader) - # top1 = 93.51% - - # Pruning Configuration, in paper 'PRUNING FILTERS FOR EFFICIENT CONVNETS', - # Conv_1, Conv_8, Conv_9, Conv_10, Conv_11, Conv_12 are pruned with 50% sparsity, as 'VGG-16-pruned-A' - configure_list = [{ - 'sparsity': 0.5, - 'op_types': ['default'], - 'op_names': ['feature.0', 'feature.24', 'feature.27', 'feature.30', 'feature.34', 'feature.37'] - }] - - # Prune model and test accuracy without fine tuning. - print('=' * 10 + 'Test on the pruned model before fine tune' + '=' * 10) - pruner = ActivationAPoZRankFilterPruner(model, configure_list) - model = pruner.compress() - test(model, device, test_loader) - # top1 = 88.19% - - # Fine tune the pruned model for 40 epochs and test accuracy - print('=' * 10 + 'Fine tuning' + '=' * 10) - optimizer_finetune = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=1e-4) - best_top1 = 0 - for epoch in range(40): - pruner.update_epoch(epoch) - print('# Epoch {} #'.format(epoch)) - train(model, device, train_loader, optimizer_finetune) - top1 = test(model, device, test_loader) - if top1 > best_top1: - best_top1 = top1 - # Export the best model, 'model_path' stores state_dict of the pruned model, - # mask_path stores mask_dict of the pruned model - pruner.export_model(model_path='pruned_vgg16_cifar10.pth', mask_path='mask_vgg16_cifar10.pth') - - # Test the exported model - print('=' * 10 + 'Test on the pruned model after fine tune' + '=' * 10) - new_model = VGG(depth=16) - new_model.to(device) - new_model.load_state_dict(torch.load('pruned_vgg16_cifar10.pth')) - test(new_model, device, test_loader) - # top1 = 93.53% - - -if __name__ == '__main__': - main() diff --git a/examples/model_compress/MeanActivation_torch_cifar10.py b/examples/model_compress/MeanActivation_torch_cifar10.py deleted file mode 100644 index b7a8c14850..0000000000 --- a/examples/model_compress/MeanActivation_torch_cifar10.py +++ /dev/null @@ -1,139 +0,0 @@ -import math -import os -import argparse -import torch -import torch.nn as nn -import torch.nn.functional as F -from torchvision import datasets, transforms -from nni.compression.torch import ActivationMeanRankFilterPruner -from models.cifar10.vgg import VGG - - -def train(model, device, train_loader, optimizer): - model.train() - for batch_idx, (data, target) in enumerate(train_loader): - data, target = data.to(device), target.to(device) - optimizer.zero_grad() - output = model(data) - loss = F.cross_entropy(output, target) - loss.backward() - optimizer.step() - if batch_idx % 100 == 0: - print('{:2.0f}% Loss {}'.format(100 * batch_idx / len(train_loader), loss.item())) - - -def test(model, device, test_loader): - model.eval() - test_loss = 0 - correct = 0 - with torch.no_grad(): - for data, target in test_loader: - data, target = data.to(device), target.to(device) - output = model(data) - test_loss += F.nll_loss(output, target, reduction='sum').item() - pred = output.argmax(dim=1, keepdim=True) - correct += pred.eq(target.view_as(pred)).sum().item() - test_loss /= len(test_loader.dataset) - acc = 100 * correct / len(test_loader.dataset) - - print('Loss: {} Accuracy: {}%)\n'.format( - test_loss, acc)) - return acc - - -def main(): - parser = argparse.ArgumentParser("multiple gpu with pruning") - parser.add_argument("--epochs", type=int, default=160) - parser.add_argument("--retrain", default=False, action="store_true") - parser.add_argument("--parallel", default=False, action="store_true") - - args = parser.parse_args() - torch.manual_seed(0) - device = torch.device("cuda" if torch.cuda.is_available() else "cpu") - train_loader = torch.utils.data.DataLoader( - datasets.CIFAR10('./data.cifar10', train=True, download=True, - transform=transforms.Compose([ - transforms.Pad(4), - transforms.RandomCrop(32), - transforms.RandomHorizontalFlip(), - transforms.ToTensor(), - transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)) - ])), - batch_size=64, shuffle=True) - test_loader = torch.utils.data.DataLoader( - datasets.CIFAR10('./data.cifar10', train=False, transform=transforms.Compose([ - transforms.ToTensor(), - transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)) - ])), - batch_size=200, shuffle=False) - - model = VGG(depth=16) - model.to(device) - - # Train the base VGG-16 model - if args.retrain: - print('=' * 10 + 'Train the unpruned base model' + '=' * 10) - optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9, weight_decay=1e-4) - lr_scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, 160, 0) - for epoch in range(args.epochs): - train(model, device, train_loader, optimizer) - test(model, device, test_loader) - lr_scheduler.step(epoch) - torch.save(model.state_dict(), 'vgg16_cifar10.pth') - else: - assert os.path.isfile('vgg16_cifar10.pth'), "can not find checkpoint 'vgg16_cifar10.pth'" - model.load_state_dict(torch.load('vgg16_cifar10.pth')) - # Test base model accuracy - print('=' * 10 + 'Test on the original model' + '=' * 10) - test(model, device, test_loader) - # top1 = 93.51% - - # Pruning Configuration, in paper 'PRUNING FILTERS FOR EFFICIENT CONVNETS', - # Conv_1, Conv_8, Conv_9, Conv_10, Conv_11, Conv_12 are pruned with 50% sparsity, as 'VGG-16-pruned-A' - configure_list = [{ - 'sparsity': 0.5, - 'op_types': ['default'], - 'op_names': ['feature.0', 'feature.24', 'feature.27', 'feature.30', 'feature.34', 'feature.37'] - }] - - # Prune model and test accuracy without fine tuning. - print('=' * 10 + 'Test on the pruned model before fine tune' + '=' * 10) - pruner = ActivationMeanRankFilterPruner(model, configure_list) - model = pruner.compress() - if args.parallel: - if torch.cuda.device_count() > 1: - print("use {} gpus for pruning".format(torch.cuda.device_count())) - model = nn.DataParallel(model) - else: - print("only detect 1 gpu, fall back") - - model.to(device) - test(model, device, test_loader) - # top1 = 88.19% - - # Fine tune the pruned model for 40 epochs and test accuracy - print('=' * 10 + 'Fine tuning' + '=' * 10) - optimizer_finetune = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=1e-4) - best_top1 = 0 - for epoch in range(40): - pruner.update_epoch(epoch) - print('# Epoch {} #'.format(epoch)) - train(model, device, train_loader, optimizer_finetune) - top1 = test(model, device, test_loader) - if top1 > best_top1: - best_top1 = top1 - # Export the best model, 'model_path' stores state_dict of the pruned model, - # mask_path stores mask_dict of the pruned model - pruner.export_model(model_path='pruned_vgg16_cifar10.pth', mask_path='mask_vgg16_cifar10.pth') - - # Test the exported model - print('=' * 10 + 'Test on the pruned model after fine tune' + '=' * 10) - new_model = VGG(depth=16) - new_model.to(device) - new_model.load_state_dict(torch.load('pruned_vgg16_cifar10.pth')) - test(new_model, device, test_loader) - # top1 = 93.53% - - -if __name__ == '__main__': - main() diff --git a/examples/model_compress/fpgm_torch_mnist.py b/examples/model_compress/fpgm_torch_mnist.py deleted file mode 100644 index ae1c8fd301..0000000000 --- a/examples/model_compress/fpgm_torch_mnist.py +++ /dev/null @@ -1,107 +0,0 @@ -import torch -import torch.nn as nn -import torch.nn.functional as F -from torchvision import datasets, transforms -from nni.compression.torch import FPGMPruner - -class Mnist(torch.nn.Module): - def __init__(self): - super().__init__() - self.conv1 = nn.Conv2d(1, 20, 5, 1) - self.conv2 = nn.Conv2d(20, 50, 5, 1) - self.fc1 = nn.Linear(4 * 4 * 50, 500) - self.fc2 = nn.Linear(500, 10) - - def forward(self, x): - x = F.relu(self.conv1(x)) - x = F.max_pool2d(x, 2, 2) - x = F.relu(self.conv2(x)) - x = F.max_pool2d(x, 2, 2) - x = x.view(x.size(0), -1) - x = F.relu(self.fc1(x)) - x = self.fc2(x) - return F.log_softmax(x, dim=1) - - def _get_conv_weight_sparsity(self, conv_layer): - num_zero_filters = (conv_layer.weight.data.sum((1, 2, 3)) == 0).sum() - num_filters = conv_layer.weight.data.size(0) - return num_zero_filters, num_filters, float(num_zero_filters)/num_filters - - def print_conv_filter_sparsity(self): - if isinstance(self.conv1, nn.Conv2d): - conv1_data = self._get_conv_weight_sparsity(self.conv1) - conv2_data = self._get_conv_weight_sparsity(self.conv2) - else: - # self.conv1 is wrapped as PrunerModuleWrapper - conv1_data = self._get_conv_weight_sparsity(self.conv1.module) - conv2_data = self._get_conv_weight_sparsity(self.conv2.module) - - print('conv1: num zero filters: {}, num filters: {}, sparsity: {:.4f}'.format(conv1_data[0], conv1_data[1], conv1_data[2])) - print('conv2: num zero filters: {}, num filters: {}, sparsity: {:.4f}'.format(conv2_data[0], conv2_data[1], conv2_data[2])) - -def train(model, device, train_loader, optimizer): - model.train() - for batch_idx, (data, target) in enumerate(train_loader): - data, target = data.to(device), target.to(device) - optimizer.zero_grad() - output = model(data) - loss = F.nll_loss(output, target) - if batch_idx % 100 == 0: - print('{:.2f}% Loss {:.4f}'.format(100 * batch_idx / len(train_loader), loss.item())) - if batch_idx == 0: - model.print_conv_filter_sparsity() - loss.backward() - optimizer.step() - -def test(model, device, test_loader): - model.eval() - test_loss = 0 - correct = 0 - with torch.no_grad(): - for data, target in test_loader: - data, target = data.to(device), target.to(device) - output = model(data) - test_loss += F.nll_loss(output, target, reduction='sum').item() - pred = output.argmax(dim=1, keepdim=True) - correct += pred.eq(target.view_as(pred)).sum().item() - test_loss /= len(test_loader.dataset) - - print('Loss: {:.4f} Accuracy: {}%)\n'.format( - test_loss, 100 * correct / len(test_loader.dataset))) - - -def main(): - torch.manual_seed(0) - device = torch.device("cuda" if torch.cuda.is_available() else "cpu") - - trans = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]) - train_loader = torch.utils.data.DataLoader( - datasets.MNIST('data', train=True, download=True, transform=trans), - batch_size=64, shuffle=True) - test_loader = torch.utils.data.DataLoader( - datasets.MNIST('data', train=False, transform=trans), - batch_size=1000, shuffle=True) - - model = Mnist() - model.to(device) - model.print_conv_filter_sparsity() - - configure_list = [{ - 'sparsity': 0.5, - 'op_types': ['Conv2d'] - }] - - pruner = FPGMPruner(model, configure_list) - pruner.compress() - model.to(device) - optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.5) - for epoch in range(10): - pruner.update_epoch(epoch) - print('# Epoch {} #'.format(epoch)) - train(model, device, train_loader, optimizer) - test(model, device, test_loader) - - pruner.export_model('model.pth', 'mask.pth') - -if __name__ == '__main__': - main() diff --git a/examples/model_compress/main_torch_pruner.py b/examples/model_compress/main_torch_pruner.py deleted file mode 100644 index b859d122be..0000000000 --- a/examples/model_compress/main_torch_pruner.py +++ /dev/null @@ -1,96 +0,0 @@ -from nni.compression.torch import AGP_Pruner -import torch -import torch.nn.functional as F -from torchvision import datasets, transforms - - -class Mnist(torch.nn.Module): - def __init__(self): - super().__init__() - self.conv1 = torch.nn.Conv2d(1, 20, 5, 1) - self.conv2 = torch.nn.Conv2d(20, 50, 5, 1) - self.fc1 = torch.nn.Linear(4 * 4 * 50, 500) - self.fc2 = torch.nn.Linear(500, 10) - - def forward(self, x): - x = F.relu(self.conv1(x)) - x = F.max_pool2d(x, 2, 2) - x = F.relu(self.conv2(x)) - x = F.max_pool2d(x, 2, 2) - x = x.view(-1, 4 * 4 * 50) - x = F.relu(self.fc1(x)) - x = self.fc2(x) - return F.log_softmax(x, dim=1) - - -def train(model, device, train_loader, optimizer): - model.train() - for batch_idx, (data, target) in enumerate(train_loader): - data, target = data.to(device), target.to(device) - optimizer.zero_grad() - output = model(data) - loss = F.nll_loss(output, target) - loss.backward() - optimizer.step() - if batch_idx % 100 == 0: - print('{:2.0f}% Loss {}'.format(100 * batch_idx / len(train_loader), loss.item())) - - -def test(model, device, test_loader): - model.eval() - test_loss = 0 - correct = 0 - with torch.no_grad(): - for data, target in test_loader: - data, target = data.to(device), target.to(device) - output = model(data) - test_loss += F.nll_loss(output, target, reduction='sum').item() - pred = output.argmax(dim=1, keepdim=True) - correct += pred.eq(target.view_as(pred)).sum().item() - test_loss /= len(test_loader.dataset) - - print('Loss: {} Accuracy: {}%)\n'.format( - test_loss, 100 * correct / len(test_loader.dataset))) - - -def main(): - torch.manual_seed(0) - device = torch.device("cuda" if torch.cuda.is_available() else "cpu") - - trans = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]) - train_loader = torch.utils.data.DataLoader( - datasets.MNIST('data', train=True, download=True, transform=trans), - batch_size=64, shuffle=True) - test_loader = torch.utils.data.DataLoader( - datasets.MNIST('data', train=False, transform=trans), - batch_size=1000, shuffle=True) - - model = Mnist() - model = model.to(device) - - '''you can change this to LevelPruner to implement it - pruner = LevelPruner(configure_list) - ''' - configure_list = [{ - 'initial_sparsity': 0, - 'final_sparsity': 0.8, - 'start_epoch': 0, - 'end_epoch': 10, - 'frequency': 1, - 'op_types': ['default'] - }] - - pruner = AGP_Pruner(model, configure_list) - model = pruner.compress() - model = model.to(device) - optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.5) - for epoch in range(10): - pruner.update_epoch(epoch) - print('# Epoch {} #'.format(epoch)) - train(model, device, train_loader, optimizer) - test(model, device, test_loader) - pruner.export_model('model.pth', 'mask.pth', 'model.onnx', [1, 1, 28, 28], device) - - -if __name__ == '__main__': - main() diff --git a/examples/model_compress/model_prune_torch.py b/examples/model_compress/model_prune_torch.py new file mode 100644 index 0000000000..1cca5f2fe0 --- /dev/null +++ b/examples/model_compress/model_prune_torch.py @@ -0,0 +1,246 @@ +import os +import argparse +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +from torch.utils.data import DataLoader +from torchvision import datasets, transforms + +from models.cifar10.vgg import VGG +import nni +from nni.compression.torch import LevelPruner, SlimPruner, FPGMPruner, L1FilterPruner, \ + L2FilterPruner, AGP_Pruner, ActivationMeanRankFilterPruner, ActivationAPoZRankFilterPruner + +prune_config = { + 'level': { + 'dataset_name': 'mnist', + 'model_name': 'naive', + 'pruner_class': LevelPruner, + 'config_list': [{ + 'sparsity': 0.5, + 'op_types': ['default'], + }] + }, + 'agp': { + 'dataset_name': 'mnist', + 'model_name': 'naive', + 'pruner_class': AGP_Pruner, + 'config_list': [{ + 'initial_sparsity': 0, + 'final_sparsity': 0.8, + 'start_epoch': 0, + 'end_epoch': 10, + 'frequency': 1, + 'op_types': ['default'] + }] + }, + 'slim': { + 'dataset_name': 'cifar10', + 'model_name': 'vgg19', + 'pruner_class': SlimPruner, + 'config_list': [{ + 'sparsity': 0.7, + 'op_types': ['BatchNorm2d'] + }] + }, + 'fpgm': { + 'dataset_name': 'mnist', + 'model_name': 'naive', + 'pruner_class': FPGMPruner, + 'config_list':[{ + 'sparsity': 0.5, + 'op_types': ['Conv2d'] + }] + }, + 'l1': { + 'dataset_name': 'cifar10', + 'model_name': 'vgg16', + 'pruner_class': L1FilterPruner, + 'config_list': [{ + 'sparsity': 0.5, + 'op_types': ['default'], + 'op_names': ['feature.0', 'feature.24', 'feature.27', 'feature.30', 'feature.34', 'feature.37'] + }] + }, + 'mean_activation': { + 'dataset_name': 'cifar10', + 'model_name': 'vgg16', + 'pruner_class': ActivationMeanRankFilterPruner, + 'configure_list': [{ + 'sparsity': 0.5, + 'op_types': ['default'], + 'op_names': ['feature.0', 'feature.24', 'feature.27', 'feature.30', 'feature.34', 'feature.37'] + }] + }, + 'apoz': { + 'dataset_name': 'cifar10', + 'model_name': 'vgg16', + 'pruner_class': ActivationAPoZRankFilterPruner, + 'config_list': [{ + 'sparsity': 0.5, + 'op_types': ['default'], + 'op_names': ['feature.0', 'feature.24', 'feature.27', 'feature.30', 'feature.34', 'feature.37'] + }] + } +} + +def get_data_loaders(dataset_name='mnist', batch_size=128): + assert dataset_name in ['cifar10', 'mnist'] + + if dataset_name == 'cifar10': + ds_class = datasets.CIFAR10 if dataset_name == 'cifar10' else datasets.MNIST + MEAN, STD = (0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010) + else: + ds_class = datasets.MNIST + MEAN, STD = (0.1307,), (0.3081,) + + train_loader = DataLoader( + ds_class( + './data', train=True, download=True, + transform=transforms.Compose([transforms.ToTensor(), transforms.Normalize(MEAN, STD)]) + ), + batch_size=batch_size, shuffle=True + ) + test_loader = DataLoader( + ds_class( + './data', train=False, download=True, + transform=transforms.Compose([transforms.ToTensor(), transforms.Normalize(MEAN, STD)]) + ), + batch_size=batch_size, shuffle=False + ) + + return train_loader, test_loader + +class NaiveModel(torch.nn.Module): + def __init__(self): + super().__init__() + self.conv1 = nn.Conv2d(1, 20, 5, 1) + self.conv2 = nn.Conv2d(20, 50, 5, 1) + self.bn1 = nn.BatchNorm2d(self.conv1.out_channels) + self.bn2 = nn.BatchNorm2d(self.conv2.out_channels) + self.fc1 = nn.Linear(4 * 4 * 50, 500) + self.fc2 = nn.Linear(500, 10) + + def forward(self, x): + x = F.relu(self.bn1(self.conv1(x))) + x = F.max_pool2d(x, 2, 2) + x = F.relu(self.bn2(self.conv2(x))) + x = F.max_pool2d(x, 2, 2) + x = x.view(-1, 4 * 4 * 50) + x = F.relu(self.fc1(x)) + x = self.fc2(x) + return x + +def create_model(model_name='naive'): + assert model_name in ['naive', 'vgg16', 'vgg19'] + + if model_name == 'naive': + return NaiveModel() + elif model_name == 'vgg16': + return VGG(16) + else: + return VGG(19) + +def create_pruner(model, pruner_name): + pruner_class = prune_config[pruner_name]['pruner_class'] + config_list = prune_config[pruner_name]['config_list'] + return pruner_class(model, config_list) + +def train(model, device, train_loader, optimizer): + model.train() + for batch_idx, (data, target) in enumerate(train_loader): + data, target = data.to(device), target.to(device) + optimizer.zero_grad() + output = model(data) + loss = F.cross_entropy(output, target) + loss.backward() + optimizer.step() + if batch_idx % 100 == 0: + print('{:2.0f}% Loss {}'.format(100 * batch_idx / len(train_loader), loss.item())) + +def test(model, device, test_loader): + model.eval() + test_loss = 0 + correct = 0 + with torch.no_grad(): + for data, target in test_loader: + data, target = data.to(device), target.to(device) + output = model(data) + test_loss += F.cross_entropy(output, target, reduction='sum').item() + pred = output.argmax(dim=1, keepdim=True) + correct += pred.eq(target.view_as(pred)).sum().item() + test_loss /= len(test_loader.dataset) + acc = 100 * correct / len(test_loader.dataset) + + print('Loss: {} Accuracy: {}%)\n'.format( + test_loss, acc)) + return acc + +def main(args): + device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu') + + model_name = prune_config[args.pruner_name]['model_name'] + dataset_name = prune_config[args.pruner_name]['dataset_name'] + train_loader, test_loader = get_data_loaders(dataset_name, args.batch_size) + model = create_model(model_name).cuda() + if args.resume_from is not None and os.path.exists(args.resume_from): + print('loading checkpoint {} ...'.format(args.resume_from)) + model.load_state_dict(torch.load(args.resume_from)) + test(model, device, test_loader) + else: + optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9, weight_decay=1e-4) + if args.multi_gpu and torch.cuda.device_count(): + model = nn.DataParallel(model) + + print('start training') + pretrain_model_path = os.path.join( + args.checkpoints_dir, 'pretrain_{}_{}_{}.pth'.format(model_name, dataset_name, args.pruner_name)) + for epoch in range(args.pretrain_epochs): + train(model, device, train_loader, optimizer) + test(model, device, test_loader) + torch.save(model.state_dict(), pretrain_model_path) + + print('start model pruning...') + + if not os.path.exists(args.checkpoints_dir): + os.makedirs(args.checkpoints_dir) + model_path = os.path.join(args.checkpoints_dir, 'pruned_{}_{}_{}.pth'.format(model_name, dataset_name, args.pruner_name)) + mask_path = os.path.join(args.checkpoints_dir, 'mask_{}_{}_{}.pth'.format(model_name, dataset_name, args.pruner_name)) + + # pruner needs to be initialized from a model not wrapped by DataParallel + if isinstance(model, nn.DataParallel): + model = model.module + + optimizer_finetune = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=1e-4) + best_top1 = 0 + + pruner = create_pruner(model, args.pruner_name) + model = pruner.compress() + + if args.multi_gpu and torch.cuda.device_count() > 1: + model = nn.DataParallel(model) + + for epoch in range(args.prune_epochs): + pruner.update_epoch(epoch) + print('# Epoch {} #'.format(epoch)) + train(model, device, train_loader, optimizer_finetune) + top1 = test(model, device, test_loader) + if top1 > best_top1: + best_top1 = top1 + # Export the best model, 'model_path' stores state_dict of the pruned model, + # mask_path stores mask_dict of the pruned model + pruner.export_model(model_path=model_path, mask_path=mask_path) + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("--pruner_name", type=str, default="level", help="pruner name") + parser.add_argument("--batch_size", type=int, default=256) + parser.add_argument("--pretrain_epochs", type=int, default=10, help="training epochs before model pruning") + parser.add_argument("--prune_epochs", type=int, default=10, help="training epochs for model pruning") + parser.add_argument("--checkpoints_dir", type=str, default="./checkpoints", help="checkpoints directory") + parser.add_argument("--resume_from", type=str, default=None, help="pretrained model weights") + parser.add_argument("--multi_gpu", action="store_true", help="Use multiple GPUs for training") + + args = parser.parse_args() + main(args) diff --git a/examples/model_compress/multi_gpu.py b/examples/model_compress/multi_gpu.py deleted file mode 100644 index 118fd57f5d..0000000000 --- a/examples/model_compress/multi_gpu.py +++ /dev/null @@ -1,101 +0,0 @@ -import torch -import torch.nn as nn -import torch.nn.functional as F -import torch.utils.data -import torchvision.datasets as datasets -import torchvision.transforms as transforms -from nni.compression.torch import SlimPruner - -class fc1(nn.Module): - - def __init__(self, num_classes=10): - super(fc1, self).__init__() - self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1) - self.bn1 = nn.BatchNorm2d(32) - self.relu1 = nn.ReLU(inplace=True) - - - self.linear1 = nn.Linear(32*28*28, 300) - self.relu2 = nn.ReLU(inplace=True) - self.linear2 = nn.Linear(300, 100) - self.relu3 = nn.ReLU(inplace=True) - self.linear3 = nn.Linear(100, num_classes) - - - def forward(self, x): - x = self.conv1(x) - x = self.bn1(x) - x = self.relu1(x) - - x = torch.flatten(x,1) - x = self.linear1(x) - x = self.relu2(x) - x = self.linear2(x) - x = self.relu3(x) - x = self.linear3(x) - return x - -def train(model, train_loader, optimizer, criterion, device): - model.train() - for imgs, targets in train_loader: - optimizer.zero_grad() - imgs, targets = imgs.to(device), targets.to(device) - output = model(imgs) - train_loss = criterion(output, targets) - train_loss.backward() - optimizer.step() - return train_loss.item() - -def test(model, test_loader, criterion, device): - model.eval() - test_loss = 0 - correct = 0 - with torch.no_grad(): - for data, target in test_loader: - data, target = data.to(device), target.to(device) - output = model(data) - test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss - pred = output.data.max(1, keepdim=True)[1] # get the index of the max log-probability - correct += pred.eq(target.data.view_as(pred)).sum().item() - test_loss /= len(test_loader.dataset) - accuracy = 100. * correct / len(test_loader.dataset) - return accuracy - - -if __name__ == '__main__': - transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]) - traindataset = datasets.MNIST('./data', train=True, download=True, transform=transform) - testdataset = datasets.MNIST('./data', train=False, transform=transform) - train_loader = torch.utils.data.DataLoader(traindataset, batch_size=60, shuffle=True, num_workers=10, drop_last=False) - test_loader = torch.utils.data.DataLoader(testdataset, batch_size=60, shuffle=False, num_workers=10, drop_last=True) - - device = torch.device("cuda" if torch.cuda.is_available() else "cpu") - model = fc1() - - criterion = nn.CrossEntropyLoss() - - configure_list = [{ - 'prune_iterations': 5, - 'sparsity': 0.86, - 'op_types': ['BatchNorm2d'] - }] - pruner = SlimPruner(model, configure_list) - pruner.compress() - - if torch.cuda.device_count()>1: - model = nn.DataParallel(model) - - model.to(device) - optimizer = torch.optim.Adam(model.parameters(), lr=1.2e-3) - for name, par in model.named_parameters(): - print(name) - # for i in pruner.get_prune_iterations(): - # pruner.prune_iteration_start() - loss = 0 - accuracy = 0 - for epoch in range(10): - loss = train(model, train_loader, optimizer, criterion, device) - accuracy = test(model, test_loader, criterion, device) - print('current epoch: {0}, loss: {1}, accuracy: {2}'.format(epoch, loss, accuracy)) - # print('prune iteration: {0}, loss: {1}, accuracy: {2}'.format(i, loss, accuracy)) - pruner.export_model('model.pth', 'mask.pth') \ No newline at end of file From a7b5f3b44b014a79bf378e1a5aca24b0e9a86a5d Mon Sep 17 00:00:00 2001 From: Lijiaoa <61399850+Lijiaoa@users.noreply.github.com> Date: Mon, 2 Mar 2020 13:46:24 +0800 Subject: [PATCH 08/12] update succeed table in time (#2103) --- src/webui/src/components/Overview.tsx | 2 +- src/webui/src/components/overview/SuccessTable.tsx | 5 +++++ src/webui/src/static/style/overview.scss | 8 ++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/webui/src/components/Overview.tsx b/src/webui/src/components/Overview.tsx index 5254a48a27..88864c7fe8 100644 --- a/src/webui/src/components/Overview.tsx +++ b/src/webui/src/components/Overview.tsx @@ -120,7 +120,7 @@ class Overview extends React.Component { -
    +
    } ]; + componentWillReceiveProps(nextProps: SuccessTableProps): void { + const { trialIds } = nextProps; + this.setState(() => ({ source: TRIALS.table(trialIds) })); + } + render(): React.ReactNode { const { columns, source } = this.state; return ( diff --git a/src/webui/src/static/style/overview.scss b/src/webui/src/static/style/overview.scss index 68a51141e4..6277bb1a66 100644 --- a/src/webui/src/static/style/overview.scss +++ b/src/webui/src/static/style/overview.scss @@ -56,4 +56,12 @@ color: #333; font-size: 14px; } +} + +/* overview-succeed-graph */ +.showMess{ + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); } \ No newline at end of file From d6999f2cef0e47e1ed752f6884b882c4ad57bc18 Mon Sep 17 00:00:00 2001 From: Scarlett Li <39592018+scarlett2018@users.noreply.github.com> Date: Mon, 2 Mar 2020 14:33:02 +0800 Subject: [PATCH 09/12] Update NAS Overview.md (#2042) * Update NAS Overview.md --- docs/en_US/NAS/Overview.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/en_US/NAS/Overview.md b/docs/en_US/NAS/Overview.md index 1a325d911f..9e7bc687d4 100644 --- a/docs/en_US/NAS/Overview.md +++ b/docs/en_US/NAS/Overview.md @@ -34,10 +34,10 @@ Here are some common dependencies to run the examples. PyTorch needs to be above |Name|Brief Introduction of Algorithm| |---|---| -| [SPOS](SPOS.md) | [Single Path One-Shot Neural Architecture Search with Uniform Sampling](https://arxiv.org/abs/1904.00420) constructs a simplified supernet trained with an uniform path sampling method, and applies an evolutionary algorithm to efficiently search for the best-performing architectures. | +| [SPOS's 2nd stage](SPOS.md) | [Single Path One-Shot Neural Architecture Search with Uniform Sampling](https://arxiv.org/abs/1904.00420) constructs a simplified supernet trained with an uniform path sampling method, and applies an evolutionary algorithm to efficiently search for the best-performing architectures. _Note:: SPOS is a two-stage algorithm, whose first stage is one-shot and second stage is distributed, leveraging result of first stage as a checkpoint._| -```eval_rst -.. Note:: SPOS is a two-stage algorithm, whose first stage is one-shot and second stage is distributed, leveraging result of first stage as a checkpoint. +```eval_rst +.. Note:: SPOS is a two-stage algorithm, whose first stage is one-shot and second stage is distributed, leveraging result of first stage as a checkpoint. ``` ## Use NNI API @@ -58,4 +58,4 @@ The programming interface of designing and searching a model is often demanded i [5]: https://arxiv.org/abs/1703.01041 * To [report a bug](https://github.com/microsoft/nni/issues/new?template=bug-report.md) for this feature in GitHub; -* To [file a feature or improvement request](https://github.com/microsoft/nni/issues/new?template=enhancement.md) for this feature in GitHub. \ No newline at end of file +* To [file a feature or improvement request](https://github.com/microsoft/nni/issues/new?template=enhancement.md) for this feature in GitHub. From 5f5f86c6dc11e7bf780986d9bfd36fdaac131a79 Mon Sep 17 00:00:00 2001 From: Scarlett Li <39592018+scarlett2018@users.noreply.github.com> Date: Mon, 2 Mar 2020 14:33:50 +0800 Subject: [PATCH 10/12] Update README.md (#2084) fix typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aff326437b..cb346f2050 100644 --- a/README.md +++ b/README.md @@ -22,13 +22,13 @@ The tool manages automated machine learning (AutoML) experiments, **dispatches a * Those who want to **try different AutoML algorithms** in their training code/model. * Those who want to run AutoML trial jobs **in different environments** to speed up search. -* Researchers and data scientists who want to easily **implement and experiement new AutoML algorithms**, may it be: hyperparameter tuning algorithm, neural architect search algorithm or model compression algorithm. +* Researchers and data scientists who want to easily **implement and experiment new AutoML algorithms**, may it be: hyperparameter tuning algorithm, neural architect search algorithm or model compression algorithm. * ML Platform owners who want to **support AutoML in their platform**. ### **NNI v1.4 has been released!  ** ## **NNI capabilities in a glance** -NNI provides CommandLine Tool as well as an user friendly WebUI to manage training experiements. With the extensible API, you can customize your own AutoML algorithms and training services. To make it easy for new users, NNI also provides a set of build-in stat-of-the-art AutoML algorithms and out of box support for popular training platforms. +NNI provides CommandLine Tool as well as an user friendly WebUI to manage training experiments. With the extensible API, you can customize your own AutoML algorithms and training services. To make it easy for new users, NNI also provides a set of build-in stat-of-the-art AutoML algorithms and out of box support for popular training platforms. Within the following table, we summarized the current NNI capabilities, we are gradually adding new capabilities and we'd love to have your contribution. From f86c7005e2c371ec3e5eabef0855c932fcc54bba Mon Sep 17 00:00:00 2001 From: Scarlett Li <39592018+scarlett2018@users.noreply.github.com> Date: Mon, 2 Mar 2020 18:38:36 +0800 Subject: [PATCH 11/12] fix a small problem in example code (#2110) * Update mnist.py for pytorch it's weird putting pytorch's tmp data in a folder called /tmp/tensorflow... --- examples/trials/mnist-pytorch/mnist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/trials/mnist-pytorch/mnist.py b/examples/trials/mnist-pytorch/mnist.py index af7dcce982..93b718ff54 100644 --- a/examples/trials/mnist-pytorch/mnist.py +++ b/examples/trials/mnist-pytorch/mnist.py @@ -128,7 +128,7 @@ def get_params(): # Training settings parser = argparse.ArgumentParser(description='PyTorch MNIST Example') parser.add_argument("--data_dir", type=str, - default='/tmp/tensorflow/mnist/input_data', help="data directory") + default='/tmp/pytorch/mnist/input_data', help="data directory") parser.add_argument('--batch_size', type=int, default=64, metavar='N', help='input batch size for training (default: 64)') parser.add_argument("--hidden_size", type=int, default=512, metavar='N', From ede593804169a727e65b6723ec8a075afdf4c8c2 Mon Sep 17 00:00:00 2001 From: Cjkkkk <656569648@qq.com> Date: Tue, 3 Mar 2020 09:56:49 +0800 Subject: [PATCH 12/12] Patch optimizer for pruner (#2058) --- docs/en_US/Compressor/Framework.md | 102 +++++++++ docs/en_US/Compressor/Overview.md | 2 +- docs/en_US/model_compression.rst | 1 + examples/model_compress/L1_torch_cifar10.py | 4 +- .../model_compress/QAT_torch_quantizer.py | 8 +- .../model_compress/lottery_torch_mnist_fc.py | 1 - examples/model_compress/model_prune_torch.py | 2 +- examples/model_compress/slim_torch_cifar10.py | 6 +- .../pynni/nni/compression/torch/__init__.py | 2 +- .../torch/activation_rank_filter_pruners.py | 82 +++---- .../pynni/nni/compression/torch/compressor.py | 206 ++++++++++-------- .../pynni/nni/compression/torch/pruners.py | 102 ++++----- .../pynni/nni/compression/torch/quantizers.py | 58 ++--- .../torch/weight_rank_filter_pruners.py | 49 +++-- src/sdk/pynni/tests/test_compressor.py | 98 ++++----- 15 files changed, 411 insertions(+), 312 deletions(-) create mode 100644 docs/en_US/Compressor/Framework.md diff --git a/docs/en_US/Compressor/Framework.md b/docs/en_US/Compressor/Framework.md new file mode 100644 index 0000000000..9922c019a8 --- /dev/null +++ b/docs/en_US/Compressor/Framework.md @@ -0,0 +1,102 @@ +## Overview +The model compression framework has two main components: `pruner` and `module wrapper`. + +### pruner +A `pruner` is responsible for : +1. provide a `cal_mask` method that calculates masks for weight and bias. +2. replace the module with `module wrapper` based on config. +3. modify the optimizer so that the `cal_mask` method is called every time the `step` method is called. + +### module wrapper +A `module wrapper` is a module containing : +1. the origin module +2. some buffers used by `cal_mask` +3. a new forward method that applies masks before running the original forward method. + +the reasons to use `module wrapper` : +1. some buffers are needed by `cal_mask` to calculate masks and these buffers should be registered in `module wrapper` so that the original modules are not contaminated. +2. a new `forward` method is needed to apply masks to weight before calling the real `forward` method. + +## How it works +A basic pruner usage: +```python +configure_list = [{ + 'sparsity': 0.7, + 'op_types': ['BatchNorm2d'], +}] + +optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=1e-4) +pruner = SlimPruner(model, configure_list, optimizer) +model = pruner.compress() +``` + +A pruner receive model, config and optimizer as arguments. In the `__init__` method, the `step` method of the optimizer is replaced with a new `step` method that calls `cal_mask`. Also, all modules are checked if they need to be pruned based on config. If a module needs to be pruned, then this module is replaced by a `module wrapper`. Afterward, the new model and new optimizer are returned, which can be trained as before. `compress` method will calculate the default masks. + +## Implement a new pruning algorithm +Implementing a new pruning algorithm requires implementing a new `pruner` class, which should subclass `Pruner` and override the `cal_mask` method. The `cal_mask` is called by`optimizer.step` method. +The `Pruner` base class provided basic functionality listed above, for example, replacing modules and patching optimizer. + +A basic pruner look likes this: +```python +class NewPruner(Pruner): + def __init__(self, model, config_list, optimizer) + super().__init__(model, config_list, optimizer) + # do some initialization + + def calc_mask(self, wrapper, **kwargs): + # do something to calculate weight_mask + wrapper.weight_mask = weight_mask +``` +### Set wrapper attribute +Sometimes `cal_mask` must save some state data, therefore users can use `set_wrappers_attribute` API to register attribute just like how buffers are registered in PyTorch modules. These buffers will be registered to `module wrapper`. Users can access these buffers through `module wrapper`. + +```python +class NewPruner(Pruner): + def __init__(self, model, config_list, optimizer): + super().__init__(model, config_list, optimizer) + self.set_wrappers_attribute("if_calculated", False) + + def calc_mask(self, wrapper): + # do something to calculate weight_mask + if wrapper.if_calculated: + pass + else: + wrapper.if_calculated = True + # update masks +``` + +### Collect data during forward +Sometimes users want to collect some data during the modules' forward method, for example, the mean value of the activation. Therefore user can add a customized collector to module. + +```python +class ActivationRankFilterPruner(Pruner): + def __init__(self, model, config_list, optimizer, activation='relu', statistics_batch_num=1): + super().__init__(model, config_list, optimizer) + self.set_wrappers_attribute("if_calculated", False) + self.set_wrappers_attribute("collected_activation", []) + self.statistics_batch_num = statistics_batch_num + + def collector(module_, input_, output): + if len(module_.collected_activation) < self.statistics_batch_num: + module_.collected_activation.append(self.activation(output.detach().cpu())) + self.add_activation_collector(collector) + assert activation in ['relu', 'relu6'] + if activation == 'relu': + self.activation = torch.nn.functional.relu + elif activation == 'relu6': + self.activation = torch.nn.functional.relu6 + else: + self.activation = None +``` +The collector function will be called each time the forward method runs. + +Users can also remove this collector like this: +```python +collector_id = self.add_activation_collector(collector) +# ... +self.remove_activation_collector(collector_id) +``` + +### Multi-GPU support +On multi-GPU training, buffers and parameters are copied to multiple GPU every time the `forward` method runs on multiple GPU. If buffers and parameters are updated in the `forward` method, an `in-place` update is needed to ensure the update is effective. +Since `cal_mask` is called in the `optimizer.step` method, which happens after the `forward` method and happens only on one GPU, it supports multi-GPU naturally. \ No newline at end of file diff --git a/docs/en_US/Compressor/Overview.md b/docs/en_US/Compressor/Overview.md index 862307fb92..047d1903c0 100644 --- a/docs/en_US/Compressor/Overview.md +++ b/docs/en_US/Compressor/Overview.md @@ -3,7 +3,7 @@ As larger neural networks with more layers and nodes are considered, reducing th We are glad to introduce model compression toolkit on top of NNI, it's still in the experiment phase which might evolve based on usage feedback. We'd like to invite you to use, feedback and even contribute. -NNI provides an easy-to-use toolkit to help user design and use compression algorithms. It currently supports PyTorch with unified interface. For users to compress their models, they only need to add several lines in their code. There are some popular model compression algorithms built-in in NNI. Users could further use NNI's auto tuning power to find the best compressed model, which is detailed in [Auto Model Compression](./AutoCompression.md). On the other hand, users could easily customize their new compression algorithms using NNI's interface, refer to the tutorial [here](#customize-new-compression-algorithms). +NNI provides an easy-to-use toolkit to help user design and use compression algorithms. It currently supports PyTorch with unified interface. For users to compress their models, they only need to add several lines in their code. There are some popular model compression algorithms built-in in NNI. Users could further use NNI's auto tuning power to find the best compressed model, which is detailed in [Auto Model Compression](./AutoCompression.md). On the other hand, users could easily customize their new compression algorithms using NNI's interface, refer to the tutorial [here](#customize-new-compression-algorithms). Details about how model compression framework works can be found in [here](./Framework.md). For a survey of model compression, you can refer to this paper: [Recent Advances in Efficient Computation of Deep Convolutional Neural Networks](https://arxiv.org/pdf/1802.00939.pdf). diff --git a/docs/en_US/model_compression.rst b/docs/en_US/model_compression.rst index 92754719fa..457acfadce 100644 --- a/docs/en_US/model_compression.rst +++ b/docs/en_US/model_compression.rst @@ -21,3 +21,4 @@ For details, please refer to the following tutorials: Quantizers Model Speedup Automatic Model Compression + Implementation diff --git a/examples/model_compress/L1_torch_cifar10.py b/examples/model_compress/L1_torch_cifar10.py index 908dadd30b..1daaec98d7 100644 --- a/examples/model_compress/L1_torch_cifar10.py +++ b/examples/model_compress/L1_torch_cifar10.py @@ -88,14 +88,14 @@ def main(): # Prune model and test accuracy without fine tuning. print('=' * 10 + 'Test on the pruned model before fine tune' + '=' * 10) - pruner = L1FilterPruner(model, configure_list) + optimizer_finetune = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=1e-4) + pruner = L1FilterPruner(model, configure_list, optimizer_finetune) model = pruner.compress() test(model, device, test_loader) # top1 = 88.19% # Fine tune the pruned model for 40 epochs and test accuracy print('=' * 10 + 'Fine tuning' + '=' * 10) - optimizer_finetune = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=1e-4) best_top1 = 0 for epoch in range(40): pruner.update_epoch(epoch) diff --git a/examples/model_compress/QAT_torch_quantizer.py b/examples/model_compress/QAT_torch_quantizer.py index 61d8fbe5a5..c73986c576 100644 --- a/examples/model_compress/QAT_torch_quantizer.py +++ b/examples/model_compress/QAT_torch_quantizer.py @@ -79,15 +79,15 @@ def main(): }, { 'quant_types': ['output'], 'quant_bits': 8, - 'quant_start_step': 7000, + 'quant_start_step': 1000, 'op_types':['ReLU6'] }] - quantizer = QAT_Quantizer(model, configure_list) + optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.5) + quantizer = QAT_Quantizer(model, configure_list, optimizer) quantizer.compress() model.to(device) - optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.5) - for epoch in range(10): + for epoch in range(40): print('# Epoch {} #'.format(epoch)) train(model, quantizer, device, train_loader, optimizer) test(model, device, test_loader) diff --git a/examples/model_compress/lottery_torch_mnist_fc.py b/examples/model_compress/lottery_torch_mnist_fc.py index bd4b0b105a..24242380b1 100644 --- a/examples/model_compress/lottery_torch_mnist_fc.py +++ b/examples/model_compress/lottery_torch_mnist_fc.py @@ -71,7 +71,6 @@ def test(model, test_loader, criterion): pruner = LotteryTicketPruner(model, configure_list, optimizer) pruner.compress() - #model = nn.DataParallel(model) for i in pruner.get_prune_iterations(): pruner.prune_iteration_start() diff --git a/examples/model_compress/model_prune_torch.py b/examples/model_compress/model_prune_torch.py index 1cca5f2fe0..fabed49095 100644 --- a/examples/model_compress/model_prune_torch.py +++ b/examples/model_compress/model_prune_torch.py @@ -215,7 +215,7 @@ def main(args): optimizer_finetune = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=1e-4) best_top1 = 0 - pruner = create_pruner(model, args.pruner_name) + pruner = create_pruner(model, args.pruner_name, optimizer_finetune) model = pruner.compress() if args.multi_gpu and torch.cuda.device_count() > 1: diff --git a/examples/model_compress/slim_torch_cifar10.py b/examples/model_compress/slim_torch_cifar10.py index c2bb052782..5bc749cfe3 100644 --- a/examples/model_compress/slim_torch_cifar10.py +++ b/examples/model_compress/slim_torch_cifar10.py @@ -107,7 +107,8 @@ def main(): # Prune model and test accuracy without fine tuning. print('=' * 10 + 'Test the pruned model before fine tune' + '=' * 10) - pruner = SlimPruner(model, configure_list) + optimizer_finetune = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=1e-4) + pruner = SlimPruner(model, configure_list, optimizer_finetune) model = pruner.compress() if args.parallel: if torch.cuda.device_count() > 1: @@ -119,13 +120,12 @@ def main(): model.to(device) # Fine tune the pruned model for 40 epochs and test accuracy print('=' * 10 + 'Fine tuning' + '=' * 10) - optimizer_finetune = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=1e-4) best_top1 = 0 for epoch in range(40): - pruner.update_epoch(epoch) print('# Epoch {} #'.format(epoch)) train(model, device, train_loader, optimizer_finetune) top1 = test(model, device, test_loader) + if top1 > best_top1: best_top1 = top1 # Export the best model, 'model_path' stores state_dict of the pruned model, diff --git a/src/sdk/pynni/nni/compression/torch/__init__.py b/src/sdk/pynni/nni/compression/torch/__init__.py index 432cdf1529..19bc0b165b 100644 --- a/src/sdk/pynni/nni/compression/torch/__init__.py +++ b/src/sdk/pynni/nni/compression/torch/__init__.py @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT license. -from .compressor import LayerInfo, Compressor, Pruner, Quantizer +from .compressor import Compressor, Pruner, Quantizer from .pruners import * from .weight_rank_filter_pruners import * from .activation_rank_filter_pruners import * diff --git a/src/sdk/pynni/nni/compression/torch/activation_rank_filter_pruners.py b/src/sdk/pynni/nni/compression/torch/activation_rank_filter_pruners.py index fd3650a031..4c0b3b8207 100644 --- a/src/sdk/pynni/nni/compression/torch/activation_rank_filter_pruners.py +++ b/src/sdk/pynni/nni/compression/torch/activation_rank_filter_pruners.py @@ -16,7 +16,7 @@ class ActivationRankFilterPruner(Pruner): to achieve a preset level of network sparsity. """ - def __init__(self, model, config_list, activation='relu', statistics_batch_num=1): + def __init__(self, model, config_list, optimizer, activation='relu', statistics_batch_num=1): """ Parameters ---------- @@ -31,11 +31,15 @@ def __init__(self, model, config_list, activation='relu', statistics_batch_num=1 Num of batches for activation statistics """ - super().__init__(model, config_list) - self.register_buffer("if_calculated", torch.tensor(0)) # pylint: disable=not-callable + super().__init__(model, config_list, optimizer) + self.set_wrappers_attribute("if_calculated", False) + self.set_wrappers_attribute("collected_activation", []) self.statistics_batch_num = statistics_batch_num - self.collected_activation = {} - self.hooks = {} + + def collector(module_, input_, output): + if len(module_.collected_activation) < self.statistics_batch_num: + module_.collected_activation.append(self.activation(output.detach().cpu())) + self.add_activation_collector(collector) assert activation in ['relu', 'relu6'] if activation == 'relu': self.activation = torch.nn.functional.relu @@ -44,33 +48,10 @@ def __init__(self, model, config_list, activation='relu', statistics_batch_num=1 else: self.activation = None - def compress(self): - """ - Compress the model, register a hook for collecting activations. - """ - if self.modules_wrapper is not None: - # already compressed - return self.bound_model - else: - self.modules_wrapper = [] - modules_to_compress = self.detect_modules_to_compress() - for layer, config in modules_to_compress: - wrapper = self._wrap_modules(layer, config) - self.modules_wrapper.append(wrapper) - self.collected_activation[layer.name] = [] - - def _hook(module_, input_, output, name=layer.name): - if len(self.collected_activation[name]) < self.statistics_batch_num: - self.collected_activation[name].append(self.activation(output.detach().cpu())) - - wrapper.module.register_forward_hook(_hook) - self._wrap_model() - return self.bound_model - def get_mask(self, base_mask, activations, num_prune): raise NotImplementedError('{} get_mask is not implemented'.format(self.__class__.__name__)) - def calc_mask(self, layer, config, **kwargs): + def calc_mask(self, wrapper, **kwargs): """ Calculate the mask of given layer. Filters with the smallest importance criterion which is calculated from the activation are masked. @@ -88,29 +69,30 @@ def calc_mask(self, layer, config, **kwargs): dictionary for storing masks """ - weight = layer.module.weight.data - op_type = layer.type + weight = wrapper.module.weight.data + op_type = wrapper.type + config = wrapper.config assert 0 <= config.get('sparsity') < 1, "sparsity must in the range [0, 1)" assert op_type in ['Conv2d'], "only support Conv2d" assert op_type in config.get('op_types') - if_calculated = kwargs["if_calculated"] - if if_calculated: + + if wrapper.if_calculated: return None mask_weight = torch.ones(weight.size()).type_as(weight).detach() - if hasattr(layer.module, 'bias') and layer.module.bias is not None: - mask_bias = torch.ones(layer.module.bias.size()).type_as(layer.module.bias).detach() + if hasattr(wrapper.module, 'bias') and wrapper.module.bias is not None: + mask_bias = torch.ones(wrapper.module.bias.size()).type_as(wrapper.module.bias).detach() else: mask_bias = None - mask = {'weight': mask_weight, 'bias': mask_bias} + mask = {'weight_mask': mask_weight, 'bias_mask': mask_bias} try: filters = weight.size(0) num_prune = int(filters * config.get('sparsity')) - if filters < 2 or num_prune < 1 or len(self.collected_activation[layer.name]) < self.statistics_batch_num: + if filters < 2 or num_prune < 1 or len(wrapper.collected_activation) < self.statistics_batch_num: return mask - mask = self.get_mask(mask, self.collected_activation[layer.name], num_prune) + mask = self.get_mask(mask, wrapper.collected_activation, num_prune) finally: - if len(self.collected_activation[layer.name]) == self.statistics_batch_num: - if_calculated.copy_(torch.tensor(1)) # pylint: disable=not-callable + if len(wrapper.collected_activation) == self.statistics_batch_num: + wrapper.if_calculated = True return mask @@ -123,7 +105,7 @@ class ActivationAPoZRankFilterPruner(ActivationRankFilterPruner): https://arxiv.org/abs/1607.03250 """ - def __init__(self, model, config_list, activation='relu', statistics_batch_num=1): + def __init__(self, model, config_list, optimizer, activation='relu', statistics_batch_num=1): """ Parameters ---------- @@ -137,7 +119,7 @@ def __init__(self, model, config_list, activation='relu', statistics_batch_num=1 statistics_batch_num : int Num of batches for activation statistics """ - super().__init__(model, config_list, activation, statistics_batch_num) + super().__init__(model, config_list, optimizer, activation, statistics_batch_num) def get_mask(self, base_mask, activations, num_prune): """ @@ -161,9 +143,9 @@ def get_mask(self, base_mask, activations, num_prune): apoz = self._calc_apoz(activations) prune_indices = torch.argsort(apoz, descending=True)[:num_prune] for idx in prune_indices: - base_mask['weight'][idx] = 0. - if base_mask['bias'] is not None: - base_mask['bias'][idx] = 0. + base_mask['weight_mask'][idx] = 0. + if base_mask['bias_mask'] is not None: + base_mask['bias_mask'][idx] = 0. return base_mask def _calc_apoz(self, activations): @@ -195,7 +177,7 @@ class ActivationMeanRankFilterPruner(ActivationRankFilterPruner): https://arxiv.org/abs/1611.06440 """ - def __init__(self, model, config_list, activation='relu', statistics_batch_num=1): + def __init__(self, model, config_list, optimizer, activation='relu', statistics_batch_num=1): """ Parameters ---------- @@ -209,7 +191,7 @@ def __init__(self, model, config_list, activation='relu', statistics_batch_num=1 statistics_batch_num : int Num of batches for activation statistics """ - super().__init__(model, config_list, activation, statistics_batch_num) + super().__init__(model, config_list, optimizer, activation, statistics_batch_num) def get_mask(self, base_mask, activations, num_prune): """ @@ -233,9 +215,9 @@ def get_mask(self, base_mask, activations, num_prune): mean_activation = self._cal_mean_activation(activations) prune_indices = torch.argsort(mean_activation)[:num_prune] for idx in prune_indices: - base_mask['weight'][idx] = 0. - if base_mask['bias'] is not None: - base_mask['bias'][idx] = 0. + base_mask['weight_mask'][idx] = 0. + if base_mask['bias_mask'] is not None: + base_mask['bias_mask'][idx] = 0. return base_mask def _cal_mean_activation(self, activations): diff --git a/src/sdk/pynni/nni/compression/torch/compressor.py b/src/sdk/pynni/nni/compression/torch/compressor.py index 107db55a0b..a61c6f5ed2 100644 --- a/src/sdk/pynni/nni/compression/torch/compressor.py +++ b/src/sdk/pynni/nni/compression/torch/compressor.py @@ -1,6 +1,7 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT license. +import types import logging import torch from . import default_layers @@ -20,12 +21,13 @@ def _setattr(model, name, module): model = getattr(model, name) setattr(model, name_list[-1], module) + class Compressor: """ Abstract base PyTorch compressor """ - def __init__(self, model, config_list): + def __init__(self, model, config_list, optimizer): """ Record necessary info in class members @@ -35,15 +37,27 @@ def __init__(self, model, config_list): the model user wants to compress config_list : list the configurations that users specify for compression + optimizer: pytorch optimizer + optimizer used to train the model """ self.bound_model = model self.config_list = config_list + self.optimizer = optimizer + self.modules_to_compress = None - self.modules_wrapper = None - self.buffers = {} + self.modules_wrapper = [] self.is_wrapped = False - def detect_modules_to_compress(self): + self._fwd_hook_handles = {} + self._fwd_hook_id = 0 + + for layer, config in self._detect_modules_to_compress(): + wrapper = self._wrap_modules(layer, config) + self.modules_wrapper.append(wrapper) + + self._wrap_model() + + def _detect_modules_to_compress(self): """ detect all modules should be compressed, and save the result in `self.modules_to_compress`. The model will be instrumented and user should never edit it after calling this method. @@ -87,26 +101,26 @@ def compress(self): torch.nn.Module model with specified modules compressed. """ - if self.modules_wrapper is not None: - # already compressed - return self.bound_model - else: - self.modules_wrapper = [] - - modules_to_compress = self.detect_modules_to_compress() - for layer, config in modules_to_compress: - wrapper = self._wrap_modules(layer, config) - self.modules_wrapper.append(wrapper) - - self._wrap_model() return self.bound_model - def register_buffer(self, name, value): + def set_wrappers_attribute(self, name, value): """ - To register buffers used in wrapped module's forward method. + To register attributes used in wrapped module's forward method. + If the type of the value is Torch.tensor, then this value is registered as a buffer in wrapper, + which will be saved by model.state_dict. Otherwise, this value is just a regular variable in wrapper. + Parameters + ---------- + name : str + name of the variable + value: any + value of the variable """ - self.buffers[name] = value + for wrapper in self.get_modules_wrapper(): + if isinstance(value, torch.Tensor): + wrapper.register_buffer(name, value.clone()) + else: + setattr(wrapper, name, value) def get_modules_to_compress(self): """ @@ -180,11 +194,7 @@ def update_epoch(self, epoch): epoch : num the current epoch number """ - - def step(self): - """ - If user want to update model every step, user can override this method - """ + pass def _wrap_modules(self, layer, config): """ @@ -200,6 +210,33 @@ def _wrap_modules(self, layer, config): raise NotImplementedError() + def add_activation_collector(self, collector): + self._fwd_hook_id += 1 + self._fwd_hook_handles[self._fwd_hook_id] = [] + for wrapper in self.get_modules_wrapper(): + handle = wrapper.register_forward_hook(collector) + self._fwd_hook_handles[self._fwd_hook_id].append(handle) + return self._fwd_hook_id + + def remove_activation_collector(self, fwd_hook_id): + if fwd_hook_id not in self._fwd_hook_handles: + raise ValueError("%s is not a valid collector id" % str(fwd_hook_id)) + for handle in self._fwd_hook_handles[fwd_hook_id]: + handle.remove() + del self._fwd_hook_handles[fwd_hook_id] + + def patch_optimizer(self, *tasks): + def patch_step(old_step): + def new_step(_, *args, **kwargs): + # call origin optimizer step method + output = old_step(*args, **kwargs) + # calculate mask + for task in tasks: + task() + return output + return new_step + self.optimizer.step = types.MethodType(patch_step(self.optimizer.step), self.optimizer) + class PrunerModuleWrapper(torch.nn.Module): def __init__(self, module, module_name, module_type, config, pruner): """ @@ -226,7 +263,6 @@ def __init__(self, module, module_name, module_type, config, pruner): # config and pruner self.config = config self.pruner = pruner - self.registered_buffers = [] # register buffer for mask self.register_buffer("weight_mask", torch.ones(self.module.weight.shape)) @@ -234,29 +270,11 @@ def __init__(self, module, module_name, module_type, config, pruner): self.register_buffer("bias_mask", torch.ones(self.module.bias.shape)) else: self.register_buffer("bias_mask", None) - self.registered_buffers.append('weight_mask') - self.registered_buffers.append('bias_mask') - # register user specified buffer - for name in self.pruner.buffers: - self.register_buffer(name, self.pruner.buffers[name].clone()) - self.registered_buffers.append(name) - - def get_registered_buffers(self): - buffers = {} - for name in self.registered_buffers: - buffers[name] = getattr(self, name) - return buffers def forward(self, *inputs): - mask = self.pruner.calc_mask(LayerInfo(self.name, self.module), self.config, **self.get_registered_buffers()) - if mask is not None: - self.weight_mask.copy_(mask['weight']) - # apply mask to weight + # apply mask to weight, bias self.module.weight.data = self.module.weight.data.mul_(self.weight_mask) - # apply mask to bias if hasattr(self.module, 'bias') and self.module.bias is not None: - if mask is not None and 'bias' in mask: - self.bias_mask.copy_(mask['bias']) self.module.bias.data = self.module.bias.data.mul_(self.bias_mask) return self.module(*inputs) @@ -272,10 +290,23 @@ class Pruner(Compressor): """ - def __init__(self, model, config_list): - super().__init__(model, config_list) + def __init__(self, model, config_list, optimizer): + super().__init__(model, config_list, optimizer) + self.patch_optimizer(self.update_mask) + + def compress(self): + self.update_mask() + return self.bound_model + + def update_mask(self): + for wrapper in self.get_modules_wrapper(): + masks = self.calc_mask(wrapper) + if masks is not None: + for k in masks: + assert hasattr(wrapper, k), "there is no attribute '%s' in wrapper" % k + setattr(wrapper, k, masks[k]) - def calc_mask(self, layer, config, **kwargs): + def calc_mask(self, wrapper, **kwargs): """ Pruners should overload this method to provide mask for weight tensors. The mask must have the same shape and type comparing to the weight. @@ -284,10 +315,8 @@ def calc_mask(self, layer, config, **kwargs): Parameters ---------- - layer : LayerInfo - calculate mask for `layer`'s weight - config : dict - the configuration for generating the mask + wrapper : Module + calculate mask for `wrapper.module`'s weight """ raise NotImplementedError("Pruners must overload calc_mask()") @@ -327,8 +356,6 @@ def export_model(self, model_path, mask_path=None, onnx_path=None, input_shape=N device of the model, used to place the dummy input tensor for exporting onnx file. the tensor is placed on cpu if ```device``` is None """ - # if self.detect_modules_to_compress() and not self.mask_dict: - # _logger.warning('You may not use self.mask_dict in base Pruner class to record masks') assert model_path is not None, 'model_path must be specified' mask_dict = {} self._unwrap_model() # used for generating correct state_dict name without wrapper state @@ -404,7 +431,6 @@ def __init__(self, module, module_name, module_type, config, quantizer): # config and pruner self.config = config self.quantizer = quantizer - self.registered_buffers = [] # register buffer and parameter # old_weight is used to store origin weight and weight is used to store quantized weight @@ -418,35 +444,18 @@ def __init__(self, module, module_name, module_type, config, quantizer): delattr(self.module, 'weight') self.module.register_buffer('weight', self.module.old_weight) - # register user specified buffer - for name in self.quantizer.buffers: - self.register_buffer(name, self.quantizer.buffers[name].clone()) - self.registered_buffers.append(name) - - def get_registered_buffers(self): - buffers = {} - for name in self.registered_buffers: - buffers[name] = getattr(self, name) - return buffers - def forward(self, *inputs): if 'input' in self.config['quant_types']: inputs = self.quantizer.quant_grad.apply( inputs, QuantType.QUANT_INPUT, - self.quantizer.quantize_input, - self.config, - LayerInfo(self.name, self.module), - **self.get_registered_buffers()) + self) if 'weight' in self.config['quant_types'] and _check_weight(self.module): new_weight = self.quantizer.quant_grad.apply( self.module.old_weight, QuantType.QUANT_WEIGHT, - self.quantizer.quantize_weight, - self.config, - LayerInfo(self.name, self.module), - **self.get_registered_buffers()) + self) self.module.weight = new_weight result = self.module(*inputs) else: @@ -456,10 +465,7 @@ def forward(self, *inputs): result = self.quantizer.quant_grad.apply( result, QuantType.QUANT_OUTPUT, - self.quantizer.quantize_output, - self.config, - LayerInfo(self.name, self.module), - **self.get_registered_buffers()) + self) return result class Quantizer(Compressor): @@ -467,11 +473,18 @@ class Quantizer(Compressor): Base quantizer for pytorch quantizer """ - def __init__(self, model, config_list): - super().__init__(model, config_list) + def __init__(self, model, config_list, optimizer=None): + super().__init__(model, config_list, optimizer) self.quant_grad = QuantGrad + if self.optimizer is not None: + self.patch_optimizer(self.step_with_optimizer) + for wrapper in self.get_modules_wrapper(): + if 'weight' in wrapper.config['quant_types']: + # old_weight is registered to keep track of weight before quantization + # and it is trainable, therefore, it should be added to optimizer. + self.optimizer.add_param_group({"params": wrapper.module.old_weight}) - def quantize_weight(self, weight, config, op, op_type, op_name): + def quantize_weight(self, weight, wrapper, **kwargs): """ quantize should overload this method to quantize weight. This method is effectively hooked to :meth:`forward` of the model. @@ -479,12 +492,12 @@ def quantize_weight(self, weight, config, op, op_type, op_name): ---------- weight : Tensor weight that needs to be quantized - config : dict - the configuration for weight quantization + wrapper : QuantizerModuleWrapper + the wrapper for origin module """ raise NotImplementedError('Quantizer must overload quantize_weight()') - def quantize_output(self, output, config, op, op_type, op_name): + def quantize_output(self, output, wrapper, **kwargs): """ quantize should overload this method to quantize output. This method is effectively hooked to :meth:`forward` of the model. @@ -492,12 +505,12 @@ def quantize_output(self, output, config, op, op_type, op_name): ---------- output : Tensor output that needs to be quantized - config : dict - the configuration for output quantization + wrapper : QuantizerModuleWrapper + the wrapper for origin module """ raise NotImplementedError('Quantizer must overload quantize_output()') - def quantize_input(self, *inputs, config, op, op_type, op_name): + def quantize_input(self, *inputs, wrapper, **kwargs): """ quantize should overload this method to quantize input. This method is effectively hooked to :meth:`forward` of the model. @@ -505,8 +518,8 @@ def quantize_input(self, *inputs, config, op, op_type, op_name): ---------- inputs : Tensor inputs that needs to be quantized - config : dict - the configuration for inputs quantization + wrapper : QuantizerModuleWrapper + the wrapper for origin module """ raise NotImplementedError('Quantizer must overload quantize_input()') @@ -532,6 +545,9 @@ def _wrap_modules(self, layer, config): return QuantizerModuleWrapper(layer.module, layer.name, layer.type, config, self) + def step_with_optimizer(self): + pass + class QuantType: """ Enum class for quantization type. @@ -540,6 +556,7 @@ class QuantType: QUANT_WEIGHT = 1 QUANT_OUTPUT = 2 + class QuantGrad(torch.autograd.Function): """ Base class for overriding backward function of quantization operation. @@ -566,15 +583,22 @@ def quant_backward(tensor, grad_output, quant_type): return grad_output @staticmethod - def forward(ctx, tensor, quant_type, quant_func, config, layer, **kwargs): + def forward(ctx, tensor, quant_type, wrapper, **kwargs): ctx.save_for_backward(tensor, torch.Tensor([quant_type])) - return quant_func(tensor, config, op=layer.module, op_type=layer.type, op_name=layer.name, **kwargs) + if quant_type == QuantType.QUANT_INPUT: + return wrapper.quantizer.quantize_input(tensor, wrapper, **kwargs) + elif quant_type == QuantType.QUANT_WEIGHT: + return wrapper.quantizer.quantize_weight(tensor, wrapper, **kwargs) + elif quant_type == QuantType.QUANT_OUTPUT: + return wrapper.quantizer.quantize_output(tensor, wrapper, **kwargs) + else: + raise ValueError("unrecognized QuantType.") @classmethod def backward(cls, ctx, grad_output): tensor, quant_type = ctx.saved_variables output = cls.quant_backward(tensor, grad_output, quant_type) - return output, None, None, None, None, None + return output, None, None, None def _check_weight(module): try: diff --git a/src/sdk/pynni/nni/compression/torch/pruners.py b/src/sdk/pynni/nni/compression/torch/pruners.py index f186fb6917..4a186349b1 100644 --- a/src/sdk/pynni/nni/compression/torch/pruners.py +++ b/src/sdk/pynni/nni/compression/torch/pruners.py @@ -16,7 +16,7 @@ class LevelPruner(Pruner): Prune to an exact pruning level specification """ - def __init__(self, model, config_list): + def __init__(self, model, config_list, optimizer): """ Parameters ---------- @@ -26,36 +26,35 @@ def __init__(self, model, config_list): List on pruning configs """ - super().__init__(model, config_list) - self.register_buffer("if_calculated", torch.tensor(0)) # pylint: disable=not-callable + super().__init__(model, config_list, optimizer) + self.set_wrappers_attribute("if_calculated", False) - def calc_mask(self, layer, config, **kwargs): + def calc_mask(self, wrapper, **kwargs): """ Calculate the mask of given layer Parameters ---------- - layer : LayerInfo - the layer to instrument the compression operation - config : dict - layer's pruning config + wrapper : Module + the module to instrument the compression operation + Returns ------- dict dictionary for storing masks """ - weight = layer.module.weight.data - if_calculated = kwargs["if_calculated"] + config = wrapper.config + weight = wrapper.module.weight.data - if not if_calculated: + if not wrapper.if_calculated: w_abs = weight.abs() k = int(weight.numel() * config['sparsity']) if k == 0: return torch.ones(weight.shape).type_as(weight) threshold = torch.topk(w_abs.view(-1), k, largest=False)[0].max() mask_weight = torch.gt(w_abs, threshold).type_as(weight) - mask = {'weight': mask_weight} - if_calculated.copy_(torch.tensor(1)) # pylint: disable=not-callable + mask = {'weight_mask': mask_weight} + wrapper.if_calculated = True return mask else: return None @@ -71,7 +70,7 @@ class AGP_Pruner(Pruner): https://arxiv.org/pdf/1710.01878.pdf """ - def __init__(self, model, config_list): + def __init__(self, model, config_list, optimizer): """ Parameters ---------- @@ -81,48 +80,45 @@ def __init__(self, model, config_list): List on pruning configs """ - super().__init__(model, config_list) + super().__init__(model, config_list, optimizer) self.now_epoch = 0 - self.register_buffer("if_calculated", torch.tensor(0)) # pylint: disable=not-callable + self.set_wrappers_attribute("if_calculated", False) - def calc_mask(self, layer, config, **kwargs): + def calc_mask(self, wrapper, **kwargs): """ Calculate the mask of given layer. Scale factors with the smallest absolute value in the BN layer are masked. Parameters ---------- - layer : LayerInfo + wrapper : Module the layer to instrument the compression operation - config : dict - layer's pruning config - kwargs: dict - buffers registered in __init__ function + Returns ------- dict dictionary for storing masks """ - weight = layer.module.weight.data + config = wrapper.config + weight = wrapper.module.weight.data start_epoch = config.get('start_epoch', 0) freq = config.get('frequency', 1) - if_calculated = kwargs["if_calculated"] - if if_calculated: + if wrapper.if_calculated: return None if not (self.now_epoch >= start_epoch and (self.now_epoch - start_epoch) % freq == 0): return None - mask = {'weight': kwargs['weight_mask'] if 'weight_mask' in kwargs else torch.ones(weight.shape).type_as(weight)} + mask = {'weight_mask': wrapper.weight_mask} target_sparsity = self.compute_target_sparsity(config) k = int(weight.numel() * target_sparsity) if k == 0 or target_sparsity >= 1 or target_sparsity <= 0: return mask # if we want to generate new mask, we should update weigth first - w_abs = weight.abs() * mask['weight'] + w_abs = weight.abs() * mask['weight_mask'] threshold = torch.topk(w_abs.view(-1), k, largest=False)[0].max() - new_mask = {'weight': torch.gt(w_abs, threshold).type_as(weight)} - if_calculated.copy_(torch.tensor(1)) # pylint: disable=not-callable + new_mask = {'weight_mask': torch.gt(w_abs, threshold).type_as(weight)} + wrapper.if_calculated = True return new_mask @@ -180,7 +176,7 @@ class SlimPruner(Pruner): https://arxiv.org/pdf/1708.06519.pdf """ - def __init__(self, model, config_list): + def __init__(self, model, config_list, optimizer): """ Parameters ---------- @@ -189,53 +185,51 @@ def __init__(self, model, config_list): - sparsity: percentage of convolutional filters to be pruned. """ - super().__init__(model, config_list) + super().__init__(model, config_list, optimizer) weight_list = [] if len(config_list) > 1: logger.warning('Slim pruner only supports 1 configuration') config = config_list[0] - for (layer, config) in self.detect_modules_to_compress(): + for (layer, config) in self.get_modules_to_compress(): assert layer.type == 'BatchNorm2d', 'SlimPruner only supports 2d batch normalization layer pruning' weight_list.append(layer.module.weight.data.abs().clone()) all_bn_weights = torch.cat(weight_list) k = int(all_bn_weights.shape[0] * config['sparsity']) self.global_threshold = torch.topk(all_bn_weights.view(-1), k, largest=False)[0].max() - self.register_buffer("if_calculated", torch.tensor(0)) # pylint: disable=not-callable + self.set_wrappers_attribute("if_calculated", False) - def calc_mask(self, layer, config, **kwargs): + def calc_mask(self, wrapper, **kwargs): """ Calculate the mask of given layer. Scale factors with the smallest absolute value in the BN layer are masked. Parameters ---------- - layer : LayerInfo + wrapper : Module the layer to instrument the compression operation - config : dict - layer's pruning config - kwargs: dict - buffers registered in __init__ function + Returns ------- dict dictionary for storing masks """ - weight = layer.module.weight.data - op_type = layer.type - if_calculated = kwargs["if_calculated"] + config = wrapper.config + weight = wrapper.module.weight.data + op_type = wrapper.type + assert op_type == 'BatchNorm2d', 'SlimPruner only supports 2d batch normalization layer pruning' - if if_calculated: + if wrapper.if_calculated: return None base_mask = torch.ones(weight.size()).type_as(weight).detach() - mask = {'weight': base_mask.detach(), 'bias': base_mask.clone().detach()} + mask = {'weight_mask': base_mask.detach(), 'bias_mask': base_mask.clone().detach()} filters = weight.size(0) num_prune = int(filters * config.get('sparsity')) if filters >= 2 and num_prune >= 1: w_abs = weight.abs() mask_weight = torch.gt(w_abs, self.global_threshold).type_as(weight) mask_bias = mask_weight.clone() - mask = {'weight': mask_weight.detach(), 'bias': mask_bias.detach()} - if_calculated.copy_(torch.tensor(1)) # pylint: disable=not-callable + mask = {'weight_mask': mask_weight.detach(), 'bias_mask': mask_bias.detach()} + wrapper.if_calculated = True return mask class LotteryTicketPruner(Pruner): @@ -267,7 +261,7 @@ def __init__(self, model, config_list, optimizer, lr_scheduler=None, reset_weigh reset_weights : bool Whether reset weights and optimizer at the beginning of each round. """ - super().__init__(model, config_list) + super().__init__(model, config_list, optimizer) self.curr_prune_iteration = None self.prune_iterations = self._validate_config(config_list) @@ -307,20 +301,16 @@ def _calc_mask(self, weight, sparsity, curr_w_mask): k = int(w_abs.numel() * curr_sparsity) threshold = torch.topk(w_abs.view(-1), k, largest=False).values.max() mask = torch.gt(w_abs, threshold).type_as(weight) - return {'weight': mask} + return {'weight_mask': mask} - def calc_mask(self, layer, config, **kwargs): + def calc_mask(self, wrapper, **kwargs): """ Generate mask for the given ``weight``. Parameters ---------- - layer : LayerInfo + wrapper : Module The layer to be pruned - config : dict - Pruning configurations for this weight - kwargs : dict - Auxiliary information Returns ------- @@ -355,7 +345,7 @@ def prune_iteration_start(self): assert self.curr_prune_iteration < self.prune_iterations + 1, 'Exceed the configured prune_iterations' modules_wrapper = self.get_modules_wrapper() - modules_to_compress = self.detect_modules_to_compress() + modules_to_compress = self.get_modules_to_compress() for layer, config in modules_to_compress: module_wrapper = None for wrapper in modules_wrapper: @@ -367,7 +357,7 @@ def prune_iteration_start(self): sparsity = config.get('sparsity') mask = self._calc_mask(layer.module.weight.data, sparsity, module_wrapper.weight_mask) # TODO: directly use weight_mask is not good - module_wrapper.weight_mask.copy_(mask['weight']) + module_wrapper.weight_mask = mask['weight_mask'] # there is no mask for bias # reinit weights back to original after new masks are generated diff --git a/src/sdk/pynni/nni/compression/torch/quantizers.py b/src/sdk/pynni/nni/compression/torch/quantizers.py index 09f88ee40e..8883cc646f 100644 --- a/src/sdk/pynni/nni/compression/torch/quantizers.py +++ b/src/sdk/pynni/nni/compression/torch/quantizers.py @@ -13,14 +13,14 @@ class NaiveQuantizer(Quantizer): """quantize weight to 8 bits """ - def __init__(self, model, config_list): - super().__init__(model, config_list) + def __init__(self, model, config_list, optimizer=None): + super().__init__(model, config_list, optimizer) self.layer_scale = {} - def quantize_weight(self, weight, config, op_name, **kwargs): + def quantize_weight(self, weight, wrapper, **kwargs): new_scale = weight.abs().max() / 127 - scale = max(self.layer_scale.get(op_name, 0), new_scale) - self.layer_scale[op_name] = scale + scale = max(self.layer_scale.get(wrapper.name, 0), new_scale) + self.layer_scale[wrapper.name] = scale orig_type = weight.type() # TODO: user layer return weight.div(scale).type(torch.int8).type(orig_type).mul(scale) @@ -104,7 +104,7 @@ class QAT_Quantizer(Quantizer): Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference http://openaccess.thecvf.com/content_cvpr_2018/papers/Jacob_Quantization_and_Training_CVPR_2018_paper.pdf """ - def __init__(self, model, config_list): + def __init__(self, model, config_list, optimizer=None): """ Parameters ---------- @@ -124,9 +124,9 @@ def __init__(self, model, config_list): - op_types : list of string types of nn.module you want to apply quantization, eg. 'Conv2d' """ - super().__init__(model, config_list) + super().__init__(model, config_list, optimizer) self.steps = 1 - modules_to_compress = self.detect_modules_to_compress() + modules_to_compress = self.get_modules_to_compress() for layer, config in modules_to_compress: layer.module.register_buffer("zero_point", None) layer.module.register_buffer("scale", None) @@ -181,7 +181,9 @@ def _dequantize(self, op, quantized_val): real_val = op.scale * (quantized_val - op.zero_point) return real_val - def quantize_weight(self, weight, config, op, **kwargs): + def quantize_weight(self, weight, wrapper, **kwargs): + config = wrapper.config + module = wrapper.module weight_bits = get_bits_length(config, 'weight') quant_start_step = config.get('quant_start_step', 0) assert weight_bits >= 1, "quant bits length should be at least 1" @@ -189,12 +191,14 @@ def quantize_weight(self, weight, config, op, **kwargs): if quant_start_step > self.steps: return weight rmin, rmax = torch.min(weight), torch.max(weight) - op.scale, op.zero_point = update_quantization_param(weight_bits, rmin, rmax) - out = self._quantize(weight_bits, op, weight) - out = self._dequantize(op, out) + module.scale, module.zero_point = update_quantization_param(weight_bits, rmin, rmax) + out = self._quantize(weight_bits, module, weight) + out = self._dequantize(module, out) return out - def quantize_output(self, output, config, op, **kwargs): + def quantize_output(self, output, wrapper, **kwargs): + config = wrapper.config + module = wrapper.module output_bits = get_bits_length(config, 'output') quant_start_step = config.get('quant_start_step', 0) assert output_bits >= 1, "quant bits length should be at least 1" @@ -203,18 +207,18 @@ def quantize_output(self, output, config, op, **kwargs): return output current_min, current_max = torch.min(output), torch.max(output) - op.tracked_min_biased, op.tracked_min = update_ema(op.tracked_min_biased, current_min, op.ema_decay, self.steps) - op.tracked_max_biased, op.tracked_max = update_ema(op.tracked_max_biased, current_max, op.ema_decay, self.steps) - op.scale, op.zero_point = update_quantization_param(output_bits, op.tracked_min, op.tracked_max) - out = self._quantize(output_bits, op, output) - out = self._dequantize(op, out) + module.tracked_min_biased, module.tracked_min = update_ema(module.tracked_min_biased, current_min, module.ema_decay, self.steps) + module.tracked_max_biased, module.tracked_max = update_ema(module.tracked_max_biased, current_max, module.ema_decay, self.steps) + module.scale, module.zero_point = update_quantization_param(output_bits, module.tracked_min, module.tracked_max) + out = self._quantize(output_bits, module, output) + out = self._dequantize(module, out) return out def fold_bn(self, config, **kwargs): # TODO simulate folded weight pass - def step(self): + def step_with_optimizer(self): """ override `compressor` `step` method, quantization only happens after certain number of steps """ @@ -226,11 +230,11 @@ class DoReFaQuantizer(Quantizer): Zhou et al., DoReFa-Net: Training Low Bitwidth Convolutional Neural Networks with Low Bitwidth Gradients (https://arxiv.org/abs/1606.06160) """ - def __init__(self, model, config_list): - super().__init__(model, config_list) + def __init__(self, model, config_list, optimizer=None): + super().__init__(model, config_list, optimizer) - def quantize_weight(self, weight, config, **kwargs): - weight_bits = get_bits_length(config, 'weight') + def quantize_weight(self, weight, wrapper, **kwargs): + weight_bits = get_bits_length(wrapper.config, 'weight') out = weight.tanh() out = out / (2 * out.abs().max()) + 0.5 out = self.quantize(out, weight_bits) @@ -256,17 +260,17 @@ class BNNQuantizer(Quantizer): Binarized Neural Networks: Training Deep Neural Networks with Weights and Activations Constrained to +1 or -1 (https://arxiv.org/abs/1602.02830) """ - def __init__(self, model, config_list): - super().__init__(model, config_list) + def __init__(self, model, config_list, optimizer=None): + super().__init__(model, config_list, optimizer) self.quant_grad = ClipGrad - def quantize_weight(self, weight, config, **kwargs): + def quantize_weight(self, weight, wrapper, **kwargs): out = torch.sign(weight) # remove zeros out[out == 0] = 1 return out - def quantize_output(self, output, config, **kwargs): + def quantize_output(self, output, wrapper, **kwargs): out = torch.sign(output) # remove zeros out[out == 0] = 1 diff --git a/src/sdk/pynni/nni/compression/torch/weight_rank_filter_pruners.py b/src/sdk/pynni/nni/compression/torch/weight_rank_filter_pruners.py index 7357567def..982b719626 100644 --- a/src/sdk/pynni/nni/compression/torch/weight_rank_filter_pruners.py +++ b/src/sdk/pynni/nni/compression/torch/weight_rank_filter_pruners.py @@ -15,7 +15,7 @@ class WeightRankFilterPruner(Pruner): importance criterion in convolution layers to achieve a preset level of network sparsity. """ - def __init__(self, model, config_list): + def __init__(self, model, config_list, optimizer): """ Parameters ---------- @@ -26,13 +26,13 @@ def __init__(self, model, config_list): - sparsity: percentage of convolutional filters to be pruned. """ - super().__init__(model, config_list) - self.register_buffer("if_calculated", torch.tensor(0)) # pylint: disable=not-callable + super().__init__(model, config_list, optimizer) + self.set_wrappers_attribute("if_calculated", False) def get_mask(self, base_mask, weight, num_prune): raise NotImplementedError('{} get_mask is not implemented'.format(self.__class__.__name__)) - def calc_mask(self, layer, config, **kwargs): + def calc_mask(self, wrapper, **kwargs): """ Calculate the mask of given layer. Filters with the smallest importance criterion of the kernel weights are masked. @@ -48,20 +48,21 @@ def calc_mask(self, layer, config, **kwargs): dictionary for storing masks """ - weight = layer.module.weight.data - op_type = layer.type + weight = wrapper.module.weight.data + op_type = wrapper.type + config = wrapper.config assert 0 <= config.get('sparsity') < 1, "sparsity must in the range [0, 1)" assert op_type in ['Conv1d', 'Conv2d'], "only support Conv1d and Conv2d" assert op_type in config.get('op_types') - if_calculated = kwargs["if_calculated"] - if if_calculated: + + if wrapper.if_calculated: return None mask_weight = torch.ones(weight.size()).type_as(weight).detach() - if hasattr(layer.module, 'bias') and layer.module.bias is not None: - mask_bias = torch.ones(layer.module.bias.size()).type_as(layer.module.bias).detach() + if hasattr(wrapper.module, 'bias') and wrapper.module.bias is not None: + mask_bias = torch.ones(wrapper.module.bias.size()).type_as(wrapper.module.bias).detach() else: mask_bias = None - mask = {'weight': mask_weight, 'bias': mask_bias} + mask = {'weight_mask': mask_weight, 'bias_mask': mask_bias} try: filters = weight.size(0) num_prune = int(filters * config.get('sparsity')) @@ -69,7 +70,7 @@ def calc_mask(self, layer, config, **kwargs): return mask mask = self.get_mask(mask, weight, num_prune) finally: - if_calculated.copy_(torch.tensor(1)) # pylint: disable=not-callable + wrapper.if_calculated = True return mask @@ -82,7 +83,7 @@ class L1FilterPruner(WeightRankFilterPruner): https://arxiv.org/abs/1608.08710 """ - def __init__(self, model, config_list): + def __init__(self, model, config_list, optimizer): """ Parameters ---------- @@ -93,7 +94,7 @@ def __init__(self, model, config_list): - sparsity: percentage of convolutional filters to be pruned. """ - super().__init__(model, config_list) + super().__init__(model, config_list, optimizer) def get_mask(self, base_mask, weight, num_prune): """ @@ -121,7 +122,7 @@ def get_mask(self, base_mask, weight, num_prune): mask_weight = torch.gt(w_abs_structured, threshold)[:, None, None, None].expand_as(weight).type_as(weight) mask_bias = torch.gt(w_abs_structured, threshold).type_as(weight) - return {'weight': mask_weight.detach(), 'bias': mask_bias.detach()} + return {'weight_mask': mask_weight.detach(), 'bias_mask': mask_bias.detach()} class L2FilterPruner(WeightRankFilterPruner): @@ -130,7 +131,7 @@ class L2FilterPruner(WeightRankFilterPruner): smallest L2 norm of the weights. """ - def __init__(self, model, config_list): + def __init__(self, model, config_list, optimizer): """ Parameters ---------- @@ -141,7 +142,7 @@ def __init__(self, model, config_list): - sparsity: percentage of convolutional filters to be pruned. """ - super().__init__(model, config_list) + super().__init__(model, config_list, optimizer) def get_mask(self, base_mask, weight, num_prune): """ @@ -167,7 +168,7 @@ def get_mask(self, base_mask, weight, num_prune): mask_weight = torch.gt(w_l2_norm, threshold)[:, None, None, None].expand_as(weight).type_as(weight) mask_bias = torch.gt(w_l2_norm, threshold).type_as(weight) - return {'weight': mask_weight.detach(), 'bias': mask_bias.detach()} + return {'weight_mask': mask_weight.detach(), 'bias_mask': mask_bias.detach()} class FPGMPruner(WeightRankFilterPruner): @@ -177,7 +178,7 @@ class FPGMPruner(WeightRankFilterPruner): https://arxiv.org/pdf/1811.00250.pdf """ - def __init__(self, model, config_list): + def __init__(self, model, config_list, optimizer): """ Parameters ---------- @@ -187,7 +188,7 @@ def __init__(self, model, config_list): support key for each list item: - sparsity: percentage of convolutional filters to be pruned. """ - super().__init__(model, config_list) + super().__init__(model, config_list, optimizer) def get_mask(self, base_mask, weight, num_prune): """ @@ -208,9 +209,9 @@ def get_mask(self, base_mask, weight, num_prune): """ min_gm_idx = self._get_min_gm_kernel_idx(weight, num_prune) for idx in min_gm_idx: - base_mask['weight'][idx] = 0. - if base_mask['bias'] is not None: - base_mask['bias'][idx] = 0. + base_mask['weight_mask'][idx] = 0. + if base_mask['bias_mask'] is not None: + base_mask['bias_mask'][idx] = 0. return base_mask def _get_min_gm_kernel_idx(self, weight, n): @@ -258,4 +259,4 @@ def _get_distance_sum(self, weight, in_idx, out_idx): def update_epoch(self, epoch): for wrapper in self.get_modules_wrapper(): - wrapper.registered_buffers['if_calculated'].copy_(torch.tensor(0)) # pylint: disable=not-callable + wrapper.if_calculated = False diff --git a/src/sdk/pynni/tests/test_compressor.py b/src/sdk/pynni/tests/test_compressor.py index 1992c19069..84a353238c 100644 --- a/src/sdk/pynni/tests/test_compressor.py +++ b/src/sdk/pynni/tests/test_compressor.py @@ -92,8 +92,9 @@ def test_torch_quantizer_modules_detection(self): def test_torch_level_pruner(self): model = TorchModel() + optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.5) configure_list = [{'sparsity': 0.8, 'op_types': ['default']}] - torch_compressor.LevelPruner(model, configure_list).compress() + torch_compressor.LevelPruner(model, configure_list, optimizer).compress() @tf2 def test_tf_level_pruner(self): @@ -130,22 +131,24 @@ def test_torch_fpgm_pruner(self): """ model = TorchModel() - config_list = [{'sparsity': 0.2, 'op_types': ['Conv2d']}, {'sparsity': 0.6, 'op_types': ['Conv2d']}] - pruner = torch_compressor.FPGMPruner(model, config_list) + optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.5) + config_list = [{'sparsity': 0.6, 'op_types': ['Conv2d']}, {'sparsity': 0.2, 'op_types': ['Conv2d']}] + pruner = torch_compressor.FPGMPruner(model, config_list, optimizer) - model.conv2.weight.data = torch.tensor(w).float() - layer = torch_compressor.compressor.LayerInfo('conv2', model.conv2) - masks = pruner.calc_mask(layer, config_list[0], if_calculated=torch.tensor(0)) - assert all(torch.sum(masks['weight'], (1, 2, 3)).numpy() == np.array([45., 45., 45., 45., 0., 0., 45., 45., 45., 45.])) + model.conv2.module.weight.data = torch.tensor(w).float() + masks = pruner.calc_mask(model.conv2) + assert all(torch.sum(masks['weight_mask'], (1, 2, 3)).numpy() == np.array([45., 45., 45., 45., 0., 0., 45., 45., 45., 45.])) - model.conv2.weight.data = torch.tensor(w).float() - masks = pruner.calc_mask(layer, config_list[1], if_calculated=torch.tensor(0)) - assert all(torch.sum(masks['weight'], (1, 2, 3)).numpy() == np.array([45., 45., 0., 0., 0., 0., 0., 0., 45., 45.])) + model.conv2.module.weight.data = torch.tensor(w).float() + model.conv2.if_calculated = False + model.conv2.config = config_list[0] + masks = pruner.calc_mask(model.conv2) + assert all(torch.sum(masks['weight_mask'], (1, 2, 3)).numpy() == np.array([45., 45., 0., 0., 0., 0., 0., 0., 45., 45.])) @tf2 def test_tf_fpgm_pruner(self): model = get_tf_model() - config_list = [{'sparsity': 0.2, 'op_types': ['Conv2D']}, {'sparsity': 0.6, 'op_types': ['Conv2D']}] + config_list = [{'sparsity': 0.2, 'op_types': ['Conv2D']}] pruner = tf_compressor.FPGMPruner(model, config_list) weights = model.layers[2].weights @@ -157,12 +160,7 @@ def test_tf_fpgm_pruner(self): masks = masks.reshape((-1, masks.shape[-1])).transpose([1, 0]) assert all(masks.sum((1)) == np.array([45., 45., 45., 45., 0., 0., 45., 45., 45., 45.])) - - model.layers[2].set_weights([weights[0], weights[1].numpy()]) - masks = pruner.calc_mask(layer, config_list[1]).numpy() - masks = masks.reshape((-1, masks.shape[-1])).transpose([1, 0]) - assert all(masks.sum((1)) == np.array([45., 45., 0., 0., 0., 0., 0., 0., 45., 45.])) - + def test_torch_l1filter_pruner(self): """ Filters with the minimum sum of the weights' L1 norm are pruned in this paper: @@ -178,18 +176,17 @@ def test_torch_l1filter_pruner(self): w = np.array([np.zeros((3, 3, 3)), np.ones((3, 3, 3)), np.ones((3, 3, 3)) * 2, np.ones((3, 3, 3)) * 3, np.ones((3, 3, 3)) * 4]) model = TorchModel() + optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.5) config_list = [{'sparsity': 0.2, 'op_types': ['Conv2d'], 'op_names': ['conv1']}, {'sparsity': 0.6, 'op_types': ['Conv2d'], 'op_names': ['conv2']}] - pruner = torch_compressor.L1FilterPruner(model, config_list) + pruner = torch_compressor.L1FilterPruner(model, config_list, optimizer) - model.conv1.weight.data = torch.tensor(w).float() - model.conv2.weight.data = torch.tensor(w).float() - layer1 = torch_compressor.compressor.LayerInfo('conv1', model.conv1) - mask1 = pruner.calc_mask(layer1, config_list[0], if_calculated=torch.tensor(0)) - layer2 = torch_compressor.compressor.LayerInfo('conv2', model.conv2) - mask2 = pruner.calc_mask(layer2, config_list[1], if_calculated=torch.tensor(0)) - assert all(torch.sum(mask1['weight'], (1, 2, 3)).numpy() == np.array([0., 27., 27., 27., 27.])) - assert all(torch.sum(mask2['weight'], (1, 2, 3)).numpy() == np.array([0., 0., 0., 27., 27.])) + model.conv1.module.weight.data = torch.tensor(w).float() + model.conv2.module.weight.data = torch.tensor(w).float() + mask1 = pruner.calc_mask(model.conv1) + mask2 = pruner.calc_mask(model.conv2) + assert all(torch.sum(mask1['weight_mask'], (1, 2, 3)).numpy() == np.array([0., 27., 27., 27., 27.])) + assert all(torch.sum(mask2['weight_mask'], (1, 2, 3)).numpy() == np.array([0., 0., 0., 27., 27.])) def test_torch_slim_pruner(self): """ @@ -207,33 +204,32 @@ def test_torch_slim_pruner(self): """ w = np.array([0, 1, 2, 3, 4]) model = TorchModel() + optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.5) config_list = [{'sparsity': 0.2, 'op_types': ['BatchNorm2d']}] model.bn1.weight.data = torch.tensor(w).float() model.bn2.weight.data = torch.tensor(-w).float() - pruner = torch_compressor.SlimPruner(model, config_list) + pruner = torch_compressor.SlimPruner(model, config_list, optimizer) - layer1 = torch_compressor.compressor.LayerInfo('bn1', model.bn1) - mask1 = pruner.calc_mask(layer1, config_list[0], if_calculated=torch.tensor(0)) - layer2 = torch_compressor.compressor.LayerInfo('bn2', model.bn2) - mask2 = pruner.calc_mask(layer2, config_list[0], if_calculated=torch.tensor(0)) - assert all(mask1['weight'].numpy() == np.array([0., 1., 1., 1., 1.])) - assert all(mask2['weight'].numpy() == np.array([0., 1., 1., 1., 1.])) - assert all(mask1['bias'].numpy() == np.array([0., 1., 1., 1., 1.])) - assert all(mask2['bias'].numpy() == np.array([0., 1., 1., 1., 1.])) + mask1 = pruner.calc_mask(model.bn1) + mask2 = pruner.calc_mask(model.bn2) + assert all(mask1['weight_mask'].numpy() == np.array([0., 1., 1., 1., 1.])) + assert all(mask2['weight_mask'].numpy() == np.array([0., 1., 1., 1., 1.])) + assert all(mask1['bias_mask'].numpy() == np.array([0., 1., 1., 1., 1.])) + assert all(mask2['bias_mask'].numpy() == np.array([0., 1., 1., 1., 1.])) + model = TorchModel() + optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.5) config_list = [{'sparsity': 0.6, 'op_types': ['BatchNorm2d']}] model.bn1.weight.data = torch.tensor(w).float() model.bn2.weight.data = torch.tensor(w).float() - pruner = torch_compressor.SlimPruner(model, config_list) + pruner = torch_compressor.SlimPruner(model, config_list, optimizer) - layer1 = torch_compressor.compressor.LayerInfo('bn1', model.bn1) - mask1 = pruner.calc_mask(layer1, config_list[0], if_calculated=torch.tensor(0)) - layer2 = torch_compressor.compressor.LayerInfo('bn2', model.bn2) - mask2 = pruner.calc_mask(layer2, config_list[0], if_calculated=torch.tensor(0)) - assert all(mask1['weight'].numpy() == np.array([0., 0., 0., 1., 1.])) - assert all(mask2['weight'].numpy() == np.array([0., 0., 0., 1., 1.])) - assert all(mask1['bias'].numpy() == np.array([0., 0., 0., 1., 1.])) - assert all(mask2['bias'].numpy() == np.array([0., 0., 0., 1., 1.])) + mask1 = pruner.calc_mask(model.bn1) + mask2 = pruner.calc_mask(model.bn2) + assert all(mask1['weight_mask'].numpy() == np.array([0., 0., 0., 1., 1.])) + assert all(mask2['weight_mask'].numpy() == np.array([0., 0., 0., 1., 1.])) + assert all(mask1['bias_mask'].numpy() == np.array([0., 0., 0., 1., 1.])) + assert all(mask2['bias_mask'].numpy() == np.array([0., 0., 0., 1., 1.])) def test_torch_QAT_quantizer(self): model = TorchModel() @@ -254,14 +250,14 @@ def test_torch_QAT_quantizer(self): # range not including 0 eps = 1e-7 weight = torch.tensor([[1, 2], [3, 5]]).float() - quantize_weight = quantizer.quantize_weight(weight, config_list[0], model.conv2) - assert math.isclose(model.conv2.scale, 5 / 255, abs_tol=eps) - assert model.conv2.zero_point == 0 + quantize_weight = quantizer.quantize_weight(weight, model.conv2) + assert math.isclose(model.conv2.module.scale, 5 / 255, abs_tol=eps) + assert model.conv2.module.zero_point == 0 # range including 0 weight = torch.tensor([[-1, 2], [3, 5]]).float() - quantize_weight = quantizer.quantize_weight(weight, config_list[0], model.conv2) - assert math.isclose(model.conv2.scale, 6 / 255, abs_tol=eps) - assert model.conv2.zero_point in (42, 43) + quantize_weight = quantizer.quantize_weight(weight, model.conv2) + assert math.isclose(model.conv2.module.scale, 6 / 255, abs_tol=eps) + assert model.conv2.module.zero_point in (42, 43) # test ema x = torch.tensor([[-0.2, 0], [0.1, 0.2]]) @@ -269,7 +265,7 @@ def test_torch_QAT_quantizer(self): assert math.isclose(model.relu.module.tracked_min_biased, 0, abs_tol=eps) assert math.isclose(model.relu.module.tracked_max_biased, 0.002, abs_tol=eps) - quantizer.step() + quantizer.step_with_optimizer() x = torch.tensor([[0.2, 0.4], [0.6, 0.8]]) out = model.relu(x) assert math.isclose(model.relu.module.tracked_min_biased, 0.002, abs_tol=eps)