已开启
【众智】【重庆大学】【ID2934】【s3fd】NPU精度达标,提交精度验收 #16
AtomGit-Bot创建于 2022年12月1日
【众智】【重庆大学】【ID2934】【s3fd】NPU精度达标,提交精度验收 #16
已开启
AtomGit-Bot创建于 2022年12月1日
refs/pull/16/head合入到master
25 个文件变更+3552-1
DPyTorch+0-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+ ![img1.png](img1.png)
231+ NPU训练loss如下:
232+ 
233+ ![img2.png](img2.png)
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+ 
@@ -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+