已开启
【众智】【重庆大学】【ID2934】【s3fd】NPU精度达标,提交精度验收 #16
AtomGit-Bot创建于 2022年12月1日
【众智】【重庆大学】【ID2934】【s3fd】NPU精度达标,提交精度验收 #16
已开启
从refs/pull/16/head合入到master
共 25 个文件变更+3552-1
| @@ -1 +0,0 @@ | |||
| 1 | -Subproject commit 2e2f8d8a05c8f701750b67cdf542c3089c759c44 | ||
| @@ -0,0 +1,203 @@ | |||
| 1 | +Copyright 2019 The TensorFlow Authors. All rights reserved. | ||
| 2 | + | ||
| 3 | + Apache License | ||
| 4 | + Version 2.0, January 2004 | ||
| 5 | + http://www.apache.org/licenses/ | ||
| 6 | + | ||
| 7 | + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||
| 8 | + | ||
| 9 | + 1. Definitions. | ||
| 10 | + | ||
| 11 | + "License" shall mean the terms and conditions for use, reproduction, | ||
| 12 | + and distribution as defined by Sections 1 through 9 of this document. | ||
| 13 | + | ||
| 14 | + "Licensor" shall mean the copyright owner or entity authorized by | ||
| 15 | + the copyright owner that is granting the License. | ||
| 16 | + | ||
| 17 | + "Legal Entity" shall mean the union of the acting entity and all | ||
| 18 | + other entities that control, are controlled by, or are under common | ||
| 19 | + control with that entity. For the purposes of this definition, | ||
| 20 | + "control" means (i) the power, direct or indirect, to cause the | ||
| 21 | + direction or management of such entity, whether by contract or | ||
| 22 | + otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||
| 23 | + outstanding shares, or (iii) beneficial ownership of such entity. | ||
| 24 | + | ||
| 25 | + "You" (or "Your") shall mean an individual or Legal Entity | ||
| 26 | + exercising permissions granted by this License. | ||
| 27 | + | ||
| 28 | + "Source" form shall mean the preferred form for making modifications, | ||
| 29 | + including but not limited to software source code, documentation | ||
| 30 | + source, and configuration files. | ||
| 31 | + | ||
| 32 | + "Object" form shall mean any form resulting from mechanical | ||
| 33 | + transformation or translation of a Source form, including but | ||
| 34 | + not limited to compiled object code, generated documentation, | ||
| 35 | + and conversions to other media types. | ||
| 36 | + | ||
| 37 | + "Work" shall mean the work of authorship, whether in Source or | ||
| 38 | + Object form, made available under the License, as indicated by a | ||
| 39 | + copyright notice that is included in or attached to the work | ||
| 40 | + (an example is provided in the Appendix below). | ||
| 41 | + | ||
| 42 | + "Derivative Works" shall mean any work, whether in Source or Object | ||
| 43 | + form, that is based on (or derived from) the Work and for which the | ||
| 44 | + editorial revisions, annotations, elaborations, or other modifications | ||
| 45 | + represent, as a whole, an original work of authorship. For the purposes | ||
| 46 | + of this License, Derivative Works shall not include works that remain | ||
| 47 | + separable from, or merely link (or bind by name) to the interfaces of, | ||
| 48 | + the Work and Derivative Works thereof. | ||
| 49 | + | ||
| 50 | + "Contribution" shall mean any work of authorship, including | ||
| 51 | + the original version of the Work and any modifications or additions | ||
| 52 | + to that Work or Derivative Works thereof, that is intentionally | ||
| 53 | + submitted to Licensor for inclusion in the Work by the copyright owner | ||
| 54 | + or by an individual or Legal Entity authorized to submit on behalf of | ||
| 55 | + the copyright owner. For the purposes of this definition, "submitted" | ||
| 56 | + means any form of electronic, verbal, or written communication sent | ||
| 57 | + to the Licensor or its representatives, including but not limited to | ||
| 58 | + communication on electronic mailing lists, source code control systems, | ||
| 59 | + and issue tracking systems that are managed by, or on behalf of, the | ||
| 60 | + Licensor for the purpose of discussing and improving the Work, but | ||
| 61 | + excluding communication that is conspicuously marked or otherwise | ||
| 62 | + designated in writing by the copyright owner as "Not a Contribution." | ||
| 63 | + | ||
| 64 | + "Contributor" shall mean Licensor and any individual or Legal Entity | ||
| 65 | + on behalf of whom a Contribution has been received by Licensor and | ||
| 66 | + subsequently incorporated within the Work. | ||
| 67 | + | ||
| 68 | + 2. Grant of Copyright License. Subject to the terms and conditions of | ||
| 69 | + this License, each Contributor hereby grants to You a perpetual, | ||
| 70 | + worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||
| 71 | + copyright license to reproduce, prepare Derivative Works of, | ||
| 72 | + publicly display, publicly perform, sublicense, and distribute the | ||
| 73 | + Work and such Derivative Works in Source or Object form. | ||
| 74 | + | ||
| 75 | + 3. Grant of Patent License. Subject to the terms and conditions of | ||
| 76 | + this License, each Contributor hereby grants to You a perpetual, | ||
| 77 | + worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||
| 78 | + (except as stated in this section) patent license to make, have made, | ||
| 79 | + use, offer to sell, sell, import, and otherwise transfer the Work, | ||
| 80 | + where such license applies only to those patent claims licensable | ||
| 81 | + by such Contributor that are necessarily infringed by their | ||
| 82 | + Contribution(s) alone or by combination of their Contribution(s) | ||
| 83 | + with the Work to which such Contribution(s) was submitted. If You | ||
| 84 | + institute patent litigation against any entity (including a | ||
| 85 | + cross-claim or counterclaim in a lawsuit) alleging that the Work | ||
| 86 | + or a Contribution incorporated within the Work constitutes direct | ||
| 87 | + or contributory patent infringement, then any patent licenses | ||
| 88 | + granted to You under this License for that Work shall terminate | ||
| 89 | + as of the date such litigation is filed. | ||
| 90 | + | ||
| 91 | + 4. Redistribution. You may reproduce and distribute copies of the | ||
| 92 | + Work or Derivative Works thereof in any medium, with or without | ||
| 93 | + modifications, and in Source or Object form, provided that You | ||
| 94 | + meet the following conditions: | ||
| 95 | + | ||
| 96 | + (a) You must give any other recipients of the Work or | ||
| 97 | + Derivative Works a copy of this License; and | ||
| 98 | + | ||
| 99 | + (b) You must cause any modified files to carry prominent notices | ||
| 100 | + stating that You changed the files; and | ||
| 101 | + | ||
| 102 | + (c) You must retain, in the Source form of any Derivative Works | ||
| 103 | + that You distribute, all copyright, patent, trademark, and | ||
| 104 | + attribution notices from the Source form of the Work, | ||
| 105 | + excluding those notices that do not pertain to any part of | ||
| 106 | + the Derivative Works; and | ||
| 107 | + | ||
| 108 | + (d) If the Work includes a "NOTICE" text file as part of its | ||
| 109 | + distribution, then any Derivative Works that You distribute must | ||
| 110 | + include a readable copy of the attribution notices contained | ||
| 111 | + within such NOTICE file, excluding those notices that do not | ||
| 112 | + pertain to any part of the Derivative Works, in at least one | ||
| 113 | + of the following places: within a NOTICE text file distributed | ||
| 114 | + as part of the Derivative Works; within the Source form or | ||
| 115 | + documentation, if provided along with the Derivative Works; or, | ||
| 116 | + within a display generated by the Derivative Works, if and | ||
| 117 | + wherever such third-party notices normally appear. The contents | ||
| 118 | + of the NOTICE file are for informational purposes only and | ||
| 119 | + do not modify the License. You may add Your own attribution | ||
| 120 | + notices within Derivative Works that You distribute, alongside | ||
| 121 | + or as an addendum to the NOTICE text from the Work, provided | ||
| 122 | + that such additional attribution notices cannot be construed | ||
| 123 | + as modifying the License. | ||
| 124 | + | ||
| 125 | + You may add Your own copyright statement to Your modifications and | ||
| 126 | + may provide additional or different license terms and conditions | ||
| 127 | + for use, reproduction, or distribution of Your modifications, or | ||
| 128 | + for any such Derivative Works as a whole, provided Your use, | ||
| 129 | + reproduction, and distribution of the Work otherwise complies with | ||
| 130 | + the conditions stated in this License. | ||
| 131 | + | ||
| 132 | + 5. Submission of Contributions. Unless You explicitly state otherwise, | ||
| 133 | + any Contribution intentionally submitted for inclusion in the Work | ||
| 134 | + by You to the Licensor shall be under the terms and conditions of | ||
| 135 | + this License, without any additional terms or conditions. | ||
| 136 | + Notwithstanding the above, nothing herein shall supersede or modify | ||
| 137 | + the terms of any separate license agreement you may have executed | ||
| 138 | + with Licensor regarding such Contributions. | ||
| 139 | + | ||
| 140 | + 6. Trademarks. This License does not grant permission to use the trade | ||
| 141 | + names, trademarks, service marks, or product names of the Licensor, | ||
| 142 | + except as required for reasonable and customary use in describing the | ||
| 143 | + origin of the Work and reproducing the content of the NOTICE file. | ||
| 144 | + | ||
| 145 | + 7. Disclaimer of Warranty. Unless required by applicable law or | ||
| 146 | + agreed to in writing, Licensor provides the Work (and each | ||
| 147 | + Contributor provides its Contributions) on an "AS IS" BASIS, | ||
| 148 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
| 149 | + implied, including, without limitation, any warranties or conditions | ||
| 150 | + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||
| 151 | + PARTICULAR PURPOSE. You are solely responsible for determining the | ||
| 152 | + appropriateness of using or redistributing the Work and assume any | ||
| 153 | + risks associated with Your exercise of permissions under this License. | ||
| 154 | + | ||
| 155 | + 8. Limitation of Liability. In no event and under no legal theory, | ||
| 156 | + whether in tort (including negligence), contract, or otherwise, | ||
| 157 | + unless required by applicable law (such as deliberate and grossly | ||
| 158 | + negligent acts) or agreed to in writing, shall any Contributor be | ||
| 159 | + liable to You for damages, including any direct, indirect, special, | ||
| 160 | + incidental, or consequential damages of any character arising as a | ||
| 161 | + result of this License or out of the use or inability to use the | ||
| 162 | + Work (including but not limited to damages for loss of goodwill, | ||
| 163 | + work stoppage, computer failure or malfunction, or any and all | ||
| 164 | + other commercial damages or losses), even if such Contributor | ||
| 165 | + has been advised of the possibility of such damages. | ||
| 166 | + | ||
| 167 | + 9. Accepting Warranty or Additional Liability. While redistributing | ||
| 168 | + the Work or Derivative Works thereof, You may choose to offer, | ||
| 169 | + and charge a fee for, acceptance of support, warranty, indemnity, | ||
| 170 | + or other liability obligations and/or rights consistent with this | ||
| 171 | + License. However, in accepting such obligations, You may act only | ||
| 172 | + on Your own behalf and on Your sole responsibility, not on behalf | ||
| 173 | + of any other Contributor, and only if You agree to indemnify, | ||
| 174 | + defend, and hold each Contributor harmless for any liability | ||
| 175 | + incurred by, or claims asserted against, such Contributor by reason | ||
| 176 | + of your accepting any such warranty or additional liability. | ||
| 177 | + | ||
| 178 | + END OF TERMS AND CONDITIONS | ||
| 179 | + | ||
| 180 | + APPENDIX: How to apply the Apache License to your work. | ||
| 181 | + | ||
| 182 | + To apply the Apache License to your work, attach the following | ||
| 183 | + boilerplate notice, with the fields enclosed by brackets "[]" | ||
| 184 | + replaced with your own identifying information. (Don't include | ||
| 185 | + the brackets!) The text should be enclosed in the appropriate | ||
| 186 | + comment syntax for the file format. We also recommend that a | ||
| 187 | + file or class name and description of purpose be included on the | ||
| 188 | + same "printed page" as the copyright notice for easier | ||
| 189 | + identification within third-party archives. | ||
| 190 | + | ||
| 191 | + Copyright [yyyy] [name of copyright owner] | ||
| 192 | + | ||
| 193 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 194 | + you may not use this file except in compliance with the License. | ||
| 195 | + You may obtain a copy of the License at | ||
| 196 | + | ||
| 197 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
| 198 | + | ||
| 199 | + Unless required by applicable law or agreed to in writing, software | ||
| 200 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
| 201 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 202 | + See the License for the specific language governing permissions and | ||
| 203 | + limitations under the License. | ||
| @@ -0,0 +1,317 @@ | |||
| 1 | +- [基本信息](#基本信息.md) | ||
| 2 | +- [概述](#概述.md) | ||
| 3 | +- [训练环境准备](#训练环境准备.md) | ||
| 4 | +- [快速上手](#快速上手.md) | ||
| 5 | +- [迁移学习指导](#迁移学习指导.md) | ||
| 6 | +- [高级参考](#高级参考.md) | ||
| 7 | +<h2 id="基本信息.md">基本信息</h2> | ||
| 8 | + | ||
| 9 | +**发布者(Publisher):Huawei** | ||
| 10 | + | ||
| 11 | +**应用领域(Application Domain):Object Detection** | ||
| 12 | + | ||
| 13 | +**版本(Version):1.1** | ||
| 14 | + | ||
| 15 | +**修改时间(Modified) :2022.11.30** | ||
| 16 | + | ||
| 17 | +**大小(Size):85.6MB** | ||
| 18 | + | ||
| 19 | +**框架(Framework):PyTorch 1.8** | ||
| 20 | + | ||
| 21 | +**模型格式(Model Format):pth** | ||
| 22 | + | ||
| 23 | +**精度(Precision):FP16** | ||
| 24 | + | ||
| 25 | +**处理器(Processor):昇腾910** | ||
| 26 | + | ||
| 27 | +**应用级别(Categories):Official** | ||
| 28 | + | ||
| 29 | +**描述(Description):基于PyTorch框架的人脸检测训练代码** | ||
| 30 | + | ||
| 31 | +## 概述<a name="section194554031510"></a> | ||
| 32 | + | ||
| 33 | + 针对尺度不变人脸的单阶段检测器的PyTorch实现,在小尺度目标检测方面具有良好效果。 | ||
| 34 | + | ||
| 35 | +- 参考论文: | ||
| 36 | + | ||
| 37 | + https://arxiv.org/abs/1708.05237 | ||
| 38 | + | ||
| 39 | +- 参考实现: | ||
| 40 | + | ||
| 41 | + https://github.com/sfzhang15/SFD | ||
| 42 | + | ||
| 43 | +- 适配昇腾 AI 处理器的实现: | ||
| 44 | + | ||
| 45 | + https://gitee.com/ascend/ModelZoo-PyTorch/tree/master/PyTorch/contrib/cv/s3fd_ID2934_for_PyTorch | ||
| 46 | + | ||
| 47 | + | ||
| 48 | +## 默认配置<a name="section91661242121611"></a> | ||
| 49 | + | ||
| 50 | +- 训练超参: | ||
| 51 | +- batch_size 16 | ||
| 52 | +- num_workers 4 | ||
| 53 | +- learning-rate 0.0001 | ||
| 54 | +- dataset face | ||
| 55 | +- basenet | ||
| 56 | +- save_folder | ||
| 57 | + | ||
| 58 | + | ||
| 59 | +## 支持特性<a name="section1899153513554"></a> | ||
| 60 | + | ||
| 61 | +| 特性列表 | 是否支持 | | ||
| 62 | +| ---------- | -------- | | ||
| 63 | +| 分布式训练 | 否 | | ||
| 64 | +| 混合精度 | 否 | | ||
| 65 | +| 数据并行 | 否 | | ||
| 66 | + | ||
| 67 | + | ||
| 68 | +## 混合精度训练<a name="section168064817164"></a> | ||
| 69 | + | ||
| 70 | +昇腾910 AI处理器提供自动混合精度功能,可以针对全网中float32数据类型的算子,按照内置的优化策略,自动将部分float32的算子降低精度到float16,从而在精度损失很小的情况下提升系统性能并减少内存使用。 | ||
| 71 | + | ||
| 72 | +## 开启混合精度<a name="section20779114113713"></a> | ||
| 73 | + | ||
| 74 | +基于NPU芯片的架构特性,会涉及到混合精度训练,即混合使用float16和float32数据类型的应用场景。使用float16代替float32有如下好处: | ||
| 75 | + | ||
| 76 | +- 对于中间变量的内存占用更少,节省内存的使用。 | ||
| 77 | +- 因内存使用会减少,所以数据传出的时间也会相应减少。 | ||
| 78 | +- float16的计算单元可以提供更快的计算性能。 | ||
| 79 | + | ||
| 80 | +但是,混合精度训练受限于float16表达的精度范围,单纯将float32转换成float16会影响训练收敛情况,为了保证部分计算使用float16来进行加速的同时能保证训练收敛,这里采用混合精度模块Apex来达到以上效果。混合精度模块Apex是一个集优化性能、精度收敛于一身的综合优化库。 | ||
| 81 | + | ||
| 82 | +适配昇腾AI处理器的混合精度模块Apex除了上述优点外,还能提升运算性能。具体如下: | ||
| 83 | + | ||
| 84 | +- Apex在混合精度运算过程中,会对模型的grad进行运算。开启combine\_grad开关,可以加速这些运算。具体为将amp.initialize\(\)接口参数combine\_grad设置为True; | ||
| 85 | +- 适配后的Apex针对adadelta/adam/sgd/lamb做了昇腾AI处理器亲和性优化,得到的NPU融合优化器与原生算法保持一致,但运算速度更快。使用时只需将原有优化器替换为apex.optimizers.\*(“\*”为优化器名称,例如NpuFusedSGD)。 | ||
| 86 | +- 适配后的Apex针对数据并行场景做了昇腾AI处理器亲和性优化,支持利用融合grad进行加速,同时保持计算逻辑一致性。通过开启combine\_ddp开关,也就是将amp.initialize\(\)接口参数combine\_ddp设置为True并关闭DistributedDataParallel,即可开启该功能。 | ||
| 87 | + | ||
| 88 | +**特性支持**<a name="section723462915303"></a> | ||
| 89 | + | ||
| 90 | +混合精度模块功能和优化描述如[表1](#table10717173813332)所示。 | ||
| 91 | + | ||
| 92 | +**表 1** 混合精度模块功能 | ||
| 93 | + | ||
| 94 | +<a name="table10717173813332"></a> | ||
| 95 | +<table><thead align="left"><tr id="row371716385333"><th class="cellrowborder" valign="top" width="32.269999999999996%" id="mcps1.2.3.1.1"><p id="p13717163815333"><a name="p13717163815333"></a><a name="p13717163815333"></a>功能</p> | ||
| 96 | +</th> | ||
| 97 | +<th class="cellrowborder" valign="top" width="67.73%" id="mcps1.2.3.1.2"><p id="p14400173910345"><a name="p14400173910345"></a><a name="p14400173910345"></a>描述</p> | ||
| 98 | +</th> | ||
| 99 | +</tr> | ||
| 100 | +</thead> | ||
| 101 | +<tbody><tr id="row1571763813334"><td class="cellrowborder" valign="top" width="32.269999999999996%" headers="mcps1.2.3.1.1 "><p id="p4502732153412"><a name="p4502732153412"></a><a name="p4502732153412"></a>O1配置模式</p> | ||
| 102 | +</td> | ||
| 103 | +<td class="cellrowborder" valign="top" width="67.73%" headers="mcps1.2.3.1.2 "><p id="p640053920348"><a name="p640053920348"></a><a name="p640053920348"></a>Conv,Matmul等使用float16计算,其他如Softmax、BN使用float32。</p> | ||
| 104 | +</td> | ||
| 105 | +</tr> | ||
| 106 | +<tr id="row3717173817336"><td class="cellrowborder" valign="top" width="32.269999999999996%" headers="mcps1.2.3.1.1 "><p id="p11503103210344"><a name="p11503103210344"></a><a name="p11503103210344"></a>O2配置模式</p> | ||
| 107 | +</td> | ||
| 108 | +<td class="cellrowborder" valign="top" width="67.73%" headers="mcps1.2.3.1.2 "><p id="p164001639143419"><a name="p164001639143419"></a><a name="p164001639143419"></a>除了BN使用float32外,其他绝大部分使用float16。</p> | ||
| 109 | +</td> | ||
| 110 | +</tr> | ||
| 111 | +<tr id="row14717193815334"><td class="cellrowborder" valign="top" width="32.269999999999996%" headers="mcps1.2.3.1.1 "><p id="p1950318328349"><a name="p1950318328349"></a><a name="p1950318328349"></a>静态Loss Scale功能</p> | ||
| 112 | +</td> | ||
| 113 | +<td class="cellrowborder" valign="top" width="67.73%" headers="mcps1.2.3.1.2 "><p id="p1440033983418"><a name="p1440033983418"></a><a name="p1440033983418"></a>静态设置参数确保混合精度训练收敛。</p> | ||
| 114 | +</td> | ||
| 115 | +</tr> | ||
| 116 | +<tr id="row871733813317"><td class="cellrowborder" valign="top" width="32.269999999999996%" headers="mcps1.2.3.1.1 "><p id="p1550303243417"><a name="p1550303243417"></a><a name="p1550303243417"></a>动态Loss Scale功能</p> | ||
| 117 | +</td> | ||
| 118 | +<td class="cellrowborder" valign="top" width="67.73%" headers="mcps1.2.3.1.2 "><p id="p15400143963412"><a name="p15400143963412"></a><a name="p15400143963412"></a>动态计算loss Scale值并判断是否溢出。</p> | ||
| 119 | +</td> | ||
| 120 | +</tr> | ||
| 121 | +</tbody> | ||
| 122 | +</table> | ||
| 123 | + | ||
| 124 | +>**说明:** | ||
| 125 | +>- 当前版本的实现方式主要为python实现,不支持AscendCL或者CUDA优化。 | ||
| 126 | +>- 当前昇腾AI设备暂不支持原始Apex的FusedLayerNorm接口模块,如果模型原始脚本文件使用了FusedLayerNorm接口模块,需要在模型迁移过程中将脚本头文件“from apex.normalization import FusedLayerNorm“替换为“from torch.nn import LayerNorm“。 | ||
| 127 | + | ||
| 128 | +**将混合精度模块集成到PyTorch模型中**<a name="section18578112873911"></a> | ||
| 129 | + | ||
| 130 | +1. 使用apex混合精度模块需要首先从apex库中导入amp,代码如下: | ||
| 131 | + | ||
| 132 | + ``` | ||
| 133 | + from apex import amp | ||
| 134 | + ``` | ||
| 135 | + | ||
| 136 | +2. 导入amp模块后,需要初始化amp,使其能对模型、优化器以及PyTorch内部函数进行必要的改动,初始化代码如下: | ||
| 137 | + | ||
| 138 | + ``` | ||
| 139 | + model, optimizer = amp.initialize(model,optimizer,opt_level='O2',loss_scale=32.0,combine_grad=True) | ||
| 140 | + ``` | ||
| 141 | + | ||
| 142 | +3. 标记反向传播.backward\(\)发生的位置,这样Amp就可以进行Loss Scaling并清除每次迭代的状态,代码如下: | ||
| 143 | + | ||
| 144 | + 原始代码: | ||
| 145 | + | ||
| 146 | + ``` | ||
| 147 | + loss = criterion(…) | ||
| 148 | + loss.backward() | ||
| 149 | + optimizer.step() | ||
| 150 | + ``` | ||
| 151 | + | ||
| 152 | + 修改以支持loss scaling后的代码: | ||
| 153 | + | ||
| 154 | + ``` | ||
| 155 | + loss = criterion(preds, labels) | ||
| 156 | + optimizer.zero_grad() | ||
| 157 | + # loss.backward() | ||
| 158 | + with amp.scale_loss(loss, optimizer) as scaled_loss: | ||
| 159 | + scaled_loss.backward() | ||
| 160 | + optimizer.step() | ||
| 161 | + | ||
| 162 | + ``` | ||
| 163 | + | ||
| 164 | + | ||
| 165 | +<h2 id="训练环境准备.md">训练环境准备</h2> | ||
| 166 | + | ||
| 167 | +- 硬件环境和运行环境准备请参见《[CANN软件安装指南](https://support.huawei.com/enterprise/zh/ascend-computing/cann-pid-251168373?category=installation-update)》 | ||
| 168 | +- 运行以下命令安装依赖。 | ||
| 169 | +``` | ||
| 170 | +pip3 install requirements.txt | ||
| 171 | +``` | ||
| 172 | +说明:依赖配置文件requirements.txt文件位于模型的根目录 | ||
| 173 | + | ||
| 174 | +<h2 id="快速上手.md">快速上手</h2> | ||
| 175 | + | ||
| 176 | +## 数据集准备<a name="section361114841316"></a> | ||
| 177 | + | ||
| 178 | +1. 下载WIDER face数据集 http://shuoyang1213.me/WIDERFACE | ||
| 179 | +2. 修改data/config.py中的数据集路径 | ||
| 180 | +``` | ||
| 181 | +_C.FACE.TRAIN_FILE = 'data/face_train.txt' | ||
| 182 | +_C.FACE.VAL_FILE = 'data/face_val.txt' | ||
| 183 | +_C.FACE.WIDER_DIR = '{data_path}/dataset' | ||
| 184 | +``` | ||
| 185 | +3. 执行初始化代码 | ||
| 186 | +``` | ||
| 187 | +python prepare_wider_data.py | ||
| 188 | +``` | ||
| 189 | +4. s3fd训练的模型及数据集可以参考"概述 -> 参考实现" | ||
| 190 | + | ||
| 191 | + | ||
| 192 | +## 模型训练<a name="section715881518135"></a> | ||
| 193 | + | ||
| 194 | +- 单击“立即下载”,并选择合适的下载方式下载源码包。 | ||
| 195 | +- 开始训练。 | ||
| 196 | + | ||
| 197 | + - 启动训练之前,首先要配置程序运行相关环境变量。 | ||
| 198 | + | ||
| 199 | + 环境变量配置信息参见: | ||
| 200 | + | ||
| 201 | + [Ascend 910训练平台环境变量设置](https://gitee.com/ascend/modelzoo/wikis/Ascend%20910%E8%AE%AD%E7%BB%83%E5%B9%B3%E5%8F%B0%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%E8%AE%BE%E7%BD%AE?sort_id=3148819) | ||
| 202 | + | ||
| 203 | + - 单卡训练 | ||
| 204 | + | ||
| 205 | + 1. 配置训练参数。 | ||
| 206 | + | ||
| 207 | + 数据集路径的参数配置在data/config.py中进行,见"快速上手 -> 数据集准备" | ||
| 208 | + | ||
| 209 | + | ||
| 210 | + 2. 启动训练。 | ||
| 211 | + | ||
| 212 | + 启动单卡GPU训练 (切换到code目录) | ||
| 213 | + | ||
| 214 | + ``` | ||
| 215 | + python3 train.py --device gpu | ||
| 216 | + ``` | ||
| 217 | + 启动单卡NPU训练 (切换到code目录) | ||
| 218 | + | ||
| 219 | + ``` | ||
| 220 | + python3 train.py --device npu | ||
| 221 | + ``` | ||
| 222 | + 若需要从checkpoint继续训练,则 | ||
| 223 | + ``` | ||
| 224 | + python3 train.py --device {device} --resume {checkpoint_path} | ||
| 225 | + ``` | ||
| 226 | + 3. 精度指标。 | ||
| 227 | + | ||
| 228 | + GPU训练loss如下: | ||
| 229 | + | ||
| 230 | +  | ||
| 231 | + NPU训练loss如下: | ||
| 232 | + | ||
| 233 | +  | ||
| 234 | + | ||
| 235 | + 同步数下loss对齐 | ||
| 236 | + | ||
| 237 | + | ||
| 238 | + | ||
| 239 | + | ||
| 240 | + | ||
| 241 | +<h2 id="高级参考.md">高级参考</h2> | ||
| 242 | + | ||
| 243 | +## 脚本和示例代码 | ||
| 244 | + | ||
| 245 | +``` | ||
| 246 | +├── data | ||
| 247 | +│ ├──__init__.py | ||
| 248 | +│ ├──config.py | ||
| 249 | +│ ├──factory.py | ||
| 250 | +│ ├──widerface.py | ||
| 251 | +├── layers | ||
| 252 | +│ ├── functions | ||
| 253 | +│ │ ├── __init__.py | ||
| 254 | +│ │ ├── detection.py | ||
| 255 | +│ │ ├── prior_box.py | ||
| 256 | +│ ├── modules | ||
| 257 | +│ │ ├── __init__.py | ||
| 258 | +│ │ ├── l2norm.py | ||
| 259 | +│ │ ├── multibox_loss.py | ||
| 260 | +│ ├── __init__.py | ||
| 261 | +│ ├── bbox_utils.py | ||
| 262 | +├── utils //图像数据处理代码 | ||
| 263 | +│ ├── __init__.py | ||
| 264 | +│ ├── augmentations.py | ||
| 265 | +├── weights //权重文件,模型默认保存在此目录 | ||
| 266 | +│ ├── vgg16_reducedfc.pth //basenet | ||
| 267 | +├── img.png //readme所用图片 | ||
| 268 | +├── img1.png //readme所用图片 | ||
| 269 | +├── img2.png //readme所用图片 | ||
| 270 | +├── prepare_wider_data.py //数据集预处理 | ||
| 271 | +├── README.md //代码说明文档 | ||
| 272 | +├── requiremnets.txt //训练python依赖列表 | ||
| 273 | +├── s3fd.py //模型 | ||
| 274 | +├── train.py //网络训练代码 | ||
| 275 | +``` | ||
| 276 | + | ||
| 277 | +## 训练过程<a name="section1589455252218"></a> | ||
| 278 | + | ||
| 279 | +训练日志中包括如下信息。 | ||
| 280 | +``` | ||
| 281 | +Timer: 1.1087 | ||
| 282 | +epoch:5 || iter:4750 || Loss:5.8974 | ||
| 283 | +->> conf loss:1.1982 || loc loss:4.1487 | ||
| 284 | +->>lr:0.001000 | ||
| 285 | +Timer: 3.5912 | ||
| 286 | +epoch:5 || iter:4760 || Loss:5.8922 | ||
| 287 | +->> conf loss:1.3858 || loc loss:4.1821 | ||
| 288 | +->>lr:0.001000 | ||
| 289 | +Timer: 1.0676 | ||
| 290 | +epoch:5 || iter:4770 || Loss:5.8903 | ||
| 291 | +->> conf loss:1.2478 || loc loss:4.2127 | ||
| 292 | +->>lr:0.001000 | ||
| 293 | +Timer: 0.9416 | ||
| 294 | +epoch:5 || iter:4780 || Loss:5.8892 | ||
| 295 | +->> conf loss:0.8212 || loc loss:4.6874 | ||
| 296 | +->>lr:0.001000 | ||
| 297 | +Timer: 1.2547 | ||
| 298 | +epoch:5 || iter:4790 || Loss:5.8877 | ||
| 299 | +->> conf loss:1.4334 || loc loss:4.4520 | ||
| 300 | +->>lr:0.001000 | ||
| 301 | +Timer: 6.2712 | ||
| 302 | +epoch:5 || iter:4800 || Loss:5.8843 | ||
| 303 | +->> conf loss:1.5964 || loc loss:4.1946 | ||
| 304 | +->>lr:0.001000 | ||
| 305 | +Timer: 6.0671 | ||
| 306 | +epoch:5 || iter:4810 || Loss:5.8823 | ||
| 307 | +->> conf loss:1.7890 || loc loss:4.8760 | ||
| 308 | +->>lr:0.001000 | ||
| 309 | +Timer: 46.3256 | ||
| 310 | +epoch:5 || iter:4820 || Loss:5.8848 | ||
| 311 | +->> conf loss:2.5106 || loc loss:4.4186 | ||
| 312 | +->>lr:0.001000 | ||
| 313 | +Timer: 392.5459 | ||
| 314 | +test epoch:5 || Loss:6.2771 | ||
| 315 | +Saving best state,epoch 5 | ||
| 316 | +``` | ||
| 317 | + | ||
The file is empty
| @@ -0,0 +1,91 @@ | |||
| 1 | +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. | ||
| 2 | +# | ||
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | +# you may not use this file except in compliance with the License. | ||
| 5 | +# You may obtain a copy of the License at | ||
| 6 | +# | ||
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | +# | ||
| 9 | +# Unless required by applicable law or agreed to in writing, software | ||
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | +# See the License for the specific language governing permissions and | ||
| 13 | +# limitations under the License. | ||
| 14 | +# ============================================================================ | ||
| 15 | +# Copyright 2021 Huawei Technologies Co., Ltd | ||
| 16 | +# | ||
| 17 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 18 | +# you may not use this file except in compliance with the License. | ||
| 19 | +# You may obtain a copy of the License at | ||
| 20 | +# | ||
| 21 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 22 | +# | ||
| 23 | +# Unless required by applicable law or agreed to in writing, software | ||
| 24 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 25 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 26 | +# See the License for the specific language governing permissions and | ||
| 27 | +# limitations under the License. | ||
| 28 | + | ||
| 29 | +from __future__ import division | ||
| 30 | +from __future__ import absolute_import | ||
| 31 | +from __future__ import print_function | ||
| 32 | + | ||
| 33 | +from easydict import EasyDict | ||
| 34 | +import numpy as np | ||
| 35 | + | ||
| 36 | +_C = EasyDict() | ||
| 37 | +cfg = _C | ||
| 38 | +# data augument config | ||
| 39 | +_C.expand_prob = 0.5 | ||
| 40 | +_C.expand_max_ratio = 4 | ||
| 41 | +_C.hue_prob = 0.5 | ||
| 42 | +_C.hue_delta = 18 | ||
| 43 | +_C.contrast_prob = 0.5 | ||
| 44 | +_C.contrast_delta = 0.5 | ||
| 45 | +_C.saturation_prob = 0.5 | ||
| 46 | +_C.saturation_delta = 0.5 | ||
| 47 | +_C.brightness_prob = 0.5 | ||
| 48 | +_C.brightness_delta = 0.125 | ||
| 49 | +_C.data_anchor_sampling_prob = 0.5 | ||
| 50 | +_C.min_face_size = 6.0 | ||
| 51 | +_C.apply_distort = True | ||
| 52 | +_C.apply_expand = False | ||
| 53 | +_C.img_mean = np.array([104., 117., 123.])[:, np.newaxis, np.newaxis].astype( | ||
| 54 | + 'float32') | ||
| 55 | +_C.resize_width = 640 | ||
| 56 | +_C.resize_height = 640 | ||
| 57 | +_C.scale = 1 / 127.0 | ||
| 58 | +_C.anchor_sampling = True | ||
| 59 | +_C.filter_min_face = True | ||
| 60 | + | ||
| 61 | +# train config | ||
| 62 | +# _C.LR_STEPS = (120, 198, 250) | ||
| 63 | +_C.MAX_STEPS = 200000 | ||
| 64 | +_C.LR_STEPS = (80000, 100000, 120000) | ||
| 65 | +_C.EPOCHES = 300 | ||
| 66 | + | ||
| 67 | +# anchor config | ||
| 68 | +_C.FEATURE_MAPS = [160, 80, 40, 20, 10, 5] | ||
| 69 | +_C.INPUT_SIZE = 640 | ||
| 70 | +_C.STEPS = [4, 8, 16, 32, 64, 128] | ||
| 71 | +_C.ANCHOR_SIZES = [16, 32, 64, 128, 256, 512] | ||
| 72 | +_C.CLIP = False | ||
| 73 | +_C.VARIANCE = [0.1, 0.2] | ||
| 74 | + | ||
| 75 | +# detection config | ||
| 76 | +_C.NMS_THRESH = 0.3 | ||
| 77 | +_C.NMS_TOP_K = 5000 | ||
| 78 | +_C.TOP_K = 750 | ||
| 79 | +_C.CONF_THRESH = 0.05 | ||
| 80 | + | ||
| 81 | +# loss config | ||
| 82 | +_C.NEG_POS_RATIOS = 3 | ||
| 83 | +_C.NUM_CLASSES = 2 | ||
| 84 | +_C.USE_NMS = True | ||
| 85 | + | ||
| 86 | +# dataset config | ||
| 87 | +_C.FACE = EasyDict() | ||
| 88 | +_C.FACE.TRAIN_FILE = 'data/face_train.txt' | ||
| 89 | +_C.FACE.VAL_FILE = 'data/face_val.txt' | ||
| 90 | +_C.FACE.WIDER_DIR = '/cache/dataset' # 运行wider_test时要修改 | ||
| 91 | +_C.FACE.OVERLAP_THRESH = [0.1, 0.35, 0.5] | ||
| @@ -0,0 +1,67 @@ | |||
| 1 | +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. | ||
| 2 | +# | ||
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | +# you may not use this file except in compliance with the License. | ||
| 5 | +# You may obtain a copy of the License at | ||
| 6 | +# | ||
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | +# | ||
| 9 | +# Unless required by applicable law or agreed to in writing, software | ||
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | +# See the License for the specific language governing permissions and | ||
| 13 | +# limitations under the License. | ||
| 14 | +# ============================================================================ | ||
| 15 | +# Copyright 2021 Huawei Technologies Co., Ltd | ||
| 16 | +# | ||
| 17 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 18 | +# you may not use this file except in compliance with the License. | ||
| 19 | +# You may obtain a copy of the License at | ||
| 20 | +# | ||
| 21 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 22 | +# | ||
| 23 | +# Unless required by applicable law or agreed to in writing, software | ||
| 24 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 25 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 26 | +# See the License for the specific language governing permissions and | ||
| 27 | +# limitations under the License. | ||
| 28 | + | ||
| 29 | +from __future__ import division | ||
| 30 | +from __future__ import absolute_import | ||
| 31 | +from __future__ import print_function | ||
| 32 | + | ||
| 33 | +from .widerface import WIDERDetection | ||
| 34 | +from .config import cfg | ||
| 35 | + | ||
| 36 | +import torch | ||
| 37 | + | ||
| 38 | + | ||
| 39 | +def dataset_factory(dataset): | ||
| 40 | + """ | ||
| 41 | + dataset_factory | ||
| 42 | + """ | ||
| 43 | + if dataset == 'face': | ||
| 44 | + train_dataset = WIDERDetection(cfg.FACE.TRAIN_FILE, mode='train') | ||
| 45 | + val_dataset = WIDERDetection(cfg.FACE.VAL_FILE, mode='val') | ||
| 46 | + return train_dataset, val_dataset | ||
| 47 | + | ||
| 48 | + | ||
| 49 | +def detection_collate(batch): | ||
| 50 | + """Custom collate fn for dealing with batches of images that have a different | ||
| 51 | + number of associated object annotations (bounding boxes). | ||
| 52 | + | ||
| 53 | + Arguments: | ||
| 54 | + batch: (tuple) A tuple of tensor images and lists of annotations | ||
| 55 | + | ||
| 56 | + Return: | ||
| 57 | + A tuple containing: | ||
| 58 | + 1) (tensor) batch of images stacked on their 0 dim | ||
| 59 | + 2) (list of tensors) annotations for a given image are stacked on | ||
| 60 | + 0 dim | ||
| 61 | + """ | ||
| 62 | + targets = [] | ||
| 63 | + imgs = [] | ||
| 64 | + for sample in batch: | ||
| 65 | + imgs.append(sample[0]) | ||
| 66 | + targets.append(torch.FloatTensor(sample[1])) | ||
| 67 | + return torch.stack(imgs, 0), targets | ||
| @@ -0,0 +1,162 @@ | |||
| 1 | +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. | ||
| 2 | +# | ||
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | +# you may not use this file except in compliance with the License. | ||
| 5 | +# You may obtain a copy of the License at | ||
| 6 | +# | ||
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | +# | ||
| 9 | +# Unless required by applicable law or agreed to in writing, software | ||
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | +# See the License for the specific language governing permissions and | ||
| 13 | +# limitations under the License. | ||
| 14 | +# ============================================================================ | ||
| 15 | +# Copyright 2021 Huawei Technologies Co., Ltd | ||
| 16 | +# | ||
| 17 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 18 | +# you may not use this file except in compliance with the License. | ||
| 19 | +# You may obtain a copy of the License at | ||
| 20 | +# | ||
| 21 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 22 | +# | ||
| 23 | +# Unless required by applicable law or agreed to in writing, software | ||
| 24 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 25 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 26 | +# See the License for the specific language governing permissions and | ||
| 27 | +# limitations under the License. | ||
| 28 | + | ||
| 29 | +from __future__ import absolute_import | ||
| 30 | +from __future__ import division | ||
| 31 | +from __future__ import print_function | ||
| 32 | + | ||
| 33 | +import torch | ||
| 34 | +from PIL import Image | ||
| 35 | +import torch.utils.data as data | ||
| 36 | +import numpy as np | ||
| 37 | +import random | ||
| 38 | +from utils.augmentations import preprocess | ||
| 39 | + | ||
| 40 | + | ||
| 41 | +class WIDERDetection(data.Dataset): | ||
| 42 | + """docstring for WIDERDetection""" | ||
| 43 | + | ||
| 44 | + def __init__(self, list_file, mode='train'): | ||
| 45 | + super(WIDERDetection, self).__init__() | ||
| 46 | + self.mode = mode | ||
| 47 | + self.fnames = [] | ||
| 48 | + self.boxes = [] | ||
| 49 | + self.labels = [] | ||
| 50 | + | ||
| 51 | + with open(list_file) as f: | ||
| 52 | + lines = f.readlines() | ||
| 53 | + | ||
| 54 | + for line in lines: | ||
| 55 | + line = line.strip().split() | ||
| 56 | + num_faces = int(line[1]) | ||
| 57 | + box = [] | ||
| 58 | + label = [] | ||
| 59 | + for i in range(num_faces): | ||
| 60 | + x = float(line[2 + 5 * i]) | ||
| 61 | + y = float(line[3 + 5 * i]) | ||
| 62 | + w = float(line[4 + 5 * i]) | ||
| 63 | + h = float(line[5 + 5 * i]) | ||
| 64 | + c = int(line[6 + 5 * i]) | ||
| 65 | + if w <= 0 or h <= 0: | ||
| 66 | + continue | ||
| 67 | + box.append([x, y, x + w, y + h]) | ||
| 68 | + label.append(c) | ||
| 69 | + if len(box) > 0: | ||
| 70 | + self.fnames.append(line[0]) | ||
| 71 | + self.boxes.append(box) | ||
| 72 | + self.labels.append(label) | ||
| 73 | + | ||
| 74 | + self.num_samples = len(self.boxes) | ||
| 75 | + | ||
| 76 | + def __len__(self): | ||
| 77 | + return self.num_samples | ||
| 78 | + | ||
| 79 | + def __getitem__(self, index): | ||
| 80 | + img, target, h, w = self.pull_item(index) | ||
| 81 | + return img, target | ||
| 82 | + | ||
| 83 | + def pull_image(self, index): | ||
| 84 | + img_path = self.fnames[index] | ||
| 85 | + img = Image.open(img_path) | ||
| 86 | + if img.mode == 'L': | ||
| 87 | + img.convert('RGB') | ||
| 88 | + img = np.array(img) | ||
| 89 | + return img | ||
| 90 | + | ||
| 91 | + def pull_item(self, index): | ||
| 92 | + while True: | ||
| 93 | + image_path = self.fnames[index] | ||
| 94 | + img = Image.open(image_path) | ||
| 95 | + if img.mode == 'L': | ||
| 96 | + img = img.convert('RGB') | ||
| 97 | + | ||
| 98 | + im_width, im_height = img.size | ||
| 99 | + boxes = self.annotransform( | ||
| 100 | + np.array(self.boxes[index]), im_width, im_height) | ||
| 101 | + label = np.array(self.labels[index]) | ||
| 102 | + bbox_labels = np.hstack((label[:, np.newaxis], boxes)).tolist() | ||
| 103 | + img, sample_labels = preprocess( | ||
| 104 | + img, bbox_labels, self.mode, image_path) | ||
| 105 | + sample_labels = np.array(sample_labels) | ||
| 106 | + if len(sample_labels) > 0: | ||
| 107 | + target = np.hstack( | ||
| 108 | + (sample_labels[:, 1:], sample_labels[:, 0][:, np.newaxis])) | ||
| 109 | + | ||
| 110 | + assert (target[:, 2] > target[:, 0]).any() | ||
| 111 | + assert (target[:, 3] > target[:, 1]).any() | ||
| 112 | + break | ||
| 113 | + else: | ||
| 114 | + index = random.randrange(0, self.num_samples) | ||
| 115 | + | ||
| 116 | + # img = Image.fromarray(img) | ||
| 117 | + ''' | ||
| 118 | + draw = ImageDraw.Draw(img) | ||
| 119 | + w,h = img.size | ||
| 120 | + for bbox in sample_labels: | ||
| 121 | + bbox = (bbox[1:] * np.array([w, h, w, h])).tolist() | ||
| 122 | + | ||
| 123 | + draw.rectangle(bbox,outline='red') | ||
| 124 | + img.save('image.jpg') | ||
| 125 | + ''' | ||
| 126 | + return torch.from_numpy(img), target, im_height, im_width | ||
| 127 | + | ||
| 128 | + def annotransform(self, boxes, im_width, im_height): | ||
| 129 | + boxes[:, 0] /= im_width | ||
| 130 | + boxes[:, 1] /= im_height | ||
| 131 | + boxes[:, 2] /= im_width | ||
| 132 | + boxes[:, 3] /= im_height | ||
| 133 | + return boxes | ||
| 134 | + | ||
| 135 | + | ||
| 136 | +def detection_collate(batch): | ||
| 137 | + """Custom collate fn for dealing with batches of images that have a different | ||
| 138 | + number of associated object annotations (bounding boxes). | ||
| 139 | + | ||
| 140 | + Arguments: | ||
| 141 | + batch: (tuple) A tuple of tensor images and lists of annotations | ||
| 142 | + | ||
| 143 | + Return: | ||
| 144 | + A tuple containing: | ||
| 145 | + 1) (tensor) batch of images stacked on their 0 dim | ||
| 146 | + 2) (list of tensors) annotations for a given image are stacked on | ||
| 147 | + 0 dim | ||
| 148 | + """ | ||
| 149 | + targets = [] | ||
| 150 | + imgs = [] | ||
| 151 | + for sample in batch: | ||
| 152 | + imgs.append(sample[0]) | ||
| 153 | + targets.append(torch.FloatTensor(sample[1])) | ||
| 154 | + return torch.stack(imgs, 0), targets | ||
| 155 | + | ||
| 156 | + | ||
| 157 | +if __name__ == '__main__': | ||
| 158 | + from config import cfg | ||
| 159 | + | ||
| 160 | + dataset = WIDERDetection(cfg.FACE.TRAIN_FILE) | ||
| 161 | + # for i in range(len(dataset)): | ||
| 162 | + dataset.pull_item(14) | ||
| @@ -0,0 +1,9 @@ | |||
| 1 | +#-*- coding:utf-8 -*- | ||
| 2 | + | ||
| 3 | +from __future__ import division | ||
| 4 | +from __future__ import absolute_import | ||
| 5 | +from __future__ import print_function | ||
| 6 | + | ||
| 7 | + | ||
| 8 | +from .functions import * | ||
| 9 | +from .modules import * | ||
| @@ -0,0 +1,366 @@ | |||
| 1 | +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. | ||
| 2 | +# | ||
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | +# you may not use this file except in compliance with the License. | ||
| 5 | +# You may obtain a copy of the License at | ||
| 6 | +# | ||
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | +# | ||
| 9 | +# Unless required by applicable law or agreed to in writing, software | ||
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | +# See the License for the specific language governing permissions and | ||
| 13 | +# limitations under the License. | ||
| 14 | +# ============================================================================ | ||
| 15 | +# Copyright 2021 Huawei Technologies Co., Ltd | ||
| 16 | +# | ||
| 17 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 18 | +# you may not use this file except in compliance with the License. | ||
| 19 | +# You may obtain a copy of the License at | ||
| 20 | +# | ||
| 21 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 22 | +# | ||
| 23 | +# Unless required by applicable law or agreed to in writing, software | ||
| 24 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 25 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 26 | +# See the License for the specific language governing permissions and | ||
| 27 | +# limitations under the License. | ||
| 28 | + | ||
| 29 | +from __future__ import division | ||
| 30 | +from __future__ import absolute_import | ||
| 31 | +from __future__ import print_function | ||
| 32 | + | ||
| 33 | +import torch | ||
| 34 | +from torch.autograd import Variable | ||
| 35 | + | ||
| 36 | + | ||
| 37 | +def point_form(boxes): | ||
| 38 | + """ Convert prior_boxes to (xmin, ymin, xmax, ymax) | ||
| 39 | + representation for comparison to point form ground truth data. | ||
| 40 | + Args: | ||
| 41 | + boxes: (tensor) center-size default boxes from priorbox layers. | ||
| 42 | + Return: | ||
| 43 | + boxes: (tensor) Converted xmin, ymin, xmax, ymax form of boxes. | ||
| 44 | + """ | ||
| 45 | + return torch.cat((boxes[:, :2] - boxes[:, 2:] / 2, # xmin, ymin | ||
| 46 | + boxes[:, :2] + boxes[:, 2:] / 2), 1) # xmax, ymax | ||
| 47 | + | ||
| 48 | + | ||
| 49 | +def center_size(boxes): | ||
| 50 | + """ Convert prior_boxes to (cx, cy, w, h) | ||
| 51 | + representation for comparison to center-size form ground truth data. | ||
| 52 | + Args: | ||
| 53 | + boxes: (tensor) point_form boxes | ||
| 54 | + Return: | ||
| 55 | + boxes: (tensor) Converted xmin, ymin, xmax, ymax form of boxes. | ||
| 56 | + """ | ||
| 57 | + return torch.cat([(boxes[:, 2:] + boxes[:, :2]) / 2, # cx, cy | ||
| 58 | + boxes[:, 2:] - boxes[:, :2]], 1) # w, h | ||
| 59 | + | ||
| 60 | + | ||
| 61 | +def intersect(box_a, box_b): | ||
| 62 | + """ We resize both tensors to [A,B,2] without new malloc: | ||
| 63 | + [A,2] -> [A,1,2] -> [A,B,2] | ||
| 64 | + [B,2] -> [1,B,2] -> [A,B,2] | ||
| 65 | + Then we compute the area of intersect between box_a and box_b. | ||
| 66 | + Args: | ||
| 67 | + box_a: (tensor) bounding boxes, Shape: [A,4]. | ||
| 68 | + box_b: (tensor) bounding boxes, Shape: [B,4]. | ||
| 69 | + Return: | ||
| 70 | + (tensor) intersection area, Shape: [A,B]. | ||
| 71 | + """ | ||
| 72 | + # print("box_a.shape:") | ||
| 73 | + # print(box_a.shape) | ||
| 74 | + # print("box_a.dtype:") | ||
| 75 | + # print(box_a.dtype) | ||
| 76 | + # print("box_a.storage().npu_format():") | ||
| 77 | + # print(box_a.storage().npu_format()) | ||
| 78 | + | ||
| 79 | + # print("box_b.shape:") | ||
| 80 | + # print(box_b.shape) | ||
| 81 | + # print("box_b.dtype:") | ||
| 82 | + # print(box_b.dtype) | ||
| 83 | + # print("box_b.storage().npu_format():") | ||
| 84 | + # print(box_b.storage().npu_format()) | ||
| 85 | + | ||
| 86 | + A = box_a.size(0) | ||
| 87 | + B = box_b.size(0) | ||
| 88 | + max_xy = torch.min(box_a[:, 2:].unsqueeze(1).expand(A, B, 2), | ||
| 89 | + box_b[:, 2:].unsqueeze(0).expand(A, B, 2)) | ||
| 90 | + min_xy = torch.max(box_a[:, :2].unsqueeze(1).expand(A, B, 2), | ||
| 91 | + box_b[:, :2].unsqueeze(0).expand(A, B, 2)) | ||
| 92 | + inter = torch.clamp((max_xy - min_xy), min=0) | ||
| 93 | + return inter[:, :, 0] * inter[:, :, 1] | ||
| 94 | + | ||
| 95 | + | ||
| 96 | +def jaccard(box_a, box_b): | ||
| 97 | + """Compute the jaccard overlap of two sets of boxes. The jaccard overlap | ||
| 98 | + is simply the intersection over union of two boxes. Here we operate on | ||
| 99 | + ground truth boxes and default boxes. | ||
| 100 | + E.g.: | ||
| 101 | + A ∩ B / A ∪ B = A ∩ B / (area(A) + area(B) - A ∩ B) | ||
| 102 | + Args: | ||
| 103 | + box_a: (tensor) Ground truth bounding boxes, Shape: [num_objects,4] | ||
| 104 | + box_b: (tensor) Prior boxes from priorbox layers, Shape: [num_priors,4] | ||
| 105 | + Return: | ||
| 106 | + jaccard overlap: (tensor) Shape: [box_a.size(0), box_b.size(0)] | ||
| 107 | + """ | ||
| 108 | + inter = intersect(box_a, box_b) | ||
| 109 | + area_a = ((box_a[:, 2] - box_a[:, 0]) * | ||
| 110 | + (box_a[:, 3] - box_a[:, 1])).unsqueeze(1).expand_as(inter) # [A,B] | ||
| 111 | + area_b = ((box_b[:, 2] - box_b[:, 0]) * | ||
| 112 | + (box_b[:, 3] - box_b[:, 1])).unsqueeze(0).expand_as(inter) # [A,B] | ||
| 113 | + union = area_a + area_b - inter | ||
| 114 | + return inter / union # [A,B] | ||
| 115 | + | ||
| 116 | + | ||
| 117 | +def match(threshold, truths, priors, variances, labels, loc_t, conf_t, idx): | ||
| 118 | + """Match each prior box with the ground truth box of the highest jaccard | ||
| 119 | + overlap, encode the bounding boxes, then return the matched indices | ||
| 120 | + corresponding to both confidence and location preds. | ||
| 121 | + Args: | ||
| 122 | + threshold: (float) The overlap threshold used when mathing boxes. | ||
| 123 | + truths: (tensor) Ground truth boxes, Shape: [num_obj, num_priors]. | ||
| 124 | + priors: (tensor) Prior boxes from priorbox layers, Shape: [n_priors,4]. | ||
| 125 | + variances: (tensor) Variances corresponding to each prior coord, | ||
| 126 | + Shape: [num_priors, 4]. | ||
| 127 | + labels: (tensor) All the class labels for the image, Shape: [num_obj]. | ||
| 128 | + loc_t: (tensor) Tensor to be filled w/ endcoded location targets. | ||
| 129 | + conf_t: (tensor) Tensor to be filled w/ matched indices for conf preds. | ||
| 130 | + idx: (int) current batch index | ||
| 131 | + Return: | ||
| 132 | + The matched indices corresponding to 1)location and 2)confidence preds. | ||
| 133 | + """ | ||
| 134 | + # jaccard index | ||
| 135 | + overlaps = jaccard( | ||
| 136 | + truths, | ||
| 137 | + point_form(priors) | ||
| 138 | + ) | ||
| 139 | + # (Bipartite Matching) | ||
| 140 | + # [1,num_objects] best prior for each ground truth | ||
| 141 | + best_prior_overlap, best_prior_idx = overlaps.max(1, keepdim=True) | ||
| 142 | + # [1,num_priors] best ground truth for each prior | ||
| 143 | + best_truth_overlap, best_truth_idx = overlaps.max( | ||
| 144 | + 0, keepdim=True) # 0-2000 | ||
| 145 | + best_truth_idx.squeeze_(0) | ||
| 146 | + best_truth_overlap.squeeze_(0) | ||
| 147 | + best_prior_idx.squeeze_(1) | ||
| 148 | + best_prior_overlap.squeeze_(1) | ||
| 149 | + best_truth_overlap.index_fill_(0, best_prior_idx, 2) # ensure best prior | ||
| 150 | + # TODO refactor: index best_prior_idx with long tensor | ||
| 151 | + # ensure every gt matches with its prior of max overlap | ||
| 152 | + for j in range(best_prior_idx.size(0)): | ||
| 153 | + best_truth_idx[best_prior_idx[j]] = j | ||
| 154 | + _th1, _th2, _th3 = threshold # _th1 = 0.1 ,_th2 = 0.35,_th3 = 0.5 | ||
| 155 | + | ||
| 156 | + N = (torch.sum(best_prior_overlap >= _th2) + | ||
| 157 | + torch.sum(best_prior_overlap >= _th3)) // 2 | ||
| 158 | + matches = truths[best_truth_idx] # Shape: [num_priors,4] | ||
| 159 | + conf = labels[best_truth_idx] # Shape: [num_priors] | ||
| 160 | + conf[best_truth_overlap < _th2] = 0 # label as background | ||
| 161 | + | ||
| 162 | + best_truth_overlap_clone = best_truth_overlap.clone() | ||
| 163 | + add_idx = best_truth_overlap_clone.gt( | ||
| 164 | + _th1).eq(best_truth_overlap_clone.lt(_th2)) | ||
| 165 | + best_truth_overlap_clone[~ add_idx] = 0 | ||
| 166 | + stage2_overlap, stage2_idx = best_truth_overlap_clone.sort(descending=True) | ||
| 167 | + | ||
| 168 | + stage2_overlap = stage2_overlap.gt(_th1) | ||
| 169 | + | ||
| 170 | + if N > 0: | ||
| 171 | + N = torch.sum(stage2_overlap[:N]) if torch.sum( | ||
| 172 | + stage2_overlap[:N]) < N else N | ||
| 173 | + conf[stage2_idx[:N]] += 1 | ||
| 174 | + | ||
| 175 | + loc = encode(matches, priors, variances) | ||
| 176 | + loc_t[idx] = loc # [num_priors,4] encoded offsets to learn | ||
| 177 | + conf_t[idx] = conf # [num_priors] top class label for each prior | ||
| 178 | + | ||
| 179 | + | ||
| 180 | +def match_ssd(threshold, truths, priors, variances, labels, loc_t, conf_t, idx): | ||
| 181 | + """Match each prior box with the ground truth box of the highest jaccard | ||
| 182 | + overlap, encode the bounding boxes, then return the matched indices | ||
| 183 | + corresponding to both confidence and location preds. | ||
| 184 | + Args: | ||
| 185 | + threshold: (float) The overlap threshold used when mathing boxes. | ||
| 186 | + truths: (tensor) Ground truth boxes, Shape: [num_obj, num_priors]. | ||
| 187 | + priors: (tensor) Prior boxes from priorbox layers, Shape: [n_priors,4]. | ||
| 188 | + variances: (tensor) Variances corresponding to each prior coord, | ||
| 189 | + Shape: [num_priors, 4]. | ||
| 190 | + labels: (tensor) All the class labels for the image, Shape: [num_obj]. | ||
| 191 | + loc_t: (tensor) Tensor to be filled w/ endcoded location targets. | ||
| 192 | + conf_t: (tensor) Tensor to be filled w/ matched indices for conf preds. | ||
| 193 | + idx: (int) current batch index | ||
| 194 | + Return: | ||
| 195 | + The matched indices corresponding to 1)location and 2)confidence preds. | ||
| 196 | + """ | ||
| 197 | + # jaccard index | ||
| 198 | + overlaps = jaccard( | ||
| 199 | + truths, | ||
| 200 | + point_form(priors) | ||
| 201 | + ) | ||
| 202 | + # (Bipartite Matching) | ||
| 203 | + # [1,num_objects] best prior for each ground truth | ||
| 204 | + best_prior_overlap, best_prior_idx = overlaps.max(1, keepdim=True) | ||
| 205 | + # [1,num_priors] best ground truth for each prior | ||
| 206 | + best_truth_overlap, best_truth_idx = overlaps.max( | ||
| 207 | + 0, keepdim=True) # 0-2000 | ||
| 208 | + best_truth_idx.squeeze_(0) | ||
| 209 | + best_truth_overlap.squeeze_(0) | ||
| 210 | + best_prior_idx.squeeze_(1) | ||
| 211 | + best_prior_overlap.squeeze_(1) | ||
| 212 | + best_truth_overlap.index_fill_(0, best_prior_idx, 2) # ensure best prior | ||
| 213 | + # TODO refactor: index best_prior_idx with long tensor | ||
| 214 | + # ensure every gt matches with its prior of max overlap | ||
| 215 | + for j in range(best_prior_idx.size(0)): | ||
| 216 | + best_truth_idx[best_prior_idx[j]] = j | ||
| 217 | + matches = truths[best_truth_idx] # Shape: [num_priors,4] | ||
| 218 | + conf = labels[best_truth_idx] # Shape: [num_priors] | ||
| 219 | + conf[best_truth_overlap < threshold] = 0 # label as background | ||
| 220 | + loc = encode(matches, priors, variances) | ||
| 221 | + loc_t[idx] = loc # [num_priors,4] encoded offsets to learn | ||
| 222 | + conf_t[idx] = conf # [num_priors] top class label for each prior | ||
| 223 | + | ||
| 224 | + | ||
| 225 | +def encode(matched, priors, variances): | ||
| 226 | + """Encode the variances from the priorbox layers into the ground truth boxes | ||
| 227 | + we have matched (based on jaccard overlap) with the prior boxes. | ||
| 228 | + Args: | ||
| 229 | + matched: (tensor) Coords of ground truth for each prior in point-form | ||
| 230 | + Shape: [num_priors, 4]. | ||
| 231 | + priors: (tensor) Prior boxes in center-offset form | ||
| 232 | + Shape: [num_priors,4]. | ||
| 233 | + variances: (list[float]) Variances of priorboxes | ||
| 234 | + Return: | ||
| 235 | + encoded boxes (tensor), Shape: [num_priors, 4] | ||
| 236 | + """ | ||
| 237 | + | ||
| 238 | + # dist b/t match center and prior's center | ||
| 239 | + g_cxcy = (matched[:, :2] + matched[:, 2:]) / 2 - priors[:, :2] | ||
| 240 | + # encode variance | ||
| 241 | + g_cxcy /= (variances[0] * priors[:, 2:]) | ||
| 242 | + # match wh / prior wh | ||
| 243 | + g_wh = (matched[:, 2:] - matched[:, :2]) / priors[:, 2:] | ||
| 244 | + # g_wh = torch.log(g_wh) / variances[1] | ||
| 245 | + g_wh = torch.log(g_wh) / variances[1] | ||
| 246 | + # return target for smooth_l1_loss | ||
| 247 | + return torch.cat([g_cxcy, g_wh], 1) # [num_priors,4] | ||
| 248 | + | ||
| 249 | + | ||
| 250 | +# Adapted from https://github.com/Hakuyume/chainer-ssd | ||
| 251 | +def decode(loc, priors, variances): | ||
| 252 | + """Decode locations from predictions using priors to undo | ||
| 253 | + the encoding we did for offset regression at train time. | ||
| 254 | + Args: | ||
| 255 | + loc (tensor): location predictions for loc layers, | ||
| 256 | + Shape: [num_priors,4] | ||
| 257 | + priors (tensor): Prior boxes in center-offset form. | ||
| 258 | + Shape: [num_priors,4]. | ||
| 259 | + variances: (list[float]) Variances of priorboxes | ||
| 260 | + Return: | ||
| 261 | + decoded bounding box predictions | ||
| 262 | + """ | ||
| 263 | + | ||
| 264 | + boxes = torch.cat(( | ||
| 265 | + priors[:, :2] + loc[:, :2] * variances[0] * priors[:, 2:], | ||
| 266 | + priors[:, 2:] * torch.exp(loc[:, 2:] * variances[1])), 1) | ||
| 267 | + boxes[:, :2] -= boxes[:, 2:] / 2 | ||
| 268 | + boxes[:, 2:] += boxes[:, :2] | ||
| 269 | + return boxes | ||
| 270 | + | ||
| 271 | + | ||
| 272 | +def log_sum_exp(x): | ||
| 273 | + """Utility function for computing log_sum_exp while determining | ||
| 274 | + This will be used to determine unaveraged confidence loss across | ||
| 275 | + all examples in a batch. | ||
| 276 | + Args: | ||
| 277 | + x (Variable(tensor)): conf_preds from conf layers | ||
| 278 | + """ | ||
| 279 | + x_max = x.data.max() | ||
| 280 | + return torch.log(torch.sum(torch.exp(x - x_max), 1, keepdim=True)) + x_max | ||
| 281 | + | ||
| 282 | + | ||
| 283 | +# Original author: Francisco Massa: | ||
| 284 | +# https://github.com/fmassa/object-detection.torch | ||
| 285 | +# Ported to PyTorch by Max deGroot (02/01/2017) | ||
| 286 | +def nms(boxes, scores, overlap=0.5, top_k=200, use_device='gpu'): | ||
| 287 | + """Apply non-maximum suppression at test time to avoid detecting too many | ||
| 288 | + overlapping bounding boxes for a given object. | ||
| 289 | + Args: | ||
| 290 | + boxes: (tensor) The location preds for the img, Shape: [num_priors,4]. | ||
| 291 | + scores: (tensor) The class predscores for the img, Shape:[num_priors]. | ||
| 292 | + overlap: (float) The overlap thresh for suppressing unnecessary boxes. | ||
| 293 | + top_k: (int) The Maximum number of box preds to consider. | ||
| 294 | + use_device: use gpu or npu | ||
| 295 | + Return: | ||
| 296 | + The indices of the kept boxes with respect to num_priors. | ||
| 297 | + | ||
| 298 | + """ | ||
| 299 | + | ||
| 300 | + keep = scores.new(scores.size(0)).zero_().long() | ||
| 301 | + if boxes.numel() == 0: | ||
| 302 | + return keep | ||
| 303 | + x1 = boxes[:, 0] | ||
| 304 | + y1 = boxes[:, 1] | ||
| 305 | + x2 = boxes[:, 2] | ||
| 306 | + y2 = boxes[:, 3] | ||
| 307 | + area = torch.mul(x2 - x1, y2 - y1) | ||
| 308 | + v, idx = scores.sort(0) # sort in ascending order | ||
| 309 | + # I = I[v >= 0.01] | ||
| 310 | + idx = idx[-top_k:] # indices of the top-k largest vals | ||
| 311 | + xx1 = boxes.new() | ||
| 312 | + yy1 = boxes.new() | ||
| 313 | + xx2 = boxes.new() | ||
| 314 | + yy2 = boxes.new() | ||
| 315 | + w = boxes.new() | ||
| 316 | + h = boxes.new() | ||
| 317 | + | ||
| 318 | + # keep = torch.Tensor() | ||
| 319 | + count = 0 | ||
| 320 | + while idx.numel() > 0: | ||
| 321 | + i = idx[-1] # index of current largest val | ||
| 322 | + # keep.append(i) | ||
| 323 | + keep[count] = i | ||
| 324 | + count += 1 | ||
| 325 | + if idx.size(0) == 1: | ||
| 326 | + break | ||
| 327 | + idx = idx[:-1] # remove kept element from view | ||
| 328 | + # load bboxes of next highest vals | ||
| 329 | + # torch.index_select(x1, 0, idx, out=xx1) | ||
| 330 | + # torch.index_select(y1, 0, idx, out=yy1) | ||
| 331 | + # torch.index_select(x2, 0, idx, out=xx2) | ||
| 332 | + # torch.index_select(y2, 0, idx, out=yy2) | ||
| 333 | + xx1 = torch.index_select(x1, 0, idx) | ||
| 334 | + yy1 = torch.index_select(y1, 0, idx) | ||
| 335 | + xx2 = torch.index_select(x2, 0, idx) | ||
| 336 | + yy2 = torch.index_select(y2, 0, idx) | ||
| 337 | + # store element-wise max with next highest score | ||
| 338 | + if use_device == 'npu': | ||
| 339 | + xx1 = torch.clamp(xx1, min=x1[i].npu().data) | ||
| 340 | + yy1 = torch.clamp(yy1, min=y1[i].npu().data) | ||
| 341 | + xx2 = torch.clamp(xx2, max=x2[i].npu().data) | ||
| 342 | + yy2 = torch.clamp(yy2, max=y2[i].npu().data) | ||
| 343 | + else: | ||
| 344 | + xx1 = torch.clamp(xx1, min=x1[i].cpu().data) | ||
| 345 | + yy1 = torch.clamp(yy1, min=y1[i].cpu().data) | ||
| 346 | + xx2 = torch.clamp(xx2, max=x2[i].cpu().data) | ||
| 347 | + yy2 = torch.clamp(yy2, max=y2[i].cpu().data) | ||
| 348 | + | ||
| 349 | + w = Variable(w) | ||
| 350 | + h = Variable(h) | ||
| 351 | + w.resize_as_(xx2) | ||
| 352 | + h.resize_as_(yy2) | ||
| 353 | + | ||
| 354 | + w = xx2 - xx1 | ||
| 355 | + h = yy2 - yy1 | ||
| 356 | + # check sizes of xx1 and xx2.. after each iteration | ||
| 357 | + w = torch.clamp(w, min=0.0) | ||
| 358 | + h = torch.clamp(h, min=0.0) | ||
| 359 | + inter = w * h | ||
| 360 | + # IoU = i / (area(a) + area(b) - i) | ||
| 361 | + rem_areas = torch.index_select(area, 0, idx) # load remaining areas) | ||
| 362 | + union = (rem_areas - inter) + area[i] | ||
| 363 | + IoU = inter / union # store result in iou | ||
| 364 | + # keep only elements with an IoU <= overlap | ||
| 365 | + idx = idx[IoU.le(overlap)] | ||
| 366 | + return keep, count | ||
| @@ -0,0 +1,5 @@ | |||
| 1 | +from .prior_box import PriorBox | ||
| 2 | +from .detection import Detect | ||
| 3 | + | ||
| 4 | +__all__=['Detect','PriorBox'] | ||
| 5 | + | ||
| @@ -0,0 +1,99 @@ | |||
| 1 | +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. | ||
| 2 | +# | ||
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | +# you may not use this file except in compliance with the License. | ||
| 5 | +# You may obtain a copy of the License at | ||
| 6 | +# | ||
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | +# | ||
| 9 | +# Unless required by applicable law or agreed to in writing, software | ||
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | +# See the License for the specific language governing permissions and | ||
| 13 | +# limitations under the License. | ||
| 14 | +# ============================================================================ | ||
| 15 | +# Copyright 2021 Huawei Technologies Co., Ltd | ||
| 16 | +# | ||
| 17 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 18 | +# you may not use this file except in compliance with the License. | ||
| 19 | +# You may obtain a copy of the License at | ||
| 20 | +# | ||
| 21 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 22 | +# | ||
| 23 | +# Unless required by applicable law or agreed to in writing, software | ||
| 24 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 25 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 26 | +# See the License for the specific language governing permissions and | ||
| 27 | +# limitations under the License. | ||
| 28 | + | ||
| 29 | +from __future__ import division | ||
| 30 | +from __future__ import absolute_import | ||
| 31 | +from __future__ import print_function | ||
| 32 | + | ||
| 33 | +import torch | ||
| 34 | + | ||
| 35 | +from ..bbox_utils import decode, nms | ||
| 36 | +from torch.autograd import Function | ||
| 37 | + | ||
| 38 | + | ||
| 39 | +class Detect(Function): | ||
| 40 | + """At test time, Detect is the final layer of SSD. Decode location preds, | ||
| 41 | + apply non-maximum suppression to location predictions based on conf | ||
| 42 | + scores and threshold to a top_k number of output predictions for both | ||
| 43 | + confidence score and locations. | ||
| 44 | + """ | ||
| 45 | + | ||
| 46 | + def __init__(self, cfg): | ||
| 47 | + self.num_classes = cfg.NUM_CLASSES | ||
| 48 | + self.top_k = cfg.TOP_K | ||
| 49 | + self.nms_thresh = cfg.NMS_THRESH | ||
| 50 | + self.conf_thresh = cfg.CONF_THRESH | ||
| 51 | + self.variance = cfg.VARIANCE | ||
| 52 | + self.nms_top_k = cfg.NMS_TOP_K | ||
| 53 | + | ||
| 54 | + def forward(self, loc_data, conf_data, prior_data, use_device='gpu'): | ||
| 55 | + """ | ||
| 56 | + Args: | ||
| 57 | + loc_data: (tensor) Loc preds from loc layers | ||
| 58 | + Shape: [batch,num_priors*4] | ||
| 59 | + conf_data: (tensor) Shape: Conf preds from conf layers | ||
| 60 | + Shape: [batch*num_priors,num_classes] | ||
| 61 | + prior_data: (tensor) Prior boxes and variances from priorbox layers | ||
| 62 | + Shape: [1,num_priors,4] | ||
| 63 | + use_device: use gpu or npu | ||
| 64 | + """ | ||
| 65 | + print("use_device: " + use_device) | ||
| 66 | + num = loc_data.size(0) | ||
| 67 | + num_priors = prior_data.size(0) | ||
| 68 | + | ||
| 69 | + conf_preds = conf_data.view( | ||
| 70 | + num, num_priors, self.num_classes).transpose(2, 1) | ||
| 71 | + batch_priors = prior_data.view(-1, num_priors, | ||
| 72 | + 4).expand(num, num_priors, 4) | ||
| 73 | + batch_priors = batch_priors.contiguous().view(-1, 4) | ||
| 74 | + | ||
| 75 | + decoded_boxes = decode(loc_data.view(-1, 4), | ||
| 76 | + batch_priors, self.variance) | ||
| 77 | + decoded_boxes = decoded_boxes.view(num, num_priors, 4) | ||
| 78 | + | ||
| 79 | + output = torch.zeros(num, self.num_classes, self.top_k, 5) | ||
| 80 | + | ||
| 81 | + for i in range(num): | ||
| 82 | + boxes = decoded_boxes[i].clone() | ||
| 83 | + conf_scores = conf_preds[i].clone() | ||
| 84 | + | ||
| 85 | + for cl in range(1, self.num_classes): | ||
| 86 | + c_mask = conf_scores[cl].gt(self.conf_thresh) | ||
| 87 | + scores = conf_scores[cl][c_mask] | ||
| 88 | + | ||
| 89 | + if scores.dim() == 0: | ||
| 90 | + continue | ||
| 91 | + l_mask = c_mask.unsqueeze(1).expand_as(boxes) | ||
| 92 | + boxes_ = boxes[l_mask].view(-1, 4) | ||
| 93 | + ids, count = nms(boxes_, scores, self.nms_thresh, self.nms_top_k, use_device) | ||
| 94 | + count = count if count < self.top_k else self.top_k | ||
| 95 | + | ||
| 96 | + output[i, cl, :count] = torch.cat((scores[ids[:count]].unsqueeze(1), | ||
| 97 | + boxes_[ids[:count]]), 1) | ||
| 98 | + | ||
| 99 | + return output | ||
| @@ -0,0 +1,61 @@ | |||
| 1 | +#-*- coding:utf-8 -*- | ||
| 2 | + | ||
| 3 | +from __future__ import division | ||
| 4 | +from __future__ import absolute_import | ||
| 5 | +from __future__ import print_function | ||
| 6 | + | ||
| 7 | +import torch | ||
| 8 | +from itertools import product as product | ||
| 9 | +import math | ||
| 10 | + | ||
| 11 | + | ||
| 12 | +class PriorBox(object): | ||
| 13 | + """Compute priorbox coordinates in center-offset form for each source | ||
| 14 | + feature map. | ||
| 15 | + """ | ||
| 16 | + | ||
| 17 | + def __init__(self, input_size, feature_maps,cfg): | ||
| 18 | + super(PriorBox, self).__init__() | ||
| 19 | + self.imh = input_size[0] | ||
| 20 | + self.imw = input_size[1] | ||
| 21 | + | ||
| 22 | + # number of priors for feature map location (either 4 or 6) | ||
| 23 | + self.variance = cfg.VARIANCE or [0.1] | ||
| 24 | + #self.feature_maps = cfg.FEATURE_MAPS | ||
| 25 | + self.min_sizes = cfg.ANCHOR_SIZES | ||
| 26 | + self.steps = cfg.STEPS | ||
| 27 | + self.clip = cfg.CLIP | ||
| 28 | + for v in self.variance: | ||
| 29 | + if v <= 0: | ||
| 30 | + raise ValueError('Variances must be greater than 0') | ||
| 31 | + self.feature_maps = feature_maps | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + def forward(self): | ||
| 35 | + mean = [] | ||
| 36 | + for k in range(len(self.feature_maps)): | ||
| 37 | + feath = self.feature_maps[k][0] | ||
| 38 | + featw = self.feature_maps[k][1] | ||
| 39 | + for i, j in product(range(feath), range(featw)): | ||
| 40 | + f_kw = self.imw / self.steps[k] | ||
| 41 | + f_kh = self.imh / self.steps[k] | ||
| 42 | + | ||
| 43 | + cx = (j + 0.5) / f_kw | ||
| 44 | + cy = (i + 0.5) / f_kh | ||
| 45 | + | ||
| 46 | + s_kw = self.min_sizes[k] / self.imw | ||
| 47 | + s_kh = self.min_sizes[k] / self.imh | ||
| 48 | + | ||
| 49 | + mean += [cx, cy, s_kw, s_kh] | ||
| 50 | + | ||
| 51 | + output = torch.Tensor(mean).view(-1, 4) | ||
| 52 | + if self.clip: | ||
| 53 | + output.clamp_(max=1, min=0) | ||
| 54 | + return output | ||
| 55 | + | ||
| 56 | + | ||
| 57 | +if __name__ == '__main__': | ||
| 58 | + from data.config import cfg | ||
| 59 | + p = PriorBox([640, 640], cfg) | ||
| 60 | + out = p.forward() | ||
| 61 | + print(out.size()) | ||
| @@ -0,0 +1,12 @@ | |||
| 1 | +#-*- coding:utf-8 -*- | ||
| 2 | + | ||
| 3 | +from __future__ import division | ||
| 4 | +from __future__ import absolute_import | ||
| 5 | +from __future__ import print_function | ||
| 6 | + | ||
| 7 | + | ||
| 8 | +from .l2norm import L2Norm | ||
| 9 | +from .multibox_loss import MultiBoxLoss | ||
| 10 | + | ||
| 11 | +__all__ = ['L2Norm', 'MultiBoxLoss'] | ||
| 12 | + | ||
| @@ -0,0 +1,62 @@ | |||
| 1 | +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. | ||
| 2 | +# | ||
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | +# you may not use this file except in compliance with the License. | ||
| 5 | +# You may obtain a copy of the License at | ||
| 6 | +# | ||
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | +# | ||
| 9 | +# Unless required by applicable law or agreed to in writing, software | ||
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | +# See the License for the specific language governing permissions and | ||
| 13 | +# limitations under the License. | ||
| 14 | +# ============================================================================ | ||
| 15 | +# Copyright 2021 Huawei Technologies Co., Ltd | ||
| 16 | +# | ||
| 17 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 18 | +# you may not use this file except in compliance with the License. | ||
| 19 | +# You may obtain a copy of the License at | ||
| 20 | +# | ||
| 21 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 22 | +# | ||
| 23 | +# Unless required by applicable law or agreed to in writing, software | ||
| 24 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 25 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 26 | +# See the License for the specific language governing permissions and | ||
| 27 | +# limitations under the License. | ||
| 28 | + | ||
| 29 | +from __future__ import division | ||
| 30 | +from __future__ import absolute_import | ||
| 31 | +from __future__ import print_function | ||
| 32 | + | ||
| 33 | + | ||
| 34 | +import torch | ||
| 35 | +import torch.nn as nn | ||
| 36 | +import torch.nn.init as init | ||
| 37 | + | ||
| 38 | + | ||
| 39 | +class L2Norm(nn.Module): | ||
| 40 | + """ | ||
| 41 | + L2Norm | ||
| 42 | + """ | ||
| 43 | + def __init__(self,n_channels, scale): | ||
| 44 | + super(L2Norm,self).__init__() | ||
| 45 | + self.n_channels = n_channels | ||
| 46 | + self.gamma = scale or None | ||
| 47 | + self.eps = 1e-10 | ||
| 48 | + self.weight = nn.Parameter(torch.Tensor(self.n_channels)) | ||
| 49 | + self.reset_parameters() | ||
| 50 | + | ||
| 51 | + def reset_parameters(self): | ||
| 52 | + init.constant(self.weight,self.gamma) | ||
| 53 | + | ||
| 54 | + def forward(self, x): | ||
| 55 | + norm = x.pow(2).sum(dim=1, keepdim=True).sqrt()+self.eps | ||
| 56 | + #x /= norm | ||
| 57 | + x = torch.div(x,norm) | ||
| 58 | + out = self.weight.unsqueeze(0).unsqueeze(2).unsqueeze(3).expand_as(x) * x | ||
| 59 | + return out | ||
| 60 | + | ||
| 61 | + | ||
| 62 | + | ||
| @@ -0,0 +1,157 @@ | |||
| 1 | +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. | ||
| 2 | +# | ||
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | +# you may not use this file except in compliance with the License. | ||
| 5 | +# You may obtain a copy of the License at | ||
| 6 | +# | ||
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | +# | ||
| 9 | +# Unless required by applicable law or agreed to in writing, software | ||
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | +# See the License for the specific language governing permissions and | ||
| 13 | +# limitations under the License. | ||
| 14 | +# ============================================================================ | ||
| 15 | +# Copyright 2021 Huawei Technologies Co., Ltd | ||
| 16 | +# | ||
| 17 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 18 | +# you may not use this file except in compliance with the License. | ||
| 19 | +# You may obtain a copy of the License at | ||
| 20 | +# | ||
| 21 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 22 | +# | ||
| 23 | +# Unless required by applicable law or agreed to in writing, software | ||
| 24 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 25 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 26 | +# See the License for the specific language governing permissions and | ||
| 27 | +# limitations under the License. | ||
| 28 | + | ||
| 29 | +from __future__ import division | ||
| 30 | +from __future__ import absolute_import | ||
| 31 | +from __future__ import print_function | ||
| 32 | + | ||
| 33 | +import math | ||
| 34 | +import torch | ||
| 35 | +import torch.nn as nn | ||
| 36 | +import torch.nn.functional as F | ||
| 37 | +from torch.autograd import Variable | ||
| 38 | + | ||
| 39 | + | ||
| 40 | +from ..bbox_utils import match, log_sum_exp, match_ssd | ||
| 41 | + | ||
| 42 | + | ||
| 43 | +class MultiBoxLoss(nn.Module): | ||
| 44 | + """SSD Weighted Loss Function | ||
| 45 | + Compute Targets: | ||
| 46 | + 1) Produce Confidence Target Indices by matching ground truth boxes | ||
| 47 | + with (default) 'priorboxes' that have jaccard index > threshold parameter | ||
| 48 | + (default threshold: 0.5). | ||
| 49 | + 2) Produce localization target by 'encoding' variance into offsets of ground | ||
| 50 | + truth boxes and their matched 'priorboxes'. | ||
| 51 | + 3) Hard negative mining to filter the excessive number of negative examples | ||
| 52 | + that comes with using a large number of default bounding boxes. | ||
| 53 | + (default negative:positive ratio 3:1) | ||
| 54 | + Objective Loss: | ||
| 55 | + L(x,c,l,g) = (Lconf(x, c) + αLloc(x,l,g)) / N | ||
| 56 | + Where, Lconf is the CrossEntropy Loss and Lloc is the SmoothL1 Loss | ||
| 57 | + weighted by α which is set to 1 by cross val. | ||
| 58 | + Args: | ||
| 59 | + c: class confidences, | ||
| 60 | + l: predicted boxes, | ||
| 61 | + g: ground truth boxes | ||
| 62 | + N: number of matched default boxes | ||
| 63 | + See: https://arxiv.org/pdf/1512.02325.pdf for more details. | ||
| 64 | + """ | ||
| 65 | + | ||
| 66 | + def __init__(self, cfg, dataset, use_device): | ||
| 67 | + super(MultiBoxLoss, self).__init__() | ||
| 68 | + self.use_device = use_device | ||
| 69 | + self.num_classes = cfg.NUM_CLASSES | ||
| 70 | + self.negpos_ratio = cfg.NEG_POS_RATIOS | ||
| 71 | + self.variance = cfg.VARIANCE | ||
| 72 | + self.dataset = dataset | ||
| 73 | + if dataset == 'face': | ||
| 74 | + self.threshold = cfg.FACE.OVERLAP_THRESH | ||
| 75 | + self.match = match | ||
| 76 | + elif dataset == 'hand': | ||
| 77 | + self.threshold = cfg.HAND.OVERLAP_THRESH | ||
| 78 | + self.match = match_ssd | ||
| 79 | + else: | ||
| 80 | + self.threshold = cfg.HEAD.OVERLAP_THRESH | ||
| 81 | + self.match = match | ||
| 82 | + | ||
| 83 | + def forward(self, predictions, targets): | ||
| 84 | + """Multibox Loss | ||
| 85 | + Args: | ||
| 86 | + predictions (tuple): A tuple containing loc preds, conf preds, | ||
| 87 | + and prior boxes from SSD net. | ||
| 88 | + conf shape: torch.size(batch_size,num_priors,num_classes) | ||
| 89 | + loc shape: torch.size(batch_size,num_priors,4) | ||
| 90 | + priors shape: torch.size(num_priors,4) | ||
| 91 | + | ||
| 92 | + targets (tensor): Ground truth boxes and labels for a batch, | ||
| 93 | + shape: [batch_size,num_objs,5] (last idx is the label). | ||
| 94 | + """ | ||
| 95 | + loc_data, conf_data, priors = predictions | ||
| 96 | + num = loc_data.size(0) | ||
| 97 | + priors = priors[:loc_data.size(1), :] | ||
| 98 | + num_priors = (priors.size(0)) | ||
| 99 | + num_classes = self.num_classes | ||
| 100 | + | ||
| 101 | + # match priors (default boxes) and ground truth boxes | ||
| 102 | + if self.use_device == 'npu': | ||
| 103 | + loc_t = torch.Tensor(num, num_priors, 4).npu() | ||
| 104 | + conf_t = torch.LongTensor(num, num_priors).npu() | ||
| 105 | + else: | ||
| 106 | + loc_t = torch.Tensor(num, num_priors, 4) | ||
| 107 | + conf_t = torch.LongTensor(num, num_priors) | ||
| 108 | + for idx in range(num): | ||
| 109 | + truths = targets[idx][:, :-1].data | ||
| 110 | + labels = targets[idx][:, -1].data | ||
| 111 | + defaults = priors.data | ||
| 112 | + self.match(self.threshold, truths, defaults, self.variance, labels, | ||
| 113 | + loc_t, conf_t, idx) | ||
| 114 | + if self.use_device == 'gpu': | ||
| 115 | + loc_t = loc_t.cuda() | ||
| 116 | + conf_t = conf_t.cuda() | ||
| 117 | + # wrap targets | ||
| 118 | + loc_t = Variable(loc_t, requires_grad=False) | ||
| 119 | + conf_t = Variable(conf_t, requires_grad=False) | ||
| 120 | + | ||
| 121 | + pos = conf_t > 0 | ||
| 122 | + num_pos = pos.sum(dim=1, keepdim=True) | ||
| 123 | + # Localization Loss (Smooth L1) | ||
| 124 | + # Shape: [batch,num_priors,4] | ||
| 125 | + pos_idx = pos.unsqueeze(pos.dim()).expand_as(loc_data) | ||
| 126 | + loc_p = loc_data[pos_idx].view(-1, 4) | ||
| 127 | + loc_t = loc_t[pos_idx].view(-1, 4) | ||
| 128 | + loss_l = F.smooth_l1_loss(loc_p, loc_t, size_average=False) | ||
| 129 | + # print(loc_p) | ||
| 130 | + # Compute max conf across batch for hard negative mining | ||
| 131 | + batch_conf = conf_data.view(-1, self.num_classes) | ||
| 132 | + loss_c = log_sum_exp(batch_conf) - \ | ||
| 133 | + batch_conf.gather(1, conf_t.view(-1, 1)) | ||
| 134 | + | ||
| 135 | + # Hard Negative Mining | ||
| 136 | + loss_c[pos.view(-1, 1)] = 0 # filter out pos boxes for now | ||
| 137 | + loss_c = loss_c.view(num, -1) | ||
| 138 | + _, loss_idx = loss_c.sort(1, descending=True) | ||
| 139 | + _, idx_rank = loss_idx.sort(1) | ||
| 140 | + num_pos = pos.long().sum(1, keepdim=True) | ||
| 141 | + num_neg = torch.clamp(self.negpos_ratio * | ||
| 142 | + num_pos, max=pos.size(1) - 1) | ||
| 143 | + neg = idx_rank < num_neg.expand_as(idx_rank) | ||
| 144 | + | ||
| 145 | + # Confidence Loss Including Positive and Negative Examples | ||
| 146 | + pos_idx = pos.unsqueeze(2).expand_as(conf_data) | ||
| 147 | + neg_idx = neg.unsqueeze(2).expand_as(conf_data) | ||
| 148 | + conf_p = conf_data[(pos_idx + neg_idx).gt(0) | ||
| 149 | + ].view(-1, self.num_classes) | ||
| 150 | + targets_weighted = conf_t[(pos + neg).gt(0)] | ||
| 151 | + loss_c = F.cross_entropy(conf_p, targets_weighted, size_average=False) | ||
| 152 | + | ||
| 153 | + # Sum of losses: L(x,c,l,g) = (Lconf(x, c) + αLloc(x,l,g)) / N | ||
| 154 | + N = num_pos.data.sum() if num_pos.data.sum() > 0 else num | ||
| 155 | + loss_l /= N | ||
| 156 | + loss_c /= N | ||
| 157 | + return loss_l, loss_c | ||
| @@ -0,0 +1,121 @@ | |||
| 1 | +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. | ||
| 2 | +# | ||
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | +# you may not use this file except in compliance with the License. | ||
| 5 | +# You may obtain a copy of the License at | ||
| 6 | +# | ||
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | +# | ||
| 9 | +# Unless required by applicable law or agreed to in writing, software | ||
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | +# See the License for the specific language governing permissions and | ||
| 13 | +# limitations under the License. | ||
| 14 | +# ============================================================================ | ||
| 15 | +# Copyright 2021 Huawei Technologies Co., Ltd | ||
| 16 | +# | ||
| 17 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 18 | +# you may not use this file except in compliance with the License. | ||
| 19 | +# You may obtain a copy of the License at | ||
| 20 | +# | ||
| 21 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 22 | +# | ||
| 23 | +# Unless required by applicable law or agreed to in writing, software | ||
| 24 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 25 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 26 | +# See the License for the specific language governing permissions and | ||
| 27 | +# limitations under the License. | ||
| 28 | + | ||
| 29 | +from __future__ import division | ||
| 30 | +from __future__ import absolute_import | ||
| 31 | +from __future__ import print_function | ||
| 32 | + | ||
| 33 | +import argparse | ||
| 34 | +import os | ||
| 35 | +from data.config import cfg | ||
| 36 | + | ||
| 37 | +parser = argparse.ArgumentParser(description='Dataset preprocess') | ||
| 38 | +parser.add_argument('--dataset_dir', default='/cache/dataset', type=str, help='Training dataset directory') | ||
| 39 | +args = parser.parse_args() | ||
| 40 | + | ||
| 41 | +WIDER_ROOT = args.dataset_dir | ||
| 42 | + | ||
| 43 | +train_list_file = os.path.join(WIDER_ROOT, 'wider_face_split', | ||
| 44 | + 'wider_face_train_bbx_gt.txt') | ||
| 45 | +val_list_file = os.path.join(WIDER_ROOT, 'wider_face_split', | ||
| 46 | + 'wider_face_val_bbx_gt.txt') | ||
| 47 | + | ||
| 48 | +WIDER_TRAIN = os.path.join(WIDER_ROOT, 'WIDER_train', 'images') | ||
| 49 | +WIDER_VAL = os.path.join(WIDER_ROOT, 'WIDER_val', 'images') | ||
| 50 | + | ||
| 51 | + | ||
| 52 | +def parse_wider_file(root, file): | ||
| 53 | + """ | ||
| 54 | + parse_wider_file | ||
| 55 | + """ | ||
| 56 | + with open(file, 'r') as fr: | ||
| 57 | + lines = fr.readlines() | ||
| 58 | + face_count = [] | ||
| 59 | + img_paths = [] | ||
| 60 | + face_loc = [] | ||
| 61 | + img_faces = [] | ||
| 62 | + count = 0 | ||
| 63 | + flag = False | ||
| 64 | + for k, line in enumerate(lines): | ||
| 65 | + line = line.strip().strip('\n') | ||
| 66 | + if count > 0: | ||
| 67 | + line = line.split(' ') | ||
| 68 | + count -= 1 | ||
| 69 | + loc = [int(line[0]), int(line[1]), int(line[2]), int(line[3])] | ||
| 70 | + face_loc += [loc] | ||
| 71 | + if flag: | ||
| 72 | + face_count += [int(line)] | ||
| 73 | + flag = False | ||
| 74 | + count = int(line) | ||
| 75 | + if 'jpg' in line: | ||
| 76 | + img_paths += [os.path.join(root, line)] | ||
| 77 | + flag = True | ||
| 78 | + | ||
| 79 | + total_face = 0 | ||
| 80 | + for k in face_count: | ||
| 81 | + face_ = [] | ||
| 82 | + for x in range(total_face, total_face + k): | ||
| 83 | + face_.append(face_loc[x]) | ||
| 84 | + img_faces += [face_] | ||
| 85 | + total_face += k | ||
| 86 | + return img_paths, img_faces | ||
| 87 | + | ||
| 88 | + | ||
| 89 | +def wider_data_file(): | ||
| 90 | + """ | ||
| 91 | + wider_data_file | ||
| 92 | + """ | ||
| 93 | + img_paths, bbox = parse_wider_file(WIDER_TRAIN, train_list_file) | ||
| 94 | + fw = open(cfg.FACE.TRAIN_FILE, 'w') | ||
| 95 | + for index in range(len(img_paths)): | ||
| 96 | + path = img_paths[index] | ||
| 97 | + boxes = bbox[index] | ||
| 98 | + fw.write(path) | ||
| 99 | + fw.write(' {}'.format(len(boxes))) | ||
| 100 | + for box in boxes: | ||
| 101 | + data = ' {} {} {} {} {}'.format(box[0], box[1], box[2], box[3], 1) | ||
| 102 | + fw.write(data) | ||
| 103 | + fw.write('\n') | ||
| 104 | + fw.close() | ||
| 105 | + | ||
| 106 | + img_paths, bbox = parse_wider_file(WIDER_VAL, val_list_file) | ||
| 107 | + fw = open(cfg.FACE.VAL_FILE, 'w') | ||
| 108 | + for index in range(len(img_paths)): | ||
| 109 | + path = img_paths[index] | ||
| 110 | + boxes = bbox[index] | ||
| 111 | + fw.write(path) | ||
| 112 | + fw.write(' {}'.format(len(boxes))) | ||
| 113 | + for box in boxes: | ||
| 114 | + data = ' {} {} {} {} {}'.format(box[0], box[1], box[2], box[3], 1) | ||
| 115 | + fw.write(data) | ||
| 116 | + fw.write('\n') | ||
| 117 | + fw.close() | ||
| 118 | + | ||
| 119 | + | ||
| 120 | +if __name__ == '__main__': | ||
| 121 | + wider_data_file() | ||
| @@ -0,0 +1,8 @@ | |||
| 1 | +torch==1.8.0 | ||
| 2 | +numpy==1.19.5 | ||
| 3 | +apex==0.1 | ||
| 4 | +opencv-python==4.5.1.48 | ||
| 5 | +scipy==1.6.3 | ||
| 6 | +Pillow==8.2.0 | ||
| 7 | +six==1.16.0 | ||
| 8 | +easydict==1.9 | ||
| @@ -0,0 +1,318 @@ | |||
| 1 | +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. | ||
| 2 | +# | ||
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | +# you may not use this file except in compliance with the License. | ||
| 5 | +# You may obtain a copy of the License at | ||
| 6 | +# | ||
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | +# | ||
| 9 | +# Unless required by applicable law or agreed to in writing, software | ||
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | +# See the License for the specific language governing permissions and | ||
| 13 | +# limitations under the License. | ||
| 14 | +# ============================================================================ | ||
| 15 | +# Copyright 2021 Huawei Technologies Co., Ltd | ||
| 16 | +# | ||
| 17 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 18 | +# you may not use this file except in compliance with the License. | ||
| 19 | +# You may obtain a copy of the License at | ||
| 20 | +# | ||
| 21 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 22 | +# | ||
| 23 | +# Unless required by applicable law or agreed to in writing, software | ||
| 24 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 25 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 26 | +# See the License for the specific language governing permissions and | ||
| 27 | +# limitations under the License. | ||
| 28 | + | ||
| 29 | +from __future__ import division | ||
| 30 | +from __future__ import absolute_import | ||
| 31 | +from __future__ import print_function | ||
| 32 | + | ||
| 33 | +import os | ||
| 34 | +import torch | ||
| 35 | +import torch.nn as nn | ||
| 36 | +import torch.nn.init as init | ||
| 37 | +import torch.nn.functional as F | ||
| 38 | +from torch.autograd import Variable | ||
| 39 | + | ||
| 40 | +from layers import * | ||
| 41 | +from data.config import cfg | ||
| 42 | + | ||
| 43 | + | ||
| 44 | +class S3FD(nn.Module): | ||
| 45 | + """Single Shot Multibox Architecture | ||
| 46 | + The network is composed of a base VGG network followed by the | ||
| 47 | + added multibox conv layers. Each multibox layer branches into | ||
| 48 | + 1) conv2d for class conf scores | ||
| 49 | + 2) conv2d for localization predictions | ||
| 50 | + 3) associated priorbox layer to produce default bounding | ||
| 51 | + boxes specific to the layer's feature map size. | ||
| 52 | + See: https://arxiv.org/pdf/1512.02325.pdf for more details. | ||
| 53 | + | ||
| 54 | + Args: | ||
| 55 | + phase: (string) Can be "test" or "train" | ||
| 56 | + size: input image size | ||
| 57 | + base: VGG16 layers for input, size of either 300 or 500 | ||
| 58 | + extras: extra layers that feed to multibox loc and conf layers | ||
| 59 | + head: "multibox head" consists of loc and conf conv layers | ||
| 60 | + """ | ||
| 61 | + | ||
| 62 | + def __init__(self, phase, base, extras, head, num_classes, use_device): | ||
| 63 | + super(S3FD, self).__init__() | ||
| 64 | + self.phase = phase | ||
| 65 | + self.num_classes = num_classes | ||
| 66 | + self.use_device = use_device | ||
| 67 | + ''' | ||
| 68 | + self.priorbox = PriorBox(size,cfg) | ||
| 69 | + self.priors = Variable(self.priorbox.forward(), volatile=True) | ||
| 70 | + ''' | ||
| 71 | + # SSD network | ||
| 72 | + self.vgg = nn.ModuleList(base) | ||
| 73 | + # Layer learns to scale the l2 normalized features from conv4_3 | ||
| 74 | + self.L2Norm3_3 = L2Norm(256, 10) | ||
| 75 | + self.L2Norm4_3 = L2Norm(512, 8) | ||
| 76 | + self.L2Norm5_3 = L2Norm(512, 5) | ||
| 77 | + | ||
| 78 | + self.extras = nn.ModuleList(extras) | ||
| 79 | + | ||
| 80 | + self.loc = nn.ModuleList(head[0]) | ||
| 81 | + self.conf = nn.ModuleList(head[1]) | ||
| 82 | + | ||
| 83 | + if self.phase == 'test': | ||
| 84 | + self.softmax = nn.Softmax(dim=-1) | ||
| 85 | + self.detect = Detect(cfg) | ||
| 86 | + | ||
| 87 | + def forward(self, x): | ||
| 88 | + """Applies network layers and ops on input image(s) x. | ||
| 89 | + | ||
| 90 | + Args: | ||
| 91 | + x: input image or batch of images. Shape: [batch,3,300,300]. | ||
| 92 | + | ||
| 93 | + Return: | ||
| 94 | + Depending on phase: | ||
| 95 | + test: | ||
| 96 | + Variable(tensor) of output class label predictions, | ||
| 97 | + confidence score, and corresponding location predictions for | ||
| 98 | + each object detected. Shape: [batch,topk,7] | ||
| 99 | + | ||
| 100 | + train_gpu: | ||
| 101 | + list of concat outputs from: | ||
| 102 | + 1: confidence layers, Shape: [batch*num_priors,num_classes] | ||
| 103 | + 2: localization layers, Shape: [batch,num_priors*4] | ||
| 104 | + 3: priorbox layers, Shape: [2,num_priors*4] | ||
| 105 | + """ | ||
| 106 | + size = x.size()[2:] | ||
| 107 | + sources = list() | ||
| 108 | + loc = list() | ||
| 109 | + conf = list() | ||
| 110 | + | ||
| 111 | + # apply vgg up to conv4_3 relu | ||
| 112 | + for k in range(16): | ||
| 113 | + x = self.vgg[k](x) | ||
| 114 | + | ||
| 115 | + s = self.L2Norm3_3(x) | ||
| 116 | + sources.append(s) | ||
| 117 | + | ||
| 118 | + # apply vgg up to fc7 | ||
| 119 | + for k in range(16, 23): | ||
| 120 | + x = self.vgg[k](x) | ||
| 121 | + | ||
| 122 | + s = self.L2Norm4_3(x) | ||
| 123 | + sources.append(s) | ||
| 124 | + | ||
| 125 | + for k in range(23, 30): | ||
| 126 | + x = self.vgg[k](x) | ||
| 127 | + | ||
| 128 | + s = self.L2Norm5_3(x) | ||
| 129 | + sources.append(s) | ||
| 130 | + | ||
| 131 | + for k in range(30, len(self.vgg)): | ||
| 132 | + x = self.vgg[k](x) | ||
| 133 | + sources.append(x) | ||
| 134 | + | ||
| 135 | + # apply extra layers and cache source layer outputs | ||
| 136 | + for k, v in enumerate(self.extras): | ||
| 137 | + x = F.relu(v(x), inplace=True) | ||
| 138 | + if k % 2 == 1: | ||
| 139 | + sources.append(x) | ||
| 140 | + | ||
| 141 | + # apply multibox head to source layers | ||
| 142 | + | ||
| 143 | + loc_x = self.loc[0](sources[0]) | ||
| 144 | + conf_x = self.conf[0](sources[0]) | ||
| 145 | + | ||
| 146 | + max_conf, _ = torch.max(conf_x[:, 0:3, :, :], dim=1, keepdim=True) | ||
| 147 | + conf_x = torch.cat((max_conf, conf_x[:, 3:, :, :]), dim=1) | ||
| 148 | + | ||
| 149 | + loc.append(loc_x.permute(0, 2, 3, 1).contiguous()) | ||
| 150 | + conf.append(conf_x.permute(0, 2, 3, 1).contiguous()) | ||
| 151 | + | ||
| 152 | + for i in range(1, len(sources)): | ||
| 153 | + x = sources[i] | ||
| 154 | + conf.append(self.conf[i](x).permute(0, 2, 3, 1).contiguous()) | ||
| 155 | + loc.append(self.loc[i](x).permute(0, 2, 3, 1).contiguous()) | ||
| 156 | + | ||
| 157 | + ''' | ||
| 158 | + for (x, l, c) in zip(sources, self.loc, self.conf): | ||
| 159 | + loc.append(l(x).permute(0, 2, 3, 1).contiguous()) | ||
| 160 | + conf.append(c(x).permute(0, 2, 3, 1).contiguous()) | ||
| 161 | + ''' | ||
| 162 | + | ||
| 163 | + features_maps = [] | ||
| 164 | + for i in range(len(loc)): | ||
| 165 | + feat = [] | ||
| 166 | + feat += [loc[i].size(1), loc[i].size(2)] | ||
| 167 | + features_maps += [feat] | ||
| 168 | + | ||
| 169 | + self.priorbox = PriorBox(size, features_maps, cfg) | ||
| 170 | + if self.use_device == 'npu': | ||
| 171 | + self.priors = Variable(self.priorbox.forward().npu(), volatile=True) | ||
| 172 | + else: | ||
| 173 | + self.priors = Variable(self.priorbox.forward(), volatile=True) | ||
| 174 | + | ||
| 175 | + loc = torch.cat([o.view(o.size(0), -1) for o in loc], 1) | ||
| 176 | + conf = torch.cat([o.view(o.size(0), -1) for o in conf], 1) | ||
| 177 | + | ||
| 178 | + if self.phase == 'test': | ||
| 179 | + if self.use_device != 'npu': | ||
| 180 | + output = self.detect.forward( | ||
| 181 | + loc.view(loc.size(0), -1, 4), # loc preds | ||
| 182 | + self.softmax(conf.view(conf.size(0), -1, | ||
| 183 | + self.num_classes)), # conf preds | ||
| 184 | + self.priors.type(type(x.data)), # default boxes | ||
| 185 | + self.use_device | ||
| 186 | + ) | ||
| 187 | + else: | ||
| 188 | + print("use:.npu()") | ||
| 189 | + output = self.detect.forward( | ||
| 190 | + loc.view(loc.size(0), -1, 4).npu(), # loc preds | ||
| 191 | + self.softmax(conf.view(conf.size(0), -1, | ||
| 192 | + self.num_classes)).npu(), # conf preds | ||
| 193 | + self.priors.type(type(x.data)).npu(), # default boxes | ||
| 194 | + self.use_device | ||
| 195 | + ).npu() | ||
| 196 | + | ||
| 197 | + else: | ||
| 198 | + output = ( | ||
| 199 | + loc.view(loc.size(0), -1, 4), | ||
| 200 | + conf.view(conf.size(0), -1, self.num_classes), | ||
| 201 | + self.priors | ||
| 202 | + ) | ||
| 203 | + return output | ||
| 204 | + | ||
| 205 | + def load_weights(self, base_file): | ||
| 206 | + other, ext = os.path.splitext(base_file) | ||
| 207 | + if ext == '.pkl' or '.pth': | ||
| 208 | + print('Loading weights into state dict...') | ||
| 209 | + mdata = torch.load(base_file, | ||
| 210 | + map_location=lambda storage, loc: storage) | ||
| 211 | + weights = mdata['weight'] | ||
| 212 | + epoch = mdata['epoch'] | ||
| 213 | + self.load_state_dict(weights) | ||
| 214 | + print('Finished!') | ||
| 215 | + else: | ||
| 216 | + print('Sorry only .pth and .pkl files supported.') | ||
| 217 | + return epoch | ||
| 218 | + | ||
| 219 | + def xavier(self, param): | ||
| 220 | + init.xavier_uniform(param) | ||
| 221 | + | ||
| 222 | + def weights_init(self, m): | ||
| 223 | + if isinstance(m, nn.Conv2d): | ||
| 224 | + self.xavier(m.weight.data) | ||
| 225 | + m.bias.data.zero_() | ||
| 226 | + | ||
| 227 | + | ||
| 228 | +vgg_cfg = [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'C', 512, 512, 512, 'M', | ||
| 229 | + 512, 512, 512, 'M'] | ||
| 230 | + | ||
| 231 | +extras_cfg = [256, 'S', 512, 128, 'S', 256] | ||
| 232 | + | ||
| 233 | + | ||
| 234 | +def vgg(cfg, i, batch_norm=False): | ||
| 235 | + """ | ||
| 236 | + vgg | ||
| 237 | + """ | ||
| 238 | + layers = [] | ||
| 239 | + in_channels = i | ||
| 240 | + for v in cfg: | ||
| 241 | + if v == 'M': | ||
| 242 | + layers += [nn.MaxPool2d(kernel_size=2, stride=2)] | ||
| 243 | + elif v == 'C': | ||
| 244 | + layers += [nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True)] | ||
| 245 | + else: | ||
| 246 | + conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1) | ||
| 247 | + if batch_norm: | ||
| 248 | + layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)] | ||
| 249 | + else: | ||
| 250 | + layers += [conv2d, nn.ReLU(inplace=True)] | ||
| 251 | + in_channels = v | ||
| 252 | + conv6 = nn.Conv2d(512, 1024, kernel_size=3, padding=6, dilation=6) | ||
| 253 | + conv7 = nn.Conv2d(1024, 1024, kernel_size=1) | ||
| 254 | + layers += [conv6, | ||
| 255 | + nn.ReLU(inplace=True), conv7, nn.ReLU(inplace=True)] | ||
| 256 | + return layers | ||
| 257 | + | ||
| 258 | + | ||
| 259 | +def add_extras(cfg, i, batch_norm=False): | ||
| 260 | + """ | ||
| 261 | + Extra layers added to VGG for feature scaling | ||
| 262 | + """ | ||
| 263 | + layers = [] | ||
| 264 | + in_channels = i | ||
| 265 | + flag = False | ||
| 266 | + for k, v in enumerate(cfg): | ||
| 267 | + if in_channels != 'S': | ||
| 268 | + if v == 'S': | ||
| 269 | + layers += [nn.Conv2d(in_channels, cfg[k + 1], | ||
| 270 | + kernel_size=(1, 3)[flag], stride=2, padding=1)] | ||
| 271 | + else: | ||
| 272 | + layers += [nn.Conv2d(in_channels, v, kernel_size=(1, 3)[flag])] | ||
| 273 | + flag = not flag | ||
| 274 | + in_channels = v | ||
| 275 | + return layers | ||
| 276 | + | ||
| 277 | + | ||
| 278 | +def multibox(vgg, extra_layers, num_classes): | ||
| 279 | + """ | ||
| 280 | + multibox | ||
| 281 | + """ | ||
| 282 | + loc_layers = [] | ||
| 283 | + conf_layers = [] | ||
| 284 | + vgg_source = [21, 28, -2] | ||
| 285 | + | ||
| 286 | + loc_layers += [nn.Conv2d(vgg[14].out_channels, 4, | ||
| 287 | + kernel_size=3, padding=1)] | ||
| 288 | + conf_layers += [nn.Conv2d(vgg[14].out_channels, | ||
| 289 | + 3 + (num_classes - 1), kernel_size=3, padding=1)] | ||
| 290 | + | ||
| 291 | + for k, v in enumerate(vgg_source): | ||
| 292 | + loc_layers += [nn.Conv2d(vgg[v].out_channels, | ||
| 293 | + 4, kernel_size=3, padding=1)] | ||
| 294 | + conf_layers += [nn.Conv2d(vgg[v].out_channels, | ||
| 295 | + num_classes, kernel_size=3, padding=1)] | ||
| 296 | + for k, v in enumerate(extra_layers[1::2], 2): | ||
| 297 | + loc_layers += [nn.Conv2d(v.out_channels, | ||
| 298 | + 4, kernel_size=3, padding=1)] | ||
| 299 | + conf_layers += [nn.Conv2d(v.out_channels, | ||
| 300 | + num_classes, kernel_size=3, padding=1)] | ||
| 301 | + return vgg, extra_layers, (loc_layers, conf_layers) | ||
| 302 | + | ||
| 303 | + | ||
| 304 | +def build_s3fd(phase, num_classes=2, use_device='gpu'): | ||
| 305 | + """ | ||
| 306 | + build s3fd model | ||
| 307 | + """ | ||
| 308 | + print(use_device) | ||
| 309 | + base_, extras_, head_ = multibox( | ||
| 310 | + vgg(vgg_cfg, 3), add_extras((extras_cfg), 1024), num_classes) | ||
| 311 | + | ||
| 312 | + return S3FD(phase, base_, extras_, head_, num_classes, use_device) | ||
| 313 | + | ||
| 314 | + | ||
| 315 | +if __name__ == '__main__': | ||
| 316 | + net = build_s3fd('train', num_classes=2) | ||
| 317 | + inputs = Variable(torch.randn(4, 3, 640, 640)) | ||
| 318 | + output = net(inputs) | ||
| @@ -0,0 +1,315 @@ | |||
| 1 | +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. | ||
| 2 | +# | ||
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | +# you may not use this file except in compliance with the License. | ||
| 5 | +# You may obtain a copy of the License at | ||
| 6 | +# | ||
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | +# | ||
| 9 | +# Unless required by applicable law or agreed to in writing, software | ||
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | +# See the License for the specific language governing permissions and | ||
| 13 | +# limitations under the License. | ||
| 14 | +# ============================================================================ | ||
| 15 | +# Copyright 2021 Huawei Technologies Co., Ltd | ||
| 16 | +# | ||
| 17 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 18 | +# you may not use this file except in compliance with the License. | ||
| 19 | +# You may obtain a copy of the License at | ||
| 20 | +# | ||
| 21 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 22 | +# | ||
| 23 | +# Unless required by applicable law or agreed to in writing, software | ||
| 24 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 25 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 26 | +# See the License for the specific language governing permissions and | ||
| 27 | +# limitations under the License. | ||
| 28 | + | ||
| 29 | +from __future__ import division | ||
| 30 | +from __future__ import absolute_import | ||
| 31 | +from __future__ import print_function | ||
| 32 | + | ||
| 33 | +import os | ||
| 34 | +import time | ||
| 35 | +import torch | ||
| 36 | +import argparse | ||
| 37 | +import torch.optim as optim | ||
| 38 | +import torch.utils.data as data | ||
| 39 | +import numpy as np | ||
| 40 | +from torch.autograd import Variable | ||
| 41 | +import torch.backends.cudnn as cudnn | ||
| 42 | +from apex import amp | ||
| 43 | + | ||
| 44 | +from data.config import cfg | ||
| 45 | +from s3fd import build_s3fd | ||
| 46 | +from layers.modules import MultiBoxLoss | ||
| 47 | +from data.factory import dataset_factory, detection_collate | ||
| 48 | +# Apex imports | ||
| 49 | +try: | ||
| 50 | + import apex_C | ||
| 51 | + import apex | ||
| 52 | + from apex.parallel.LARC import LARC | ||
| 53 | + from apex.parallel import DistributedDataParallel as DDP | ||
| 54 | + from apex.fp16_utils import * | ||
| 55 | + from apex.multi_tensor_apply import multi_tensor_applier | ||
| 56 | + #import amp_C | ||
| 57 | +except ImportError: | ||
| 58 | + raise ImportError("Please install APEX from https://github.com/nvidia/apex") | ||
| 59 | + | ||
| 60 | +def str2bool(v): | ||
| 61 | + return v.lower() in ("yes", "true", "t", "1") | ||
| 62 | + | ||
| 63 | + | ||
| 64 | +parser = argparse.ArgumentParser( | ||
| 65 | + description='S3FD face Detector Training With Pytorch') | ||
| 66 | +parser.add_argument('--dataset', | ||
| 67 | + default='face', | ||
| 68 | + choices=['hand', 'face', 'head'], | ||
| 69 | + help='Train target') | ||
| 70 | +parser.add_argument('--basenet', | ||
| 71 | + default='vgg16_reducedfc.pth', | ||
| 72 | + help='Pretrained base model') | ||
| 73 | +parser.add_argument('--batch_size', | ||
| 74 | + default=16, type=int, | ||
| 75 | + help='Batch size for training') | ||
| 76 | +parser.add_argument('--resume', | ||
| 77 | + default=None, type=str, | ||
| 78 | + help='Checkpoint state_dict file to resume training from') | ||
| 79 | +parser.add_argument('--num_workers', | ||
| 80 | + default=4, type=int, | ||
| 81 | + help='Number of workers used in dataloading') | ||
| 82 | +parser.add_argument('--lr', '--learning-rate', | ||
| 83 | + default=1e-3, type=float, | ||
| 84 | + help='initial learning rate') | ||
| 85 | +parser.add_argument('--momentum', | ||
| 86 | + default=0.9, type=float, | ||
| 87 | + help='Momentum value for optim') | ||
| 88 | +parser.add_argument('--weight_decay', | ||
| 89 | + default=5e-4, type=float, | ||
| 90 | + help='Weight decay for SGD') | ||
| 91 | +parser.add_argument('--gamma', | ||
| 92 | + default=0.1, type=float, | ||
| 93 | + help='Gamma update for SGD') | ||
| 94 | +parser.add_argument('--multigpu', | ||
| 95 | + default=False, type=str2bool, | ||
| 96 | + help='Use mutil Gpu training') | ||
| 97 | +parser.add_argument('--save_folder', | ||
| 98 | + default='weights/', | ||
| 99 | + help='Directory for saving checkpoint models') | ||
| 100 | +# for ascend 910 | ||
| 101 | +parser.add_argument('--device', | ||
| 102 | + default='npu', type=str, | ||
| 103 | + help='Use npu or gpu or cpu to train model') | ||
| 104 | +parser.add_argument('--device_id', default=0, type=int, help='device id') | ||
| 105 | +# parser.add_argument('--addr', default='10.136.181.115', | ||
| 106 | +# type=str, help='master addr') | ||
| 107 | + | ||
| 108 | +# for modelArts | ||
| 109 | +# parser.add_argument('--device_list', default='0,1,2,3,4,5,6,7', | ||
| 110 | +# type=str, help='device id list') | ||
| 111 | +# parser.add_argument('--warm_up_epochs', default=0, type=int, | ||
| 112 | +# help='warm up') | ||
| 113 | +parser.add_argument('--amp', default=False, action='store_true', | ||
| 114 | + help='use amp to train the model') | ||
| 115 | +parser.add_argument('--loss-scale', default=-1, type=float, | ||
| 116 | + help='loss scale using in amp, default -1 means dynamic') | ||
| 117 | + | ||
| 118 | +args = parser.parse_args() | ||
| 119 | + | ||
| 120 | +device = '' | ||
| 121 | +if args.device == 'gpu': | ||
| 122 | + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | ||
| 123 | +elif args.device == 'npu': | ||
| 124 | + device = 'npu:{}'.format(args.device_id) | ||
| 125 | + torch.npu.set_device(device) | ||
| 126 | + print("Use NPU: {} for training".format(args.device_id)) | ||
| 127 | + | ||
| 128 | +if args.device == 'gpu' and torch.cuda.is_available(): | ||
| 129 | + torch.set_default_tensor_type('torch.cuda.FloatTensor') | ||
| 130 | +else: | ||
| 131 | + torch.set_default_tensor_type('torch.FloatTensor') | ||
| 132 | + | ||
| 133 | +if not os.path.exists(args.save_folder): | ||
| 134 | + os.makedirs(args.save_folder) | ||
| 135 | + | ||
| 136 | +train_dataset, val_dataset = dataset_factory(args.dataset) | ||
| 137 | + | ||
| 138 | +train_loader = data.DataLoader(train_dataset, args.batch_size, | ||
| 139 | + num_workers=args.num_workers, | ||
| 140 | + shuffle=True, | ||
| 141 | + collate_fn=detection_collate, | ||
| 142 | + pin_memory=True) | ||
| 143 | + | ||
| 144 | +val_batchsize = args.batch_size // 2 | ||
| 145 | +val_loader = data.DataLoader(val_dataset, val_batchsize, | ||
| 146 | + num_workers=args.num_workers, | ||
| 147 | + shuffle=False, | ||
| 148 | + collate_fn=detection_collate, | ||
| 149 | + pin_memory=True) | ||
| 150 | + | ||
| 151 | +min_loss = np.inf | ||
| 152 | +start_epoch = 0 | ||
| 153 | +s3fd_net = build_s3fd('train', cfg.NUM_CLASSES, args.device) | ||
| 154 | +net = s3fd_net | ||
| 155 | + | ||
| 156 | +if args.resume: | ||
| 157 | + print('Resuming training, loading {}...'.format(args.resume)) | ||
| 158 | + start_epoch = net.load_weights(args.resume) | ||
| 159 | + | ||
| 160 | +else: | ||
| 161 | + vgg_weights = torch.load(args.save_folder + args.basenet) | ||
| 162 | + print('Load base network....') | ||
| 163 | + net.vgg.load_state_dict(vgg_weights) | ||
| 164 | + | ||
| 165 | +if args.device != 'cpu': | ||
| 166 | + if args.multigpu: | ||
| 167 | + net = torch.nn.DataParallel(s3fd_net) | ||
| 168 | + net = net.to(device) | ||
| 169 | + cudnn.benckmark = True | ||
| 170 | + | ||
| 171 | +if not args.resume: | ||
| 172 | + print('Initializing weights...') | ||
| 173 | + s3fd_net.extras.apply(s3fd_net.weights_init) | ||
| 174 | + s3fd_net.loc.apply(s3fd_net.weights_init) | ||
| 175 | + s3fd_net.conf.apply(s3fd_net.weights_init) | ||
| 176 | + | ||
| 177 | +optimizer = optim.SGD(net.parameters(), lr=args.lr, momentum=args.momentum, | ||
| 178 | + weight_decay=args.weight_decay) | ||
| 179 | + | ||
| 180 | +if args.amp: | ||
| 181 | + net, optimizer = amp.initialize( | ||
| 182 | + net, optimizer, opt_level='O1', combine_grad=True) | ||
| 183 | + | ||
| 184 | +criterion = MultiBoxLoss(cfg, args.dataset, args.device) | ||
| 185 | +print('Loading wider dataset...') | ||
| 186 | +print('Using the specified args:') | ||
| 187 | +print(args) | ||
| 188 | + | ||
| 189 | + | ||
| 190 | +def train(): | ||
| 191 | + """ | ||
| 192 | + train | ||
| 193 | + """ | ||
| 194 | + step_index = 0 | ||
| 195 | + iteration = 0 | ||
| 196 | + net.train() | ||
| 197 | + for epoch in range(start_epoch, cfg.EPOCHES): | ||
| 198 | + losses = 0 | ||
| 199 | + for batch_idx, (images, targets) in enumerate(train_loader): | ||
| 200 | + if args.device != 'cpu': | ||
| 201 | + images = Variable(images.to(device)) | ||
| 202 | + targets = [Variable(ann.to(device), volatile=True) | ||
| 203 | + for ann in targets] | ||
| 204 | + else: | ||
| 205 | + images = Variable(images) | ||
| 206 | + targets = [Variable(ann, volatile=True) for ann in targets] | ||
| 207 | + | ||
| 208 | + if iteration in cfg.LR_STEPS: | ||
| 209 | + step_index += 1 | ||
| 210 | + adjust_learning_rate(optimizer, args.gamma, step_index) | ||
| 211 | + | ||
| 212 | + t0 = time.time() | ||
| 213 | + out = net(images) | ||
| 214 | + | ||
| 215 | + # backprop | ||
| 216 | + optimizer.zero_grad() | ||
| 217 | + | ||
| 218 | + loss_l, loss_c = criterion(out, targets) | ||
| 219 | + loss = loss_l + loss_c | ||
| 220 | + | ||
| 221 | + if args.amp: | ||
| 222 | + with amp.scale_loss(loss, optimizer) as scaled_loss: | ||
| 223 | + scaled_loss.backward() | ||
| 224 | + else: | ||
| 225 | + loss.backward() | ||
| 226 | + | ||
| 227 | + optimizer.step() | ||
| 228 | + t1 = time.time() | ||
| 229 | + | ||
| 230 | + if args.device == 'npu': | ||
| 231 | + losses += loss.cpu().item() | ||
| 232 | + else: | ||
| 233 | + losses += loss.item() | ||
| 234 | + | ||
| 235 | + if iteration % 10 == 0: | ||
| 236 | + tloss = losses / (batch_idx + 1) | ||
| 237 | + print('Timer: %.4f' % (t1 - t0)) | ||
| 238 | + print('epoch:' + repr(epoch) + ' || iter:' + | ||
| 239 | + repr(iteration) + ' || Loss:%.4f' % (tloss)) | ||
| 240 | + print('->> conf loss:{:.4f} || loc loss:{:.4f}'.format( | ||
| 241 | + loss_c.item(), loss_l.item())) | ||
| 242 | + print('->>lr:{:.6f}'.format(optimizer.param_groups[0]['lr'])) | ||
| 243 | + | ||
| 244 | + if iteration != 0 and iteration % 5000 == 0: | ||
| 245 | + print('Saving state, iter:', iteration) | ||
| 246 | + file = 'sfd_' + args.dataset + '_' + repr(iteration) + '.pth' | ||
| 247 | + torch.save(s3fd_net.state_dict(), | ||
| 248 | + os.path.join(args.save_folder, file)) | ||
| 249 | + iteration += 1 | ||
| 250 | + | ||
| 251 | + val(epoch) | ||
| 252 | + if iteration == cfg.MAX_STEPS: | ||
| 253 | + break | ||
| 254 | + | ||
| 255 | + | ||
| 256 | +def val(epoch): | ||
| 257 | + """ | ||
| 258 | + val | ||
| 259 | + """ | ||
| 260 | + net.eval() | ||
| 261 | + loc_loss = 0 | ||
| 262 | + conf_loss = 0 | ||
| 263 | + step = 0 | ||
| 264 | + t1 = time.time() | ||
| 265 | + for batch_idx, (images, targets) in enumerate(val_loader): | ||
| 266 | + if args.device != 'cpu': | ||
| 267 | + images = Variable(images.to(device)) | ||
| 268 | + targets = [Variable(ann.to(device), volatile=True) | ||
| 269 | + for ann in targets] | ||
| 270 | + else: | ||
| 271 | + images = Variable(images) | ||
| 272 | + targets = [Variable(ann, volatile=True) for ann in targets] | ||
| 273 | + | ||
| 274 | + out = net(images) | ||
| 275 | + loss_l, loss_c = criterion(out, targets) | ||
| 276 | + | ||
| 277 | + loc_loss += loss_l.item() | ||
| 278 | + conf_loss += loss_c.item() | ||
| 279 | + step += 1 | ||
| 280 | + | ||
| 281 | + tloss = (loc_loss + conf_loss) / step | ||
| 282 | + t2 = time.time() | ||
| 283 | + print('Timer: %.4f' % (t2 - t1)) | ||
| 284 | + print('test epoch:' + repr(epoch) + ' || Loss:%.4f' % (tloss)) | ||
| 285 | + | ||
| 286 | + global min_loss | ||
| 287 | + if tloss < min_loss: | ||
| 288 | + print('Saving best state,epoch', epoch) | ||
| 289 | + file = 'sfd_{}.pth'.format(args.dataset) | ||
| 290 | + torch.save(s3fd_net.state_dict(), os.path.join( | ||
| 291 | + args.save_folder, file)) | ||
| 292 | + min_loss = tloss | ||
| 293 | + | ||
| 294 | + states = { | ||
| 295 | + 'epoch': epoch, | ||
| 296 | + 'weight': s3fd_net.state_dict(), | ||
| 297 | + } | ||
| 298 | + file = 'sfd_{}_checkpoint.pth'.format(args.dataset) | ||
| 299 | + torch.save(states, os.path.join( | ||
| 300 | + args.save_folder, file)) | ||
| 301 | + | ||
| 302 | + | ||
| 303 | +def adjust_learning_rate(optimizer, gamma, step): | ||
| 304 | + """Sets the learning rate to the initial LR decayed by 10 at every | ||
| 305 | + specified step | ||
| 306 | + # Adapted from PyTorch Imagenet example: | ||
| 307 | + # https://github.com/pytorch/examples/blob/master/imagenet/main.py | ||
| 308 | + """ | ||
| 309 | + lr = args.lr * (gamma ** (step)) | ||
| 310 | + for param_group in optimizer.param_groups: | ||
| 311 | + param_group['lr'] = lr | ||
| 312 | + | ||
| 313 | + | ||
| 314 | +if __name__ == '__main__': | ||
| 315 | + train() | ||
| @@ -0,0 +1,2 @@ | |||
| 1 | + | ||
| 2 | + | ||