已开启
【众智】【西安电子科技大学】【ID1232】【Deep_sort】NPU执行通过,提交功能验收 #2
AtomGit-Bot创建于 2022年3月24日
【众智】【西安电子科技大学】【ID1232】【Deep_sort】NPU执行通过,提交功能验收 #2
已开启
AtomGit-Bot创建于 2022年3月24日
refs/pull/2/head合入到master
24 个文件变更+3934-0
@@ -0,0 +1,674 @@
1+ GNU GENERAL PUBLIC LICENSE
2+ Version 3, 29 June 2007
3+ 
4+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5+ Everyone is permitted to copy and distribute verbatim copies
6+ of this license document, but changing it is not allowed.
7+ 
8+ Preamble
9+ 
10+ The GNU General Public License is a free, copyleft license for
11+software and other kinds of works.
12+ 
13+ The licenses for most software and other practical works are designed
14+to take away your freedom to share and change the works. By contrast,
15+the GNU General Public License is intended to guarantee your freedom to
16+share and change all versions of a program--to make sure it remains free
17+software for all its users. We, the Free Software Foundation, use the
18+GNU General Public License for most of our software; it applies also to
19+any other work released this way by its authors. You can apply it to
20+your programs, too.
21+ 
22+ When we speak of free software, we are referring to freedom, not
23+price. Our General Public Licenses are designed to make sure that you
24+have the freedom to distribute copies of free software (and charge for
25+them if you wish), that you receive source code or can get it if you
26+want it, that you can change the software or use pieces of it in new
27+free programs, and that you know you can do these things.
28+ 
29+ To protect your rights, we need to prevent others from denying you
30+these rights or asking you to surrender the rights. Therefore, you have
31+certain responsibilities if you distribute copies of the software, or if
32+you modify it: responsibilities to respect the freedom of others.
33+ 
34+ For example, if you distribute copies of such a program, whether
35+gratis or for a fee, you must pass on to the recipients the same
36+freedoms that you received. You must make sure that they, too, receive
37+or can get the source code. And you must show them these terms so they
38+know their rights.
39+ 
40+ Developers that use the GNU GPL protect your rights with two steps:
41+(1) assert copyright on the software, and (2) offer you this License
42+giving you legal permission to copy, distribute and/or modify it.
43+ 
44+ For the developers' and authors' protection, the GPL clearly explains
45+that there is no warranty for this free software. For both users' and
46+authors' sake, the GPL requires that modified versions be marked as
47+changed, so that their problems will not be attributed erroneously to
48+authors of previous versions.
49+ 
50+ Some devices are designed to deny users access to install or run
51+modified versions of the software inside them, although the manufacturer
52+can do so. This is fundamentally incompatible with the aim of
53+protecting users' freedom to change the software. The systematic
54+pattern of such abuse occurs in the area of products for individuals to
55+use, which is precisely where it is most unacceptable. Therefore, we
56+have designed this version of the GPL to prohibit the practice for those
57+products. If such problems arise substantially in other domains, we
58+stand ready to extend this provision to those domains in future versions
59+of the GPL, as needed to protect the freedom of users.
60+ 
61+ Finally, every program is threatened constantly by software patents.
62+States should not allow patents to restrict development and use of
63+software on general-purpose computers, but in those that do, we wish to
64+avoid the special danger that patents applied to a free program could
65+make it effectively proprietary. To prevent this, the GPL assures that
66+patents cannot be used to render the program non-free.
67+ 
68+ The precise terms and conditions for copying, distribution and
69+modification follow.
70+ 
71+ TERMS AND CONDITIONS
72+ 
73+ 0. Definitions.
74+ 
75+ "This License" refers to version 3 of the GNU General Public License.
76+ 
77+ "Copyright" also means copyright-like laws that apply to other kinds of
78+works, such as semiconductor masks.
79+ 
80+ "The Program" refers to any copyrightable work licensed under this
81+License. Each licensee is addressed as "you". "Licensees" and
82+"recipients" may be individuals or organizations.
83+ 
84+ To "modify" a work means to copy from or adapt all or part of the work
85+in a fashion requiring copyright permission, other than the making of an
86+exact copy. The resulting work is called a "modified version" of the
87+earlier work or a work "based on" the earlier work.
88+ 
89+ A "covered work" means either the unmodified Program or a work based
90+on the Program.
91+ 
92+ To "propagate" a work means to do anything with it that, without
93+permission, would make you directly or secondarily liable for
94+infringement under applicable copyright law, except executing it on a
95+computer or modifying a private copy. Propagation includes copying,
96+distribution (with or without modification), making available to the
97+public, and in some countries other activities as well.
98+ 
99+ To "convey" a work means any kind of propagation that enables other
100+parties to make or receive copies. Mere interaction with a user through
101+a computer network, with no transfer of a copy, is not conveying.
102+ 
103+ An interactive user interface displays "Appropriate Legal Notices"
104+to the extent that it includes a convenient and prominently visible
105+feature that (1) displays an appropriate copyright notice, and (2)
106+tells the user that there is no warranty for the work (except to the
107+extent that warranties are provided), that licensees may convey the
108+work under this License, and how to view a copy of this License. If
109+the interface presents a list of user commands or options, such as a
110+menu, a prominent item in the list meets this criterion.
111+ 
112+ 1. Source Code.
113+ 
114+ The "source code" for a work means the preferred form of the work
115+for making modifications to it. "Object code" means any non-source
116+form of a work.
117+ 
118+ A "Standard Interface" means an interface that either is an official
119+standard defined by a recognized standards body, or, in the case of
120+interfaces specified for a particular programming language, one that
121+is widely used among developers working in that language.
122+ 
123+ The "System Libraries" of an executable work include anything, other
124+than the work as a whole, that (a) is included in the normal form of
125+packaging a Major Component, but which is not part of that Major
126+Component, and (b) serves only to enable use of the work with that
127+Major Component, or to implement a Standard Interface for which an
128+implementation is available to the public in source code form. A
129+"Major Component", in this context, means a major essential component
130+(kernel, window system, and so on) of the specific operating system
131+(if any) on which the executable work runs, or a compiler used to
132+produce the work, or an object code interpreter used to run it.
133+ 
134+ The "Corresponding Source" for a work in object code form means all
135+the source code needed to generate, install, and (for an executable
136+work) run the object code and to modify the work, including scripts to
137+control those activities. However, it does not include the work's
138+System Libraries, or general-purpose tools or generally available free
139+programs which are used unmodified in performing those activities but
140+which are not part of the work. For example, Corresponding Source
141+includes interface definition files associated with source files for
142+the work, and the source code for shared libraries and dynamically
143+linked subprograms that the work is specifically designed to require,
144+such as by intimate data communication or control flow between those
145+subprograms and other parts of the work.
146+ 
147+ The Corresponding Source need not include anything that users
148+can regenerate automatically from other parts of the Corresponding
149+Source.
150+ 
151+ The Corresponding Source for a work in source code form is that
152+same work.
153+ 
154+ 2. Basic Permissions.
155+ 
156+ All rights granted under this License are granted for the term of
157+copyright on the Program, and are irrevocable provided the stated
158+conditions are met. This License explicitly affirms your unlimited
159+permission to run the unmodified Program. The output from running a
160+covered work is covered by this License only if the output, given its
161+content, constitutes a covered work. This License acknowledges your
162+rights of fair use or other equivalent, as provided by copyright law.
163+ 
164+ You may make, run and propagate covered works that you do not
165+convey, without conditions so long as your license otherwise remains
166+in force. You may convey covered works to others for the sole purpose
167+of having them make modifications exclusively for you, or provide you
168+with facilities for running those works, provided that you comply with
169+the terms of this License in conveying all material for which you do
170+not control copyright. Those thus making or running the covered works
171+for you must do so exclusively on your behalf, under your direction
172+and control, on terms that prohibit them from making any copies of
173+your copyrighted material outside their relationship with you.
174+ 
175+ Conveying under any other circumstances is permitted solely under
176+the conditions stated below. Sublicensing is not allowed; section 10
177+makes it unnecessary.
178+ 
179+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180+ 
181+ No covered work shall be deemed part of an effective technological
182+measure under any applicable law fulfilling obligations under article
183+11 of the WIPO copyright treaty adopted on 20 December 1996, or
184+similar laws prohibiting or restricting circumvention of such
185+measures.
186+ 
187+ When you convey a covered work, you waive any legal power to forbid
188+circumvention of technological measures to the extent such circumvention
189+is effected by exercising rights under this License with respect to
190+the covered work, and you disclaim any intention to limit operation or
191+modification of the work as a means of enforcing, against the work's
192+users, your or third parties' legal rights to forbid circumvention of
193+technological measures.
194+ 
195+ 4. Conveying Verbatim Copies.
196+ 
197+ You may convey verbatim copies of the Program's source code as you
198+receive it, in any medium, provided that you conspicuously and
199+appropriately publish on each copy an appropriate copyright notice;
200+keep intact all notices stating that this License and any
201+non-permissive terms added in accord with section 7 apply to the code;
202+keep intact all notices of the absence of any warranty; and give all
203+recipients a copy of this License along with the Program.
204+ 
205+ You may charge any price or no price for each copy that you convey,
206+and you may offer support or warranty protection for a fee.
207+ 
208+ 5. Conveying Modified Source Versions.
209+ 
210+ You may convey a work based on the Program, or the modifications to
211+produce it from the Program, in the form of source code under the
212+terms of section 4, provided that you also meet all of these conditions:
213+ 
214+ a) The work must carry prominent notices stating that you modified
215+ it, and giving a relevant date.
216+ 
217+ b) The work must carry prominent notices stating that it is
218+ released under this License and any conditions added under section
219+ 7. This requirement modifies the requirement in section 4 to
220+ "keep intact all notices".
221+ 
222+ c) You must license the entire work, as a whole, under this
223+ License to anyone who comes into possession of a copy. This
224+ License will therefore apply, along with any applicable section 7
225+ additional terms, to the whole of the work, and all its parts,
226+ regardless of how they are packaged. This License gives no
227+ permission to license the work in any other way, but it does not
228+ invalidate such permission if you have separately received it.
229+ 
230+ d) If the work has interactive user interfaces, each must display
231+ Appropriate Legal Notices; however, if the Program has interactive
232+ interfaces that do not display Appropriate Legal Notices, your
233+ work need not make them do so.
234+ 
235+ A compilation of a covered work with other separate and independent
236+works, which are not by their nature extensions of the covered work,
237+and which are not combined with it such as to form a larger program,
238+in or on a volume of a storage or distribution medium, is called an
239+"aggregate" if the compilation and its resulting copyright are not
240+used to limit the access or legal rights of the compilation's users
241+beyond what the individual works permit. Inclusion of a covered work
242+in an aggregate does not cause this License to apply to the other
243+parts of the aggregate.
244+ 
245+ 6. Conveying Non-Source Forms.
246+ 
247+ You may convey a covered work in object code form under the terms
248+of sections 4 and 5, provided that you also convey the
249+machine-readable Corresponding Source under the terms of this License,
250+in one of these ways:
251+ 
252+ a) Convey the object code in, or embodied in, a physical product
253+ (including a physical distribution medium), accompanied by the
254+ Corresponding Source fixed on a durable physical medium
255+ customarily used for software interchange.
256+ 
257+ b) Convey the object code in, or embodied in, a physical product
258+ (including a physical distribution medium), accompanied by a
259+ written offer, valid for at least three years and valid for as
260+ long as you offer spare parts or customer support for that product
261+ model, to give anyone who possesses the object code either (1) a
262+ copy of the Corresponding Source for all the software in the
263+ product that is covered by this License, on a durable physical
264+ medium customarily used for software interchange, for a price no
265+ more than your reasonable cost of physically performing this
266+ conveying of source, or (2) access to copy the
267+ Corresponding Source from a network server at no charge.
268+ 
269+ c) Convey individual copies of the object code with a copy of the
270+ written offer to provide the Corresponding Source. This
271+ alternative is allowed only occasionally and noncommercially, and
272+ only if you received the object code with such an offer, in accord
273+ with subsection 6b.
274+ 
275+ d) Convey the object code by offering access from a designated
276+ place (gratis or for a charge), and offer equivalent access to the
277+ Corresponding Source in the same way through the same place at no
278+ further charge. You need not require recipients to copy the
279+ Corresponding Source along with the object code. If the place to
280+ copy the object code is a network server, the Corresponding Source
281+ may be on a different server (operated by you or a third party)
282+ that supports equivalent copying facilities, provided you maintain
283+ clear directions next to the object code saying where to find the
284+ Corresponding Source. Regardless of what server hosts the
285+ Corresponding Source, you remain obligated to ensure that it is
286+ available for as long as needed to satisfy these requirements.
287+ 
288+ e) Convey the object code using peer-to-peer transmission, provided
289+ you inform other peers where the object code and Corresponding
290+ Source of the work are being offered to the general public at no
291+ charge under subsection 6d.
292+ 
293+ A separable portion of the object code, whose source code is excluded
294+from the Corresponding Source as a System Library, need not be
295+included in conveying the object code work.
296+ 
297+ A "User Product" is either (1) a "consumer product", which means any
298+tangible personal property which is normally used for personal, family,
299+or household purposes, or (2) anything designed or sold for incorporation
300+into a dwelling. In determining whether a product is a consumer product,
301+doubtful cases shall be resolved in favor of coverage. For a particular
302+product received by a particular user, "normally used" refers to a
303+typical or common use of that class of product, regardless of the status
304+of the particular user or of the way in which the particular user
305+actually uses, or expects or is expected to use, the product. A product
306+is a consumer product regardless of whether the product has substantial
307+commercial, industrial or non-consumer uses, unless such uses represent
308+the only significant mode of use of the product.
309+ 
310+ "Installation Information" for a User Product means any methods,
311+procedures, authorization keys, or other information required to install
312+and execute modified versions of a covered work in that User Product from
313+a modified version of its Corresponding Source. The information must
314+suffice to ensure that the continued functioning of the modified object
315+code is in no case prevented or interfered with solely because
316+modification has been made.
317+ 
318+ If you convey an object code work under this section in, or with, or
319+specifically for use in, a User Product, and the conveying occurs as
320+part of a transaction in which the right of possession and use of the
321+User Product is transferred to the recipient in perpetuity or for a
322+fixed term (regardless of how the transaction is characterized), the
323+Corresponding Source conveyed under this section must be accompanied
324+by the Installation Information. But this requirement does not apply
325+if neither you nor any third party retains the ability to install
326+modified object code on the User Product (for example, the work has
327+been installed in ROM).
328+ 
329+ The requirement to provide Installation Information does not include a
330+requirement to continue to provide support service, warranty, or updates
331+for a work that has been modified or installed by the recipient, or for
332+the User Product in which it has been modified or installed. Access to a
333+network may be denied when the modification itself materially and
334+adversely affects the operation of the network or violates the rules and
335+protocols for communication across the network.
336+ 
337+ Corresponding Source conveyed, and Installation Information provided,
338+in accord with this section must be in a format that is publicly
339+documented (and with an implementation available to the public in
340+source code form), and must require no special password or key for
341+unpacking, reading or copying.
342+ 
343+ 7. Additional Terms.
344+ 
345+ "Additional permissions" are terms that supplement the terms of this
346+License by making exceptions from one or more of its conditions.
347+Additional permissions that are applicable to the entire Program shall
348+be treated as though they were included in this License, to the extent
349+that they are valid under applicable law. If additional permissions
350+apply only to part of the Program, that part may be used separately
351+under those permissions, but the entire Program remains governed by
352+this License without regard to the additional permissions.
353+ 
354+ When you convey a copy of a covered work, you may at your option
355+remove any additional permissions from that copy, or from any part of
356+it. (Additional permissions may be written to require their own
357+removal in certain cases when you modify the work.) You may place
358+additional permissions on material, added by you to a covered work,
359+for which you have or can give appropriate copyright permission.
360+ 
361+ Notwithstanding any other provision of this License, for material you
362+add to a covered work, you may (if authorized by the copyright holders of
363+that material) supplement the terms of this License with terms:
364+ 
365+ a) Disclaiming warranty or limiting liability differently from the
366+ terms of sections 15 and 16 of this License; or
367+ 
368+ b) Requiring preservation of specified reasonable legal notices or
369+ author attributions in that material or in the Appropriate Legal
370+ Notices displayed by works containing it; or
371+ 
372+ c) Prohibiting misrepresentation of the origin of that material, or
373+ requiring that modified versions of such material be marked in
374+ reasonable ways as different from the original version; or
375+ 
376+ d) Limiting the use for publicity purposes of names of licensors or
377+ authors of the material; or
378+ 
379+ e) Declining to grant rights under trademark law for use of some
380+ trade names, trademarks, or service marks; or
381+ 
382+ f) Requiring indemnification of licensors and authors of that
383+ material by anyone who conveys the material (or modified versions of
384+ it) with contractual assumptions of liability to the recipient, for
385+ any liability that these contractual assumptions directly impose on
386+ those licensors and authors.
387+ 
388+ All other non-permissive additional terms are considered "further
389+restrictions" within the meaning of section 10. If the Program as you
390+received it, or any part of it, contains a notice stating that it is
391+governed by this License along with a term that is a further
392+restriction, you may remove that term. If a license document contains
393+a further restriction but permits relicensing or conveying under this
394+License, you may add to a covered work material governed by the terms
395+of that license document, provided that the further restriction does
396+not survive such relicensing or conveying.
397+ 
398+ If you add terms to a covered work in accord with this section, you
399+must place, in the relevant source files, a statement of the
400+additional terms that apply to those files, or a notice indicating
401+where to find the applicable terms.
402+ 
403+ Additional terms, permissive or non-permissive, may be stated in the
404+form of a separately written license, or stated as exceptions;
405+the above requirements apply either way.
406+ 
407+ 8. Termination.
408+ 
409+ You may not propagate or modify a covered work except as expressly
410+provided under this License. Any attempt otherwise to propagate or
411+modify it is void, and will automatically terminate your rights under
412+this License (including any patent licenses granted under the third
413+paragraph of section 11).
414+ 
415+ However, if you cease all violation of this License, then your
416+license from a particular copyright holder is reinstated (a)
417+provisionally, unless and until the copyright holder explicitly and
418+finally terminates your license, and (b) permanently, if the copyright
419+holder fails to notify you of the violation by some reasonable means
420+prior to 60 days after the cessation.
421+ 
422+ Moreover, your license from a particular copyright holder is
423+reinstated permanently if the copyright holder notifies you of the
424+violation by some reasonable means, this is the first time you have
425+received notice of violation of this License (for any work) from that
426+copyright holder, and you cure the violation prior to 30 days after
427+your receipt of the notice.
428+ 
429+ Termination of your rights under this section does not terminate the
430+licenses of parties who have received copies or rights from you under
431+this License. If your rights have been terminated and not permanently
432+reinstated, you do not qualify to receive new licenses for the same
433+material under section 10.
434+ 
435+ 9. Acceptance Not Required for Having Copies.
436+ 
437+ You are not required to accept this License in order to receive or
438+run a copy of the Program. Ancillary propagation of a covered work
439+occurring solely as a consequence of using peer-to-peer transmission
440+to receive a copy likewise does not require acceptance. However,
441+nothing other than this License grants you permission to propagate or
442+modify any covered work. These actions infringe copyright if you do
443+not accept this License. Therefore, by modifying or propagating a
444+covered work, you indicate your acceptance of this License to do so.
445+ 
446+ 10. Automatic Licensing of Downstream Recipients.
447+ 
448+ Each time you convey a covered work, the recipient automatically
449+receives a license from the original licensors, to run, modify and
450+propagate that work, subject to this License. You are not responsible
451+for enforcing compliance by third parties with this License.
452+ 
453+ An "entity transaction" is a transaction transferring control of an
454+organization, or substantially all assets of one, or subdividing an
455+organization, or merging organizations. If propagation of a covered
456+work results from an entity transaction, each party to that
457+transaction who receives a copy of the work also receives whatever
458+licenses to the work the party's predecessor in interest had or could
459+give under the previous paragraph, plus a right to possession of the
460+Corresponding Source of the work from the predecessor in interest, if
461+the predecessor has it or can get it with reasonable efforts.
462+ 
463+ You may not impose any further restrictions on the exercise of the
464+rights granted or affirmed under this License. For example, you may
465+not impose a license fee, royalty, or other charge for exercise of
466+rights granted under this License, and you may not initiate litigation
467+(including a cross-claim or counterclaim in a lawsuit) alleging that
468+any patent claim is infringed by making, using, selling, offering for
469+sale, or importing the Program or any portion of it.
470+ 
471+ 11. Patents.
472+ 
473+ A "contributor" is a copyright holder who authorizes use under this
474+License of the Program or a work on which the Program is based. The
475+work thus licensed is called the contributor's "contributor version".
476+ 
477+ A contributor's "essential patent claims" are all patent claims
478+owned or controlled by the contributor, whether already acquired or
479+hereafter acquired, that would be infringed by some manner, permitted
480+by this License, of making, using, or selling its contributor version,
481+but do not include claims that would be infringed only as a
482+consequence of further modification of the contributor version. For
483+purposes of this definition, "control" includes the right to grant
484+patent sublicenses in a manner consistent with the requirements of
485+this License.
486+ 
487+ Each contributor grants you a non-exclusive, worldwide, royalty-free
488+patent license under the contributor's essential patent claims, to
489+make, use, sell, offer for sale, import and otherwise run, modify and
490+propagate the contents of its contributor version.
491+ 
492+ In the following three paragraphs, a "patent license" is any express
493+agreement or commitment, however denominated, not to enforce a patent
494+(such as an express permission to practice a patent or covenant not to
495+sue for patent infringement). To "grant" such a patent license to a
496+party means to make such an agreement or commitment not to enforce a
497+patent against the party.
498+ 
499+ If you convey a covered work, knowingly relying on a patent license,
500+and the Corresponding Source of the work is not available for anyone
501+to copy, free of charge and under the terms of this License, through a
502+publicly available network server or other readily accessible means,
503+then you must either (1) cause the Corresponding Source to be so
504+available, or (2) arrange to deprive yourself of the benefit of the
505+patent license for this particular work, or (3) arrange, in a manner
506+consistent with the requirements of this License, to extend the patent
507+license to downstream recipients. "Knowingly relying" means you have
508+actual knowledge that, but for the patent license, your conveying the
509+covered work in a country, or your recipient's use of the covered work
510+in a country, would infringe one or more identifiable patents in that
511+country that you have reason to believe are valid.
512+ 
513+ If, pursuant to or in connection with a single transaction or
514+arrangement, you convey, or propagate by procuring conveyance of, a
515+covered work, and grant a patent license to some of the parties
516+receiving the covered work authorizing them to use, propagate, modify
517+or convey a specific copy of the covered work, then the patent license
518+you grant is automatically extended to all recipients of the covered
519+work and works based on it.
520+ 
521+ A patent license is "discriminatory" if it does not include within
522+the scope of its coverage, prohibits the exercise of, or is
523+conditioned on the non-exercise of one or more of the rights that are
524+specifically granted under this License. You may not convey a covered
525+work if you are a party to an arrangement with a third party that is
526+in the business of distributing software, under which you make payment
527+to the third party based on the extent of your activity of conveying
528+the work, and under which the third party grants, to any of the
529+parties who would receive the covered work from you, a discriminatory
530+patent license (a) in connection with copies of the covered work
531+conveyed by you (or copies made from those copies), or (b) primarily
532+for and in connection with specific products or compilations that
533+contain the covered work, unless you entered into that arrangement,
534+or that patent license was granted, prior to 28 March 2007.
535+ 
536+ Nothing in this License shall be construed as excluding or limiting
537+any implied license or other defenses to infringement that may
538+otherwise be available to you under applicable patent law.
539+ 
540+ 12. No Surrender of Others' Freedom.
541+ 
542+ If conditions are imposed on you (whether by court order, agreement or
543+otherwise) that contradict the conditions of this License, they do not
544+excuse you from the conditions of this License. If you cannot convey a
545+covered work so as to satisfy simultaneously your obligations under this
546+License and any other pertinent obligations, then as a consequence you may
547+not convey it at all. For example, if you agree to terms that obligate you
548+to collect a royalty for further conveying from those to whom you convey
549+the Program, the only way you could satisfy both those terms and this
550+License would be to refrain entirely from conveying the Program.
551+ 
552+ 13. Use with the GNU Affero General Public License.
553+ 
554+ Notwithstanding any other provision of this License, you have
555+permission to link or combine any covered work with a work licensed
556+under version 3 of the GNU Affero General Public License into a single
557+combined work, and to convey the resulting work. The terms of this
558+License will continue to apply to the part which is the covered work,
559+but the special requirements of the GNU Affero General Public License,
560+section 13, concerning interaction through a network will apply to the
561+combination as such.
562+ 
563+ 14. Revised Versions of this License.
564+ 
565+ The Free Software Foundation may publish revised and/or new versions of
566+the GNU General Public License from time to time. Such new versions will
567+be similar in spirit to the present version, but may differ in detail to
568+address new problems or concerns.
569+ 
570+ Each version is given a distinguishing version number. If the
571+Program specifies that a certain numbered version of the GNU General
572+Public License "or any later version" applies to it, you have the
573+option of following the terms and conditions either of that numbered
574+version or of any later version published by the Free Software
575+Foundation. If the Program does not specify a version number of the
576+GNU General Public License, you may choose any version ever published
577+by the Free Software Foundation.
578+ 
579+ If the Program specifies that a proxy can decide which future
580+versions of the GNU General Public License can be used, that proxy's
581+public statement of acceptance of a version permanently authorizes you
582+to choose that version for the Program.
583+ 
584+ Later license versions may give you additional or different
585+permissions. However, no additional obligations are imposed on any
586+author or copyright holder as a result of your choosing to follow a
587+later version.
588+ 
589+ 15. Disclaimer of Warranty.
590+ 
591+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599+ 
600+ 16. Limitation of Liability.
601+ 
602+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610+SUCH DAMAGES.
611+ 
612+ 17. Interpretation of Sections 15 and 16.
613+ 
614+ If the disclaimer of warranty and limitation of liability provided
615+above cannot be given local legal effect according to their terms,
616+reviewing courts shall apply local law that most closely approximates
617+an absolute waiver of all civil liability in connection with the
618+Program, unless a warranty or assumption of liability accompanies a
619+copy of the Program in return for a fee.
620+ 
621+ END OF TERMS AND CONDITIONS
622+ 
623+ How to Apply These Terms to Your New Programs
624+ 
625+ If you develop a new program, and you want it to be of the greatest
626+possible use to the public, the best way to achieve this is to make it
627+free software which everyone can redistribute and change under these terms.
628+ 
629+ To do so, attach the following notices to the program. It is safest
630+to attach them to the start of each source file to most effectively
631+state the exclusion of warranty; and each file should have at least
632+the "copyright" line and a pointer to where the full notice is found.
633+ 
634+ <one line to give the program's name and a brief idea of what it does.>
635+ Copyright (C) <year> <name of author>
636+ 
637+ This program is free software: you can redistribute it and/or modify
638+ it under the terms of the GNU General Public License as published by
639+ the Free Software Foundation, either version 3 of the License, or
640+ (at your option) any later version.
641+ 
642+ This program is distributed in the hope that it will be useful,
643+ but WITHOUT ANY WARRANTY; without even the implied warranty of
644+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645+ GNU General Public License for more details.
646+ 
647+ You should have received a copy of the GNU General Public License
648+ along with this program. If not, see <http://www.gnu.org/licenses/>.
649+ 
650+Also add information on how to contact you by electronic and paper mail.
651+ 
652+ If the program does terminal interaction, make it output a short
653+notice like this when it starts in an interactive mode:
654+ 
655+ <program> Copyright (C) <year> <name of author>
656+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657+ This is free software, and you are welcome to redistribute it
658+ under certain conditions; type `show c' for details.
659+ 
660+The hypothetical commands `show w' and `show c' should show the appropriate
661+parts of the General Public License. Of course, your program's commands
662+might be different; for a GUI interface, you would use an "about box".
663+ 
664+ You should also get your employer (if you work as a programmer) or school,
665+if any, to sign a "copyright disclaimer" for the program, if necessary.
666+For more information on this, and how to apply and follow the GNU GPL, see
667+<http://www.gnu.org/licenses/>.
668+ 
669+ The GNU General Public License does not permit incorporating your program
670+into proprietary programs. If your program is a subroutine library, you
671+may consider it more useful to permit linking proprietary applications with
672+the library. If this is what you want to do, use the GNU Lesser General
673+Public License instead of this License. But first, please read
674+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
@@ -0,0 +1,165 @@
1+# cosine_metric_learning
2+ 
3+## Introduction
4+ 
5+This repository contains code for training a metric feature representation to be
6+used with the [deep_sort tracker](https://github.com/nwojke/deep_sort). The
7+approach is described in
8+ 
9+ @inproceedings{Wojke2018deep,
10+ title={Deep Cosine Metric Learning for Person Re-identification},
11+ author={Wojke, Nicolai and Bewley, Alex},
12+ booktitle={2018 IEEE Winter Conference on Applications of Computer Vision (WACV)},
13+ year={2018},
14+ pages={748--756},
15+ organization={IEEE},
16+ doi={10.1109/WACV.2018.00087}
17+ }
18+ 
19+Pre-trained models used in the paper can be found
20+[here](https://drive.google.com/open?id=13HtkxD6ggcrGJLWaUcqgXl2UO6-p4PK0).
21+A preprint of the paper is available [here](http://elib.dlr.de/116408/).
22+The repository comes with code to train a model on the
23+[Market1501](http://www.liangzheng.org/Project/project_reid.html)
24+and [MARS](http://www.liangzheng.com.cn/Project/project_mars.html) datasets.
25+ 
26+## Training on Market1501
27+ 
28+The following description assumes you have downloaded the Market1501 dataset to
29+``./Market-1501-v15.09.15``. The following command starts training
30+using the cosine-softmax classifier described in the above paper:
31+```
32+python train_market1501.py \
33+ --dataset_dir=./Market-1501-v15.09.15/ \
34+ --loss_mode=cosine-softmax \
35+ --log_dir=./output/market1501/ \
36+ --run_id=cosine-softmax
37+```
38+This will create a directory `./output/market1501/cosine-softmax` where
39+TensorFlow checkpoints are stored and which can be monitored using
40+``tensorboard``:
41+```
42+tensorboard --logdir ./output/market1501/cosine-softmax --port 6006
43+```
44+The code splits off 10% of the training data for validation.
45+Concurrently to training, run the following command to run CMC evaluation
46+metrics on the validation set:
47+```
48+CUDA_VISIBLE_DEVICES="" python train_market1501.py \
49+ --mode=eval \
50+ --dataset_dir=./Market-1501-v15.09.15/ \
51+ --loss_mode=cosine-softmax \
52+ --log_dir=./output/market1501/ \
53+ --run_id=cosine-softmax \
54+ --eval_log_dir=./eval_output/market1501
55+```
56+The command will block indefinitely to monitor the training directory for saved
57+checkpoints and each stored checkpoint in the training directory is evaluated on
58+the validation set. The results of this evaluation are stored in
59+``./eval_output/market1501/cosine-softmax`` to be monitored using
60+``tensorboard``:
61+```
62+tensorboard --logdir ./eval_output/market1501/cosine-softmax --port 6007
63+```
64+ 
65+## Training on MARS
66+ 
67+To train on MARS, download the
68+[evaluation software](https://github.com/liangzheng06/MARS-evaluation) and
69+extract ``bbox_train.zip`` and ``bbox_test.zip`` from the
70+[dataset website](http://www.liangzheng.com.cn/Project/project_mars.html)
71+into the evaluation software directory. The following description assumes they
72+are stored in ``./MARS-evaluation-master/bbox_train`` and
73+``./MARS-evaluation-master/bbox_test``. Training can be started with the following
74+command:
75+```
76+python train_mars.py \
77+ --dataset_dir=./MARS-evaluation-master \
78+ --loss_mode=cosine-softmax \
79+ --log_dir=./output/mars/ \
80+ --run_id=cosine-softmax
81+```
82+Again, this will create a directory `./output/mars/cosine-softmax` where
83+TensorFlow checkpoints are stored and which can be monitored using
84+``tensorboard``:
85+```
86+tensorboard --logdir ./output/mars/cosine-softmax --port 7006
87+```
88+As for Market1501, 10% of the training data are split off for validation.
89+Concurrently to training, run the following command to run CMC evaluation
90+metrics on the validation set:
91+```
92+CUDA_VISIBLE_DEVICES="" python train_mars.py \
93+ --mode=eval \
94+ --dataset_dir=./MARS-evaluation-master/ \
95+ --loss_mode=cosine-softmax \
96+ --log_dir=./output/mars/ \
97+ --run_id=cosine-softmax \
98+ --eval_log_dir=./eval_output/mars
99+```
100+Evaluation metrics on the validation set can be monitored with ``tensorboard``
101+```
102+tensorboard --logdir ./eval_output/mars/cosine-softmax
103+```
104+ 
105+## Testing
106+ 
107+Final model testing has been carried out using evaluation software provided by
108+the dataset authors. The training scripts can be used to write features of the
109+test split. The following command exports MARS test features to
110+``./MARS-evaluation-master/feat_test.mat``
111+```
112+python train_mars.py \
113+ --mode=export \
114+ --dataset_dir=./MARS-evaluation-master \
115+ --loss_mode=cosine-softmax .\
116+ --restore_path=PATH_TO_CHECKPOINT
117+```
118+where ``PATH_TO_CHECKPOINT`` the checkpoint file to evaluate. Note that the
119+evaluation script needs minor adjustments to apply the cosine similarity metric.
120+More precisely, change the feature computation in
121+``utils/process_box_features.m`` to average pooling (line 8) and apply
122+a re-normalization at the end of the file. The modified file should look like
123+this:
124+```
125+function video_feat = process_box_feat(box_feat, video_info)
126+ 
127+nVideo = size(video_info, 1);
128+video_feat = zeros(size(box_feat, 1), nVideo);
129+for n = 1:nVideo
130+ feature_set = box_feat(:, video_info(n, 1):video_info(n, 2));
131+% video_feat(:, n) = max(feature_set, [], 2); % max pooling
132+ video_feat(:, n) = mean(feature_set, 2); % avg pooling
133+end
134+ 
135+%%% normalize train and test features
136+sum_val = sqrt(sum(video_feat.^2));
137+for n = 1:size(video_feat, 1)
138+ video_feat(n, :) = video_feat(n, :)./sum_val;
139+end
140+```
141+The Market1501 script contains a similar export functionality which can be
142+applied in the same way as described for MARS:
143+```
144+python train_market1501.py \
145+ --mode=export \
146+ --dataset_dir=./Market-1501-v15.09.15/
147+ --sdk_dir=./Market-1501_baseline-v16.01.14/
148+ --loss_mode=cosine-softmax \
149+ --restore_path=PATH_TO_CHECKPOINT
150+```
151+This command creates ``./Market-1501_baseline-v16.01.14/feat_query.mat`` and
152+``./Market-1501_baseline-v16.01.14/feat_test.mat`` to be used with the
153+Market1501 evaluation code.
154+ 
155+## Model export
156+ 
157+To export your trained model for use with the
158+[deep_sort tracker](https://github.com/nwojke/deep_sort), run the following
159+command:
160+```
161+python train_mars.py --mode=freeze --restore_path=PATH_TO_CHECKPOINT
162+```
163+This will create a ``mars.pb`` file which can be supplied to Deep SORT. Again,
164+the Market1501 script contains a similar function.
165+##kernel_meta&ckpt&resources (three missed doc)
@@ -0,0 +1,72 @@
1+# Copyright 2021 Huawei Technologies Co., Ltd
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+This is the boot file for ModelArts platform.
16+Firstly, the train datasets are copyed from obs to ModelArts.
17+Then, the string of train shell command is concated and using 'os.system()' to execute
18+"""
19+import os
20+import numpy as np
21+import argparse
22+from help_modelarts import obs_data2modelarts
23+from cfg import make_config
24+print(os.system('env'))
25+ 
26+ 
27+import argparse
28+import moxing as mox
29+# 解析输入参数data_url
30+ 
31+ 
32+ 
33+if __name__ == '__main__':
34+ ## Note: the code dir is not the same as work dir on ModelArts Platform!!!
35+ code_dir = os.path.dirname(__file__)
36+ work_dir = os.getcwd()
37+ print("===>>>code_dir:{}, work_dir:{}".format(code_dir, work_dir))
38+ 
39+ parser = argparse.ArgumentParser()
40+ parser.add_argument("--data_url", type=str, default="./dataset")
41+ parser.add_argument("--train_url", type=str, default="./output")
42+ config = parser.parse_args()
43+ # 在ModelArts容器创建数据存放目录
44+ data_dir = "/cache/dataset"
45+ model_dir = "/cache/result"
46+ os.makedirs(data_dir)
47+ os.makedirs(model_dir)
48+ # OBS数据拷贝到ModelArts容器内
49+ mox.file.copy_parallel(config.data_url, data_dir)
50+ mox.file.copy_parallel(model_dir, config.train_url)
51+ #parser.add_argument("--train_url", type=str, default="s3://boom/Market-1501-v15.09.15/")
52+ #parser.add_argument("--data_url", type=str, default="s3://boom/Market-1501-v15.09.15/")
53+ #parser.add_argument("--modelarts_data_dir", type=str, default="/cache/Market-1501-v15.09.15")
54+ #parser.add_argument("--modelarts_result_dir", type=str, default="./output")
55+ 
56+ 
57+ print("--------config----------")
58+ for k in list(vars(config).keys()):
59+ print("key:{}: value:{}".format(k, vars(config)[k]))
60+ print("--------config----------")
61+ 
62+ ## copy dataset from obs to modelarts
63+ obs_data2modelarts(config)
64+ 
65+ ## start to train on Modelarts platform
66+ if not os.path.exists(config.modelarts_result_dir):
67+ os.makedirs(config.modelarts_result_dir)
68+ bash_header = os.path.join(code_dir, 'scripts/run_1p.sh')
69+ arg_url = '%s %s %s %s' % (code_dir, config.modelarts_data_dir, config.modelarts_result_dir)
70+ bash_command = 'bash %s %s' % (bash_header, arg_url)
71+ print("bash command:", bash_command)
72+ os.system(bash_command)
@@ -0,0 +1,46 @@
1+# Copyright 2021 Huawei Technologies Co., Ltd
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+Here, define the configuration of tensorflow session
16+For different chips, the config is not the same.
17+"""
18+import tensorflow as tf
19+import os
20+ 
21+ 
22+def make_config(FLAGS):
23+ chip = FLAGS.chip.lower()
24+ tf.logging.info("chip is [%s]", chip)
25+ 
26+ if chip == 'cpu':
27+ config = tf.ConfigProto()
28+ elif chip == 'gpu':
29+ config = tf.ConfigProto(allow_soft_placement=True)
30+ config.gpu_options.allow_growth = True
31+ elif chip == 'npu':
32+ from tensorflow.core.protobuf.rewriter_config_pb2 import RewriterConfig
33+ 
34+ config = tf.ConfigProto()
35+ custom_op = config.graph_options.rewrite_options.custom_optimizers.add()
36+ custom_op.name = "NpuOptimizer"
37+ config.graph_options.rewrite_options.remapping = RewriterConfig.OFF
38+ config.graph_options.rewrite_options.memory_optimization = RewriterConfig.OFF # 必须显式关闭
39+ ## Performance Profiling
40+ ## refer to link:https://support.huaweicloud.com/Development-tg-cann202training1/atlasprofilingtrain_16_0003.html
41+ 
42+ 
43+ else:
44+ raise RuntimeError('chip [%s] has not supported' % chip)
45+ 
46+ return config
@@ -0,0 +1 @@
1+# vim: expandtab:ts=4:sw=4
@@ -0,0 +1,214 @@
1+# vim: expandtab:ts=4:sw=4
2+from npu_bridge.npu_init import *
3+import os
4+import numpy as np
5+import cv2
6+import scipy.io as sio
7+ 
8+ 
9+# The maximum person ID in the dataset.
10+MAX_LABEL = 1501
11+ 
12+IMAGE_SHAPE = 128, 64, 3
13+ 
14+ 
15+def _parse_filename(filename):
16+ """Parse meta-information from given filename.
17+ 
18+ Parameters
19+ ----------
20+ filename : str
21+ A Market 1501 image filename.
22+ 
23+ Returns
24+ -------
25+ (int, int, str, str) | NoneType
26+ Returns a tuple with the following entries:
27+ 
28+ * Unique ID of the individual in the image
29+ * Index of the camera which has observed the individual
30+ * Filename without extension
31+ * File extension
32+ 
33+ Returns None if the given filename is not a valid filename.
34+ 
35+ """
36+ filename_base, ext = os.path.splitext(filename)
37+ if '.' in filename_base:
38+ # Some images have double filename extensions.
39+ filename_base, ext = os.path.splitext(filename_base)
40+ if ext != ".jpg":
41+ return None
42+ person_id, cam_seq, frame_idx, detection_idx = filename_base.split('_')
43+ return int(person_id), int(cam_seq[1]), filename_base, ext
44+ 
45+ 
46+def read_train_split_to_str(dataset_dir):
47+ """Read training data to list of filenames.
48+ 
49+ Parameters
50+ ----------
51+ dataset_dir : str
52+ Path to the Market 1501 dataset directory.
53+ 
54+ Returns
55+ -------
56+ (List[str], List[int], List[int])
57+ Returns a tuple with the following values:
58+ 
59+ * List of image filenames (full path to image files).
60+ * List of unique IDs for the individuals in the images.
61+ * List of camera indices.
62+ 
63+ """
64+ filenames, ids, camera_indices = [], [], []
65+ 
66+ image_dir = os.path.join(dataset_dir, "bounding_box_train")
67+ for filename in sorted(os.listdir(image_dir)):
68+ meta_data = _parse_filename(filename)
69+ if meta_data is None:
70+ # This is not a valid filename (e.g., Thumbs.db).
71+ continue
72+ 
73+ filenames.append(os.path.join(image_dir, filename))
74+ ids.append(meta_data[0])
75+ camera_indices.append(meta_data[1])
76+ 
77+ return filenames, ids, camera_indices
78+ 
79+ 
80+def read_train_split_to_image(dataset_dir):
81+ """Read training images to memory. This consumes a lot of memory.
82+ 
83+ Parameters
84+ ----------
85+ dataset_dir : str
86+ Path to the Market 1501 dataset directory.
87+ 
88+ Returns
89+ -------
90+ (ndarray, ndarray, ndarray)
91+ Returns a tuple with the following values:
92+ 
93+ * Tensor of images in BGR color space of shape 128x64x3.
94+ * One dimensional array of unique IDs for the individuals in the images.
95+ * One dimensional array of camera indices.
96+ 
97+ """
98+ filenames, ids, camera_indices = read_train_split_to_str(dataset_dir)
99+ 
100+ images = np.zeros((len(filenames), 128, 64, 3), np.uint8)
101+ for i, filename in enumerate(filenames):
102+ images[i] = cv2.imread(filename, cv2.IMREAD_COLOR)
103+ 
104+ ids = np.asarray(ids, np.int64)
105+ camera_indices = np.asarray(camera_indices, np.int64)
106+ return images, ids, camera_indices
107+ 
108+ 
109+def read_test_split_to_str(dataset_dir):
110+ """Read query and gallery data to list of filenames.
111+ 
112+ Parameters
113+ ----------
114+ dataset_dir : str
115+ Path to the Market 1501 dataset directory.
116+ 
117+ Returns
118+ -------
119+ (List[str], List[int], List[str], List[int], ndarray)
120+ Returns a tuple with the following values:
121+ 
122+ * List of N gallery filenames (full path to image files).
123+ * List of N unique IDs for the individuals in the gallery.
124+ * List of M query filenames (full path to image files).
125+ * List of M unique IDs for the individuals in the queries.
126+ * Matrix of shape MxN such that element (i, j) evaluates to 0 if
127+ gallery image j should be excluded from metrics computation of
128+ query i and 1 otherwise.
129+ 
130+ """
131+ # Read gallery.
132+ gallery_filenames, gallery_ids = [], []
133+ 
134+ image_dir = os.path.join(dataset_dir, "bounding_box_test")
135+ for filename in sorted(os.listdir(image_dir)):
136+ meta_data = _parse_filename(filename)
137+ if meta_data is None:
138+ # This is not a valid filename (e.g., Thumbs.db).
139+ continue
140+ 
141+ gallery_filenames.append(os.path.join(image_dir, filename))
142+ gallery_ids.append(meta_data[0])
143+ 
144+ # Read queries.
145+ query_filenames, query_ids, query_junk_indices = [], [], []
146+ 
147+ image_dir = os.path.join(dataset_dir, "query")
148+ for filename in sorted(os.listdir(image_dir)):
149+ meta_data = _parse_filename(filename)
150+ if meta_data is None:
151+ # This is not a valid filename (e.g., Thumbs.db).
152+ continue
153+ 
154+ filename_base = meta_data[2]
155+ junk_matfile = filename_base + "_junk.mat"
156+ mat = sio.loadmat(os.path.join(dataset_dir, "gt_query", junk_matfile))
157+ if np.any(mat["junk_index"] < 1):
158+ indices = []
159+ else:
160+ # MATLAB to Python index.
161+ indices = list(mat["junk_index"].astype(np.int64).ravel() - 1)
162+ 
163+ query_junk_indices.append(indices)
164+ query_filenames.append(os.path.join(image_dir, filename))
165+ query_ids.append(meta_data[0])
166+ 
167+ # The following matrix maps from query (row) to gallery image (column) such
168+ # that element (i, j) evaluates to 0 if query i and gallery image j should
169+ # be excluded from computation of the evaluation metrics and 1 otherwise.
170+ good_mask = np.ones(
171+ (len(query_filenames), len(gallery_filenames)), np.float32)
172+ for i, junk_indices in enumerate(query_junk_indices):
173+ good_mask[i, junk_indices] = 0.
174+ 
175+ return gallery_filenames, gallery_ids, query_filenames, query_ids, good_mask
176+ 
177+ 
178+def read_test_split_to_image(dataset_dir):
179+ """Read query and gallery data to memory. This consumes a lot of memory.
180+ 
181+ Parameters
182+ ----------
183+ dataset_dir : str
184+ Path to the Market 1501 dataset directory.
185+ 
186+ Returns
187+ -------
188+ (ndarray, ndarray, ndarray, ndarray, ndarray)
189+ Returns a tuple with the following values:
190+ 
191+ * Tensor of shape Nx128x64x3 of N gallery images in BGR color space.
192+ * One dimensional array of N unique gallery IDs.
193+ * Tensor of shape Mx128x64x3 of M query images in BGR color space.
194+ * One dimensional array of M unique query IDs.
195+ * Matrix of shape MxN such that element (i, j) evaluates to 0 if
196+ gallery image j should be excluded from metrics computation of
197+ query i and 1 otherwise.
198+ 
199+ """
200+ gallery_filenames, gallery_ids, query_filenames, query_ids, good_mask = (
201+ read_test_split_to_str(dataset_dir))
202+ 
203+ gallery_images = np.zeros((len(gallery_filenames), 128, 64, 3), np.uint8)
204+ for i, filename in enumerate(gallery_filenames):
205+ gallery_images[i] = cv2.imread(filename, cv2.IMREAD_COLOR)
206+ 
207+ query_images = np.zeros((len(query_filenames), 128, 64, 3), np.uint8)
208+ for i, filename in enumerate(query_filenames):
209+ query_images[i] = cv2.imread(filename, cv2.IMREAD_COLOR)
210+ 
211+ gallery_ids = np.asarray(gallery_ids, np.int64)
212+ query_ids = np.asarray(query_ids, np.int64)
213+ return gallery_images, gallery_ids, query_images, query_ids, good_mask
214+ 
@@ -0,0 +1,190 @@
1+# vim: expandtab:ts=4:sw=4
2+from npu_bridge.npu_init import *
3+import os
4+import numpy as np
5+import cv2
6+ 
7+ 
8+# The maximum person ID in the dataset.
9+MAX_LABEL = 1500
10+ 
11+IMAGE_SHAPE = 256, 128, 3
12+ 
13+ 
14+def read_train_test_directory_to_str(directory):
15+ """Read bbox_train/bbox_test directory.
16+ 
17+ Parameters
18+ ----------
19+ directory : str
20+ Path to bbox_train/bbox_test directory.
21+ 
22+ Returns
23+ -------
24+ (List[str], List[int], List[int], List[int])
25+ Returns a tuple with the following entries:
26+ 
27+ * List of image filenames.
28+ * List of corresponding unique IDs for the individuals in the images.
29+ * List of camera indices.
30+ * List of tracklet indices.
31+ 
32+ """
33+ 
34+ def to_label(x):
35+ return int(x) if x.isdigit() else -1
36+ 
37+ dirnames = os.listdir(directory)
38+ image_filenames, ids, camera_indices, tracklet_indices = [], [], [], []
39+ for dirname in dirnames:
40+ filenames = os.listdir(os.path.join(directory, dirname))
41+ filenames = [
42+ f for f in filenames if os.path.splitext(f)[1] == ".jpg"]
43+ image_filenames += [
44+ os.path.join(directory, dirname, f) for f in filenames]
45+ ids += [to_label(dirname) for _ in filenames]
46+ camera_indices += [int(f[5]) for f in filenames]
47+ tracklet_indices += [int(f[7:11]) for f in filenames]
48+ 
49+ return image_filenames, ids, camera_indices, tracklet_indices
50+ 
51+ 
52+def read_train_test_directory_to_image(directory, image_shape=(128, 64)):
53+ """Read images in bbox_train/bbox_test directory.
54+ 
55+ Parameters
56+ ----------
57+ directory : str
58+ Path to bbox_train/bbox_test directory.
59+ image_shape : Tuple[int, int]
60+ A tuple (height, width) of the desired image size.
61+ 
62+ Returns
63+ -------
64+ (np.ndarray, np.ndarray, np.ndarray, np.ndarray)
65+ Returns a tuple with the following entries:
66+ 
67+ * Tensor of images in BGR color space.
68+ * One dimensional array of unique IDs for the individuals in the images.
69+ * One dimensional array of camera indices.
70+ * One dimensional array of tracklet indices.
71+ 
72+ """
73+ reshape_fn = (
74+ (lambda x: x) if image_shape == IMAGE_SHAPE[:2]
75+ else (lambda x: cv2.resize(x, image_shape[::-1])))
76+ 
77+ filenames, ids, camera_indices, tracklet_indices = (
78+ read_train_test_directory_to_str(directory))
79+ 
80+ images = np.zeros((len(filenames), ) + image_shape + (3, ), np.uint8)
81+ for i, filename in enumerate(filenames):
82+ if i % 1000 == 0:
83+ print("Reading %s, %d / %d" % (directory, i, len(filenames)))
84+ image = cv2.imread(filename, cv2.IMREAD_COLOR)
85+ images[i] = reshape_fn(image)
86+ ids = np.asarray(ids, dtype=np.int64)
87+ camera_indices = np.asarray(camera_indices, dtype=np.int64)
88+ tracklet_indices = np.asarray(tracklet_indices, dtype=np.int64)
89+ return images, ids, camera_indices, tracklet_indices
90+ 
91+ 
92+def read_train_split_to_str(dataset_dir):
93+ """Read training data to list of filenames.
94+ 
95+ Parameters
96+ ----------
97+ dataset_dir : str
98+ Path to the MARS dataset directory; ``bbox_train`` should be a
99+ subdirectory of this folder.
100+ 
101+ Returns
102+ -------
103+ (List[str], List[int], List[int], List[int])
104+ Returns a tuple with the following entries:
105+ 
106+ * List of image filenames.
107+ * List of corresponding unique IDs for the individuals in the images.
108+ * List of camera indices.
109+ * List of tracklet indices.
110+ 
111+ """
112+ train_dir = os.path.join(dataset_dir, "bbox_train")
113+ return read_train_test_directory_to_str(train_dir)
114+ 
115+ 
116+def read_train_split_to_image(dataset_dir, image_shape=(128, 64)):
117+ """Read training images to memory. This consumes a lot of memory.
118+ 
119+ Parameters
120+ ----------
121+ dataset_dir : str
122+ Path to the MARS dataset directory; ``bbox_train`` should be a
123+ subdirectory of this folder.
124+ image_shape : Tuple[int, int]
125+ A tuple (height, width) of the desired image size.
126+ 
127+ Returns
128+ -------
129+ (np.ndarray, np.ndarray, np.ndarray, np.ndarray)
130+ Returns a tuple with the following entries:
131+ 
132+ * Tensor of images in BGR color space.
133+ * One dimensional array of unique IDs for the individuals in the images.
134+ * One dimensional array of camera indices.
135+ * One dimensional array of tracklet indices.
136+ 
137+ """
138+ train_dir = os.path.join(dataset_dir, "bbox_train")
139+ return read_train_test_directory_to_image(train_dir, image_shape)
140+ 
141+ 
142+def read_test_split_to_str(dataset_dir):
143+ """Read training data to list of filenames.
144+ 
145+ Parameters
146+ ----------
147+ dataset_dir : str
148+ Path to the MARS dataset directory; ``bbox_test`` should be a
149+ subdirectory of this folder.
150+ 
151+ Returns
152+ -------
153+ (List[str], List[int], List[int], List[int])
154+ Returns a tuple with the following entries:
155+ 
156+ * List of image filenames.
157+ * List of corresponding unique IDs for the individuals in the images.
158+ * List of camera indices.
159+ * List of tracklet indices.
160+ 
161+ """
162+ test_dir = os.path.join(dataset_dir, "bbox_test")
163+ return read_train_test_directory_to_str(test_dir)
164+ 
165+ 
166+def read_test_split_to_image(dataset_dir, image_shape=(128, 64)):
167+ """Read test images to memory. This consumes a lot of memory.
168+ 
169+ Parameters
170+ ----------
171+ dataset_dir : str
172+ Path to the MARS dataset directory; ``bbox_test`` should be a
173+ subdirectory of this folder.
174+ image_shape : Tuple[int, int]
175+ A tuple (height, width) of the desired image size.
176+ 
177+ Returns
178+ -------
179+ (np.ndarray, np.ndarray, np.ndarray, np.ndarray)
180+ Returns a tuple with the following entries:
181+ 
182+ * Tensor of images in BGR color space.
183+ * One dimensional array of unique IDs for the individuals in the images.
184+ * One dimensional array of camera indices.
185+ * One dimensional array of tracklet indices.
186+ 
187+ """
188+ test_dir = os.path.join(dataset_dir, "bbox_test")
189+ return read_train_test_directory_to_image(test_dir, image_shape)
190+ 
@@ -0,0 +1,173 @@
1+# vim: expandtab:ts=4:sw=4
2+from npu_bridge.npu_init import *
3+import numpy as np
4+import cv2
5+ 
6+ 
7+def crop_to_shape(images, patch_shape):
8+ """Crop images to desired shape, respecting the target aspect ratio.
9+ 
10+ Parameters
11+ ----------
12+ images : List[ndarray]
13+ A list of images in BGR format (dtype np.uint8)
14+ patch_shape : (int, int)
15+ Target image patch shape (height, width).
16+ 
17+ Returns
18+ -------
19+ ndarray
20+ A tensor of output images.
21+ 
22+ """
23+ assert len(images) > 0, "Empty image list is not allowed."
24+ channels = () if len(images[0].shape) == 0 else (images[0].shape[-1], )
25+ output_images = np.zeros(
26+ (len(images), ) + patch_shape + channels, dtype=np.uint8)
27+ 
28+ target_aspect_ratio = float(patch_shape[1]) / patch_shape[0]
29+ for i, image in enumerate(images):
30+ image_aspect_ratio = float(image.shape[1]) / image.shape[0]
31+ if target_aspect_ratio > image_aspect_ratio:
32+ # Fix width, modify height.
33+ crop_height = image.shape[1] / target_aspect_ratio
34+ crop_width = image.shape[1]
35+ else:
36+ # Fix height, modify width.
37+ crop_width = target_aspect_ratio * image.shape[0]
38+ crop_height = image.shape[0]
39+ 
40+ sx = int((image.shape[1] - crop_width) / 2)
41+ sy = int((image.shape[0] - crop_height) / 2)
42+ ex = int(min(sx + crop_width, image.shape[1]))
43+ ey = int(min(sy + crop_height, image.shape[0]))
44+ output_images[i, ...] = cv2.resize(
45+ image[sy:ey, sx:ex], patch_shape[::-1],
46+ interpolation=cv2.INTER_CUBIC)
47+ 
48+ return output_images
49+ 
50+ 
51+def create_validation_split(data_y, num_validation_y, seed=None):
52+ """Split dataset into training and validation set with disjoint classes.
53+ 
54+ Parameters
55+ ----------
56+ data_y : ndarray
57+ A label vector.
58+ num_validation_y : int | float
59+ The number of identities to split off for validation. If an integer
60+ is given, this value should be at least 1 and is interpreted as absolute
61+ number of validation identities. If a float is given, this value should
62+ be in [0, 1[ and is interpreted as fraction of validation identities.
63+ seed : Optional[int]
64+ A random generator seed used to select the validation idenities.
65+ 
66+ Returns
67+ -------
68+ (ndarray, ndarray)
69+ Returns indices of training and validation set.
70+ 
71+ """
72+ unique_y = np.unique(data_y)
73+ if isinstance(num_validation_y, float):
74+ num_validation_y = int(num_validation_y * len(unique_y))
75+ 
76+ random_generator = np.random.RandomState(seed=seed)
77+ validation_y = random_generator.choice(
78+ unique_y, num_validation_y, replace=False)
79+ 
80+ validation_mask = np.full((len(data_y), ), False, bool)
81+ for y in validation_y:
82+ validation_mask = np.logical_or(validation_mask, data_y == y)
83+ training_mask = np.logical_not(validation_mask)
84+ return np.where(training_mask)[0], np.where(validation_mask)[0]
85+ 
86+ 
87+def limit_num_elements_per_identity(data_y, max_num_images_per_id, seed=None):
88+ """Limit the number of elements per identity to `max_num_images_per_id`.
89+ 
90+ Parameters
91+ ----------
92+ data_y : ndarray
93+ A label vector.
94+ max_num_images_per_id : int
95+ The maximum number of elements per identity that should remain in
96+ the data set.
97+ seed : Optional[int]
98+ Random generator seed.
99+ 
100+ Returns
101+ -------
102+ ndarray
103+ A boolean mask that evaluates to True if the corresponding
104+ should remain in the data set.
105+ 
106+ """
107+ random_generator = np.random.RandomState(seed=seed)
108+ valid_mask = np.full((len(data_y), ), False, bool)
109+ for y in np.unique(data_y):
110+ indices = np.where(data_y == y)[0]
111+ num_select = min(len(indices), max_num_images_per_id)
112+ indices = random_generator.choice(indices, num_select, replace=False)
113+ valid_mask[indices] = True
114+ return valid_mask
115+ 
116+ 
117+def create_cmc_probe_and_gallery(data_y, camera_indices=None, seed=None):
118+ """Create probe and gallery images for evaluation of CMC top-k statistics.
119+ 
120+ For every identity, this function selects one image as probe and one image
121+ for the gallery. Cross-view validation is performed when multiple cameras
122+ are given.
123+ 
124+ Parameters
125+ ----------
126+ data_y : ndarray
127+ Vector of data labels.
128+ camera_indices : Optional[ndarray]
129+ Optional array of camera indices. If possible, probe and gallery images
130+ are selected from different cameras (i.e., cross-view validation).
131+ If None given, assumes all images are taken from the same camera.
132+ seed : Optional[int]
133+ The random seed used to select probe and gallery images.
134+ 
135+ Returns
136+ -------
137+ (ndarray, ndarray)
138+ Returns a tuple of indices to probe and gallery images.
139+ 
140+ """
141+ data_y = np.asarray(data_y)
142+ if camera_indices is None:
143+ camera_indices = np.zeros_like(data_y, dtype=np.int)
144+ camera_indices = np.asarray(camera_indices)
145+ 
146+ random_generator = np.random.RandomState(seed=seed)
147+ unique_y = np.unique(data_y)
148+ probe_indices, gallery_indices = [], []
149+ for y in unique_y:
150+ mask_y = data_y == y
151+ 
152+ unique_cameras = np.unique(camera_indices[mask_y])
153+ if len(unique_cameras) == 1:
154+ # If we have only one camera, take any two images from this device.
155+ c = unique_cameras[0]
156+ indices = np.where(np.logical_and(mask_y, camera_indices == c))[0]
157+ if len(indices) < 2:
158+ continue # Cannot generate a pair for this identity.
159+ i1, i2 = random_generator.choice(indices, 2, replace=False)
160+ else:
161+ # If we have multiple cameras, take images of two (randomly chosen)
162+ # different devices.
163+ c1, c2 = random_generator.choice(unique_cameras, 2, replace=False)
164+ indices1 = np.where(np.logical_and(mask_y, camera_indices == c1))[0]
165+ indices2 = np.where(np.logical_and(mask_y, camera_indices == c2))[0]
166+ i1 = random_generator.choice(indices1)
167+ i2 = random_generator.choice(indices2)
168+ 
169+ probe_indices.append(i1)
170+ gallery_indices.append(i2)
171+ 
172+ return np.asarray(probe_indices), np.asarray(gallery_indices)
173+ 
@@ -0,0 +1,80 @@
1+# Copyright 2021 Huawei Technologies Co., Ltd
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+import os
15+import datetime
16+import moxing as mox
17+ 
18+def obs_data2modelarts(config):
19+ """
20+ Copy train data from obs to modelarts by using moxing api.
21+ """
22+ start = datetime.datetime.now()
23+ print("===>>>Copy files from obs:{} to modelarts dir:{}".format(config.data_url, config.modelarts_data_dir))
24+ mox.file.copy_parallel(src_url=config.data_url, dst_url=config.modelarts_data_dir)
25+ end = datetime.datetime.now()
26+ print("===>>>Copy from obs to modelarts, time use:{}(s)".format((end - start).seconds))
27+ files = os.listdir(config.modelarts_data_dir)
28+ print("===>>>Files:", files)
29+ 
30+ 
31+def modelarts_result2obs(FLAGS):
32+ """
33+ Copy debug data from modelarts to obs.
34+ According to the swich flags, the debug data may contains auto tune repository,
35+ dump data for precision comparision, even the computation graph and profiling data.
36+ """
37+ work_dir = os.getcwd()
38+ 
39+ ## copy result from modelarts to obs
40+ obs_result_dir = os.path.join(FLAGS.obs_dir, 'result')
41+ if not mox.file.exists(obs_result_dir):
42+ mox.file.make_dirs(obs_result_dir)
43+ mox.file.copy_parallel(src_url=FLAGS.result, dst_url=obs_result_dir)
44+ print("===>>>Copy Event or Checkpoint from modelarts dir:{} to obs:{}".format(FLAGS.result, obs_result_dir))
45+ 
46+ ## Copy auto tune repository. Comment this snippets if npu_auto_tune is off.
47+ if FLAGS.npu_auto_tune:
48+ modelarts_auto_tune_dir = os.path.join(work_dir, "npu_auto_tune")
49+ obs_auto_tune_dir = os.path.join(FLAGS.obs_dir, 'npu_auto_tune')
50+ if not mox.file.exists(obs_auto_tune_dir):
51+ mox.file.make_dirs(obs_auto_tune_dir)
52+ mox.file.copy_parallel(modelarts_auto_tune_dir, obs_auto_tune_dir)
53+ print("===>>>Auto tune:{} on OBS dir:{}".format(mox.file.list_directory(obs_auto_tune_dir), obs_auto_tune_dir))
54+ 
55+ ## Copy dump data. Comment this snippets if npu_dump_data is off.
56+ if FLAGS.npu_dump_data:
57+ modelarts_dump_data_dir = os.path.join(work_dir, "npu_dump_data")
58+ obs_dump_data_dir = os.path.join(FLAGS.obs_dir, 'npu_dump_data')
59+ if not mox.file.exists(obs_dump_data_dir):
60+ mox.file.make_dirs(obs_dump_data_dir)
61+ mox.file.copy_parallel(modelarts_dump_data_dir, obs_dump_data_dir)
62+ print("===>>>Dumped graph:{} on OBS dir:{}".format(mox.file.list_directory(obs_dump_data_dir), obs_dump_data_dir))
63+ 
64+ ## Copy compute graph. Comment this snippets if npu_dump_graph is off.
65+ if FLAGS.npu_dump_graph:
66+ modelarts_dump_graph_dir = os.path.join(work_dir, "npu_dump_graph")
67+ obs_dump_graph_dir = os.path.join(FLAGS.obs_dir, 'npu_dump_graph')
68+ if not mox.file.exists(obs_dump_graph_dir):
69+ mox.file.make_dirs(obs_dump_graph_dir)
70+ mox.file.copy_parallel(modelarts_dump_graph_dir, obs_dump_graph_dir)
71+ print("===>>>Dumped data:{} on OBS dir:{}".format(mox.file.list_directory(obs_dump_graph_dir), obs_dump_graph_dir))
72+ 
73+ ## Copy profiling data. Comment this snippets if npu_profiling is off.
74+ if FLAGS.npu_profiling:
75+ modelarts_profiling_dir = os.path.join(work_dir, "npu_profiling")
76+ obs_profiling_dir = os.path.join(FLAGS.obs_dir, 'npu_profiling')
77+ if not mox.file.exists(obs_profiling_dir):
78+ mox.file.make_dirs(obs_profiling_dir)
79+ mox.file.copy_parallel(modelarts_profiling_dir, obs_profiling_dir)
80+ print("===>>>Profiling data:{} on OBS dir:{}".format(mox.file.list_directory(obs_profiling_dir), obs_profiling_dir))
@@ -0,0 +1,145 @@
1+# vim: expandtab:ts=4:sw=4
2+from npu_bridge.npu_init import *
3+import tensorflow as tf
4+ 
5+ 
6+def _pdist(a, b=None):
7+ sq_sum_a = tf.reduce_sum(tf.square(a), reduction_indices=[1])
8+ if b is None:
9+ return -2 * tf.matmul(a, tf.transpose(a)) + \
10+ tf.reshape(sq_sum_a, (-1, 1)) + tf.reshape(sq_sum_a, (1, -1))
11+ sq_sum_b = tf.reduce_sum(tf.square(b), reduction_indices=[1])
12+ return -2 * tf.matmul(a, tf.transpose(b)) + \
13+ tf.reshape(sq_sum_a, (-1, 1)) + tf.reshape(sq_sum_b, (1, -1))
14+ 
15+ 
16+def softmargin_triplet_loss(features, labels, create_summaries=True):
17+ """Softmargin triplet loss.
18+ 
19+ See::
20+ 
21+ Hermans, Beyer, Leibe: In Defense of the Triplet Loss for Person
22+ Re-Identification. arXiv, 2017.
23+ 
24+ Parameters
25+ ----------
26+ features : tf.Tensor
27+ A matrix of shape NxM that contains the M-dimensional feature vectors
28+ of N objects (floating type).
29+ labels : tf.Tensor
30+ The one-dimensional array of length N that contains for each feature
31+ the associated class label (integer type).
32+ create_summaries : Optional[bool]
33+ If True, creates summaries to monitor training behavior.
34+ 
35+ Returns
36+ -------
37+ tf.Tensor
38+ A scalar loss tensor.
39+ 
40+ """
41+ eps = tf.constant(1e-5, tf.float32)
42+ nil = tf.constant(0., tf.float32)
43+ almost_inf = tf.constant(1e+10, tf.float32)
44+ 
45+ squared_distance_mat = _pdist(features)
46+ distance_mat = tf.sqrt(tf.maximum(nil, eps + squared_distance_mat))
47+ label_mat = tf.cast(tf.equal(
48+ tf.reshape(labels, (-1, 1)), tf.reshape(labels, (1, -1))), tf.float32)
49+ 
50+ positive_distance = tf.reduce_max(label_mat * distance_mat, axis=1)
51+ negative_distance = tf.reduce_min(
52+ (label_mat * almost_inf) + distance_mat, axis=1)
53+ loss = tf.nn.softplus(positive_distance - negative_distance)
54+ if create_summaries:
55+ fraction_invalid_pdist = tf.reduce_mean(
56+ tf.cast(tf.less_equal(squared_distance_mat, -eps), tf.float32))
57+ tf.summary.scalar("fraction_invalid_pdist", fraction_invalid_pdist)
58+ 
59+ fraction_active_triplets = tf.reduce_mean(
60+ tf.cast(tf.greater_equal(loss, 1e-5), tf.float32))
61+ tf.summary.scalar("fraction_active_triplets", fraction_active_triplets)
62+ 
63+ embedding_squared_norm = tf.reduce_mean(
64+ tf.reduce_sum(tf.square(features), axis=1))
65+ tf.summary.scalar("mean squared feature norm", embedding_squared_norm)
66+ 
67+ mean_distance = tf.reduce_mean(distance_mat)
68+ tf.summary.scalar("mean feature distance", mean_distance)
69+ 
70+ mean_positive_distance = tf.reduce_mean(positive_distance)
71+ tf.summary.scalar("mean positive distance", mean_positive_distance)
72+ 
73+ mean_negative_distance = tf.reduce_mean(negative_distance)
74+ tf.summary.scalar("mean negative distance", mean_negative_distance)
75+ 
76+ return tf.reduce_mean(loss)
77+ 
78+ 
79+def magnet_loss(features, labels, margin=1.0, unique_labels=None):
80+ """Simple unimodal magnet loss.
81+ 
82+ See::
83+ 
84+ Rippel, Paluri, Dollar, Bourdev: Metric Learning With Adaptive
85+ Density Discrimination. ICLR, 2016.
86+ 
87+ Parameters
88+ ----------
89+ features : tf.Tensor
90+ A matrix of shape NxM that contains the M-dimensional feature vectors
91+ of N objects (floating type).
92+ labels : tf.Tensor
93+ The one-dimensional array of length N that contains for each feature
94+ the associated class label (integer type).
95+ margin : float
96+ A scalar margin hyperparameter.
97+ unique_labels : Optional[tf.Tensor]
98+ Optional tensor of unique values in `labels`. If None given, computed
99+ from data.
100+ 
101+ Returns
102+ -------
103+ tf.Tensor
104+ A scalar loss tensor.
105+ 
106+ """
107+ nil = tf.constant(0., tf.float32)
108+ one = tf.constant(1., tf.float32)
109+ minus_two = tf.constant(-2., tf.float32)
110+ eps = tf.constant(1e-4, tf.float32)
111+ margin = tf.constant(margin, tf.float32)
112+ 
113+ num_per_class = None
114+ if unique_labels is None:
115+ unique_labels, sample_to_unique_y, num_per_class = tf.unique_with_counts(labels)
116+ num_per_class = tf.cast(num_per_class, tf.float32)
117+ 
118+ y_mat = tf.cast(tf.equal(
119+ tf.reshape(labels, (-1, 1)), tf.reshape(unique_labels, (1, -1))),
120+ dtype=tf.float32)
121+ 
122+ # If class_means is None, compute from batch data.
123+ if num_per_class is None:
124+ num_per_class = tf.reduce_sum(y_mat, reduction_indices=[0])
125+ class_means = tf.reduce_sum(
126+ tf.expand_dims(tf.transpose(y_mat), -1) * tf.expand_dims(features, 0),
127+ reduction_indices=[1]) / tf.expand_dims(num_per_class, -1)
128+ 
129+ squared_distance = _pdist(features, class_means)
130+ 
131+ num_samples = tf.cast(tf.shape(labels)[0], tf.float32)
132+ variance = tf.reduce_sum(
133+ y_mat * squared_distance) / (num_samples - one)
134+ 
135+ const = one / (minus_two * (variance + eps))
136+ linear = const * squared_distance - y_mat * margin
137+ 
138+ maxi = tf.reduce_max(linear, reduction_indices=[1], keepdims=True)
139+ loss_mat = tf.exp(linear - maxi)
140+ 
141+ a = tf.reduce_sum(y_mat * loss_mat, reduction_indices=[1])
142+ b = tf.reduce_sum((one - y_mat) * loss_mat, reduction_indices=[1])
143+ loss = tf.maximum(nil, -tf.log(eps + a / (eps + b)))
144+ return tf.reduce_mean(loss), class_means, variance
145+ 
@@ -0,0 +1,223 @@
1+# vim: expandtab:ts=4:sw=4
2+from npu_bridge.npu_init import *
3+import tensorflow as tf
4+import tensorflow.contrib.slim as slim
5+ 
6+ 
7+def pdist(a, b=None):
8+ """Compute element-wise squared distance between `a` and `b`.
9+ 
10+ Parameters
11+ ----------
12+ a : tf.Tensor
13+ A matrix of shape NxL with N row-vectors of dimensionality L.
14+ b : tf.Tensor
15+ A matrix of shape MxL with M row-vectors of dimensionality L.
16+ 
17+ Returns
18+ -------
19+ tf.Tensor
20+ A matrix of shape NxM where element (i, j) contains the squared
21+ distance between elements `a[i]` and `b[j]`.
22+ 
23+ """
24+ sq_sum_a = tf.reduce_sum(tf.square(a), reduction_indices=[1])
25+ if b is None:
26+ return -2 * tf.matmul(a, tf.transpose(a)) + \
27+ tf.reshape(sq_sum_a, (-1, 1)) + tf.reshape(sq_sum_a, (1, -1))
28+ sq_sum_b = tf.reduce_sum(tf.square(b), reduction_indices=[1])
29+ return -2 * tf.matmul(a, tf.transpose(b)) + \
30+ tf.reshape(sq_sum_a, (-1, 1)) + tf.reshape(sq_sum_b, (1, -1))
31+ 
32+ 
33+def cosine_distance(a, b=None):
34+ """Compute element-wise cosine distance between `a` and `b`.
35+ 
36+ Parameters
37+ ----------
38+ a : tf.Tensor
39+ A matrix of shape NxL with N row-vectors of dimensionality L.
40+ b : tf.Tensor
41+ A matrix of shape NxL with N row-vectors of dimensionality L.
42+ 
43+ Returns
44+ -------
45+ tf.Tensor
46+ A matrix of shape NxM where element (i, j) contains the cosine distance
47+ between elements `a[i]` and `b[j]`.
48+ 
49+ """
50+ a_normed = tf.nn.l2_normalize(a, dim=1)
51+ b_normed = a_normed if b is None else tf.nn.l2_normalize(b, dim=1)
52+ return (
53+ tf.constant(1.0, tf.float32) -
54+ tf.matmul(a_normed, tf.transpose(b_normed)))
55+ 
56+ 
57+def recognition_rate_at_k(probe_x, probe_y, gallery_x, gallery_y, k,
58+ measure=pdist):
59+ """Compute the recognition rate at a given level `k`.
60+ 
61+ For a given probe and ranked gallery that is sorted according to a distance
62+ measure `measure` in descending order, the recognition rate at `k` is::
63+ 
64+ recognition_rate_at_k = num_correct / min(k, num_relevant)
65+ 
66+ where num_correct refers to the fraction of images in the top k entries of
67+ the ranked gallery that have the same label as the probe and `num_relevant`
68+ refers to the total number of elements in the gallery that have the same
69+ label.
70+ 
71+ Parameters
72+ ----------
73+ probe_x: tf.Tensor
74+ A tensor of probe images.
75+ probe_y: tf.Tensor
76+ A tensor of probe labels.
77+ gallery_x: tf.Tensor
78+ A tensor of gallery images.
79+ gallery_y: tf.Tensor
80+ A tensor of gallery labels.
81+ k: int
82+ See description above.
83+ measure: Callable[tf.Tensor, tf.Tensor] -> tf.Tensor
84+ A callable that computes for two matrices of row-vectors a matrix of
85+ element-wise distances. See `pdist` for an example.
86+ 
87+ Returns
88+ -------
89+ tf.Tensor
90+ Returns a scalar tensor which represents the computed metric.
91+ 
92+ """
93+ # Build a matrix of shape (num_probes, num_gallery_images) where element
94+ # (i, j) is 1 if probe image i and the gallery image j have the same
95+ # identity, otherwise 0.
96+ label_eq_mat = tf.cast(tf.equal(tf.reshape(
97+ probe_y, (-1, 1)), tf.reshape(gallery_y, (1, -1))),
98+ tf.float32)
99+ 
100+ # For each probe image, compute the number of relevant images in the
101+ # gallery (same identity). This should always be one for CMC evaluation
102+ # because we always have exactly one probe and one gallery image for each
103+ # identity.
104+ num_relevant = tf.minimum(tf.cast(k, tf.float32), tf.reduce_sum(
105+ label_eq_mat, reduction_indices=[1]))
106+ 
107+ # Rank gallery images by the similarity measure to build a matrix of
108+ # shape (num_probes, k) where element (i, j) contains the label of the
109+ # j-th ranked gallery image for probe i.
110+ predictions = tf.exp(-measure(probe_x, gallery_x)) # Compute similarity.
111+ _, prediction_indices = tf.nn.top_k(predictions, k=k)
112+ label_mat = tf.gather(gallery_y, prediction_indices)
113+ 
114+ # Just as we have done before, build a matrix where element (i, j) is
115+ # one if probe i and gallery image j share the same label (same identity).
116+ # This time, the matrix is ranked by the similarity measure and we only
117+ # keep the top-k predictions.
118+ label_eq_mat = tf.cast(tf.equal(
119+ label_mat, tf.reshape(probe_y, (-1, 1))), tf.float32)
120+ 
121+ # Compute the number of true positives in [0, k[, i.e., check if we find
122+ # the correct gallery image within the top-k ranked results. Then, compute
123+ # the recognition rate, which in our case is either 0 or 1 since we have
124+ # only one gallery image that shares the same identity with the probe.
125+ #
126+ # This is the final output of our CMC metric.
127+ true_positives_at_k = tf.reduce_sum(label_eq_mat, reduction_indices=[1])
128+ return true_positives_at_k / num_relevant
129+ 
130+ 
131+def streaming_mean_cmc_at_k(probe_x, probe_y, gallery_x, gallery_y, k,
132+ measure=pdist):
133+ """Compute cumulated matching characteristics (CMC) at level `k` over
134+ a stream of data (i.e., multiple batches).
135+ 
136+ The function is compatible with TensorFlow-Slim's streaming metrics
137+ interface, e.g., `slim.metrics.aggregate_metric_map`.
138+ 
139+ Parameters
140+ ----------
141+ probe_x: tf.Tensor
142+ A tensor of probe images.
143+ probe_y: tf.Tensor
144+ A tensor of probe labels.
145+ gallery_x: tf.Tensor
146+ A tensor of gallery images.
147+ gallery_y: tf.Tensor
148+ A tensor of gallery labels.
149+ k: int
150+ See description above.
151+ measure: Callable[tf.Tensor, tf.Tensor] -> tf.Tensor
152+ A callable that computes for two matrices of row-vectors a matrix of
153+ element-wise distances. See `pdist` for an example.
154+ 
155+ Returns
156+ -------
157+ Tuple[tf.Tensor, tf.Tensor]
158+ The first element in the tuple is the current result. The second element
159+ is an operation that updates the computed metric based on new data.
160+ 
161+ """
162+ recognition_rate = recognition_rate_at_k(
163+ probe_x, probe_y, gallery_x, gallery_y, k, measure)
164+ return slim.metrics.streaming_mean(recognition_rate)
165+ 
166+ 
167+def streaming_mean_averge_precision(probe_x, probe_y, gallery_x, gallery_y,
168+ good_mask, measure=pdist):
169+ """Compute mean average precision (mAP) over a stream of data.
170+ 
171+ Parameters
172+ ----------
173+ probe_x: tf.Tensor
174+ A tensor of N probe images.
175+ probe_y: tf.Tensor
176+ A tensor of N probe labels.
177+ gallery_x: tf.Tensor
178+ A tensor of M gallery images.
179+ gallery_y: tf.Tensor
180+ A tensor of M gallery labels.
181+ good_mask: Optional[tf.Tensor]
182+ A matrix of shape NxM where element (i, j) evaluates to 0.0 if the pair
183+ of i-th probe and j-th gallery image should be excluded from metric
184+ computation. All other elements should evaluate to 1.0.
185+ measure: Callable[tf.Tensor, tf.Tensor] -> tf.Tensor
186+ A callable that computes for two matrices of row-vectors a matrix of
187+ element-wise distances. See `pdist` for an example.
188+ 
189+ Returns
190+ -------
191+ Tuple[tf.Tensor, tf.Tensor]
192+ The first element in the tuple is the current result. The second element
193+ is an operation that updates the computed metric based on new data.
194+ 
195+ """
196+ # See Wikipedia:
197+ # https://en.wikipedia.org/wiki/Information_retrieval#Average_precision
198+ if good_mask.dtype != tf.float32:
199+ good_mask = tf.cast(good_mask, tf.float32)
200+ 
201+ # Compute similarity measure and mask out diagonal (similarity to self).
202+ predictions = good_mask * tf.exp(-measure(probe_x, gallery_x))
203+ 
204+ # Compute matrix of predicted labels.
205+ k = tf.shape(gallery_y)[0]
206+ _, prediction_indices = tf.nn.top_k(predictions, k=k)
207+ predicted_label_mat = tf.gather(gallery_y, prediction_indices)
208+ label_eq_mat = tf.cast(tf.equal(
209+ predicted_label_mat, tf.reshape(probe_y, (-1, 1))), tf.float32)
210+ 
211+ # Compute statistics.
212+ num_relevant = tf.reduce_sum(
213+ good_mask * label_eq_mat, reduction_indices=[1], keep_dims=True)
214+ true_positives_at_k = tf.cumsum(label_eq_mat, axis=1)
215+ retrieved_at_k = tf.cumsum(tf.ones_like(label_eq_mat), axis=1)
216+ precision_at_k = true_positives_at_k / retrieved_at_k
217+ relevant_at_k = label_eq_mat
218+ average_precision = (
219+ tf.reduce_sum(precision_at_k * relevant_at_k, reduction_indices=[1]) /
220+ tf.cast(tf.squeeze(num_relevant), tf.float32))
221+ 
222+ return slim.metrics.streaming_mean(average_precision)
223+ 
The file is empty
The file is empty
The file is empty
@@ -0,0 +1,134 @@
1+# vim: expandtab:ts=4:sw=4
2+from npu_bridge.npu_init import *
3+import tensorflow as tf
4+import tensorflow.contrib.slim as slim
5+ 
6+from . import residual_net
7+ 
8+ 
9+def create_network(images, num_classes=None, add_logits=True, reuse=None,
10+ create_summaries=True, weight_decay=1e-8):
11+ nonlinearity = tf.nn.elu
12+ conv_weight_init = tf.truncated_normal_initializer(stddev=1e-3)
13+ conv_bias_init = tf.zeros_initializer()
14+ conv_regularizer = slim.l2_regularizer(weight_decay)
15+ fc_weight_init = tf.truncated_normal_initializer(stddev=1e-3)
16+ fc_bias_init = tf.zeros_initializer()
17+ fc_regularizer = slim.l2_regularizer(weight_decay)
18+ 
19+ def batch_norm_fn(x):
20+ return slim.batch_norm(x, scope=tf.get_variable_scope().name + "/bn")
21+ 
22+ network = images
23+ network = slim.conv2d(
24+ network, 32, [3, 3], stride=1, activation_fn=nonlinearity,
25+ padding="SAME", normalizer_fn=batch_norm_fn, scope="conv1_1",
26+ weights_initializer=conv_weight_init, biases_initializer=conv_bias_init,
27+ weights_regularizer=conv_regularizer)
28+ if create_summaries:
29+ tf.summary.histogram(network.name + "/activations", network)
30+ tf.summary.image("conv1_1/weights", tf.transpose(
31+ slim.get_variables("conv1_1/weights:0")[0], [3, 0, 1, 2]),
32+ max_outputs=128)
33+ network = slim.conv2d(
34+ network, 32, [3, 3], stride=1, activation_fn=nonlinearity,
35+ padding="SAME", normalizer_fn=batch_norm_fn, scope="conv1_2",
36+ weights_initializer=conv_weight_init, biases_initializer=conv_bias_init,
37+ weights_regularizer=conv_regularizer)
38+ if create_summaries:
39+ tf.summary.histogram(network.name + "/activations", network)
40+ 
41+ network = slim.max_pool2d(
42+ network, [3, 3], [2, 2], scope="pool1", padding="SAME")
43+ 
44+ network = residual_net.residual_block(
45+ network, "conv2_1", nonlinearity, conv_weight_init, conv_bias_init,
46+ conv_regularizer, increase_dim=False, is_first=True,
47+ summarize_activations=create_summaries)
48+ network = residual_net.residual_block(
49+ network, "conv2_3", nonlinearity, conv_weight_init, conv_bias_init,
50+ conv_regularizer, increase_dim=False,
51+ summarize_activations=create_summaries)
52+ 
53+ network = residual_net.residual_block(
54+ network, "conv3_1", nonlinearity, conv_weight_init, conv_bias_init,
55+ conv_regularizer, increase_dim=True,
56+ summarize_activations=create_summaries)
57+ network = residual_net.residual_block(
58+ network, "conv3_3", nonlinearity, conv_weight_init, conv_bias_init,
59+ conv_regularizer, increase_dim=False,
60+ summarize_activations=create_summaries)
61+ 
62+ network = residual_net.residual_block(
63+ network, "conv4_1", nonlinearity, conv_weight_init, conv_bias_init,
64+ conv_regularizer, increase_dim=True,
65+ summarize_activations=create_summaries)
66+ network = residual_net.residual_block(
67+ network, "conv4_3", nonlinearity, conv_weight_init, conv_bias_init,
68+ conv_regularizer, increase_dim=False,
69+ summarize_activations=create_summaries)
70+ 
71+ feature_dim = network.get_shape().as_list()[-1]
72+ print("feature dimensionality: ", feature_dim)
73+ network = slim.flatten(network)
74+ 
75+ network = slim.dropout(network, keep_prob=0.6)
76+ network = slim.fully_connected(
77+ network, feature_dim, activation_fn=nonlinearity,
78+ normalizer_fn=batch_norm_fn, weights_regularizer=fc_regularizer,
79+ scope="fc1", weights_initializer=fc_weight_init,
80+ biases_initializer=fc_bias_init)
81+ 
82+ features = network
83+ 
84+ # Features in rows, normalize axis 1.
85+ features = tf.nn.l2_normalize(features, dim=1)
86+ 
87+ if add_logits:
88+ with slim.variable_scope.variable_scope("ball", reuse=reuse):
89+ weights = slim.model_variable(
90+ "mean_vectors", (feature_dim, int(num_classes)),
91+ initializer=tf.truncated_normal_initializer(stddev=1e-3),
92+ regularizer=None)
93+ scale = slim.model_variable(
94+ "scale", (), tf.float32,
95+ initializer=tf.constant_initializer(0., tf.float32),
96+ regularizer=slim.l2_regularizer(1e-1))
97+ if create_summaries:
98+ tf.summary.scalar("scale", scale)
99+ scale = tf.nn.softplus(scale)
100+ 
101+ # Mean vectors in colums, normalize axis 0.
102+ weights_normed = tf.nn.l2_normalize(weights, dim=0)
103+ logits = scale * tf.matmul(features, weights_normed)
104+ else:
105+ logits = None
106+ return features, logits
107+ 
108+ 
109+def create_network_factory(is_training, num_classes, add_logits,
110+ weight_decay=1e-8, reuse=None):
111+ 
112+ def factory_fn(image):
113+ with slim.arg_scope([slim.batch_norm, slim.dropout],
114+ is_training=is_training):
115+ with slim.arg_scope([slim.conv2d, slim.fully_connected,
116+ slim.batch_norm, slim.layer_norm],
117+ reuse=reuse):
118+ features, logits = create_network(
119+ image, num_classes=num_classes, add_logits=add_logits,
120+ reuse=reuse, create_summaries=is_training,
121+ weight_decay=weight_decay)
122+ return features, logits
123+ 
124+ return factory_fn
125+ 
126+ 
127+def preprocess(image, is_training=False, input_is_bgr=False):
128+ if input_is_bgr:
129+ image = image[:, :, ::-1] # BGR to RGB
130+ image = tf.divide(tf.cast(image, tf.float32), 255.0)
131+ if is_training:
132+ image = tf.image.random_flip_left_right(image)
133+ return image
134+ 
@@ -0,0 +1,86 @@
1+# vim: expandtab:ts=4:sw=4
2+from npu_bridge.npu_init import *
3+import tensorflow as tf
4+from tensorflow.contrib import slim
5+ 
6+ 
7+def _batch_norm_fn(x, scope=None):
8+ if scope is None:
9+ scope = tf.get_variable_scope().name + "/bn"
10+ return slim.batch_norm(x, scope=scope)
11+ 
12+ 
13+def create_link(
14+ incoming, network_builder, scope, nonlinearity=tf.nn.elu,
15+ weights_initializer=tf.truncated_normal_initializer(stddev=1e-3),
16+ regularizer=None, is_first=False, summarize_activations=True):
17+ if is_first:
18+ network = incoming
19+ else:
20+ network = _batch_norm_fn(incoming, scope=scope + "/bn")
21+ network = nonlinearity(network)
22+ if summarize_activations:
23+ tf.summary.histogram(scope+"/activations", network)
24+ 
25+ pre_block_network = incoming
26+ post_block_network = network_builder(network, scope)
27+ 
28+ incoming_dim = pre_block_network.get_shape().as_list()[-1]
29+ outgoing_dim = post_block_network.get_shape().as_list()[-1]
30+ if incoming_dim != outgoing_dim:
31+ assert outgoing_dim == 2 * incoming_dim, \
32+ "%d != %d" % (outgoing_dim, 2 * incoming)
33+ projection = slim.conv2d(
34+ incoming, outgoing_dim, 1, 2, padding="SAME", activation_fn=None,
35+ scope=scope+"/projection", weights_initializer=weights_initializer,
36+ biases_initializer=None, weights_regularizer=regularizer)
37+ network = projection + post_block_network
38+ else:
39+ network = incoming + post_block_network
40+ return network
41+ 
42+ 
43+def create_inner_block(
44+ incoming, scope, nonlinearity=tf.nn.elu,
45+ weights_initializer=tf.truncated_normal_initializer(1e-3),
46+ bias_initializer=tf.zeros_initializer(), regularizer=None,
47+ increase_dim=False, summarize_activations=True):
48+ n = incoming.get_shape().as_list()[-1]
49+ stride = 1
50+ if increase_dim:
51+ n *= 2
52+ stride = 2
53+ 
54+ incoming = slim.conv2d(
55+ incoming, n, [3, 3], stride, activation_fn=nonlinearity, padding="SAME",
56+ normalizer_fn=_batch_norm_fn, weights_initializer=weights_initializer,
57+ biases_initializer=bias_initializer, weights_regularizer=regularizer,
58+ scope=scope + "/1")
59+ if summarize_activations:
60+ tf.summary.histogram(incoming.name + "/activations", incoming)
61+ 
62+ incoming = slim.dropout(incoming, keep_prob=0.6)
63+ 
64+ incoming = slim.conv2d(
65+ incoming, n, [3, 3], 1, activation_fn=None, padding="SAME",
66+ normalizer_fn=None, weights_initializer=weights_initializer,
67+ biases_initializer=bias_initializer, weights_regularizer=regularizer,
68+ scope=scope + "/2")
69+ return incoming
70+ 
71+ 
72+def residual_block(incoming, scope, nonlinearity=tf.nn.elu,
73+ weights_initializer=tf.truncated_normal_initializer(1e3),
74+ bias_initializer=tf.zeros_initializer(), regularizer=None,
75+ increase_dim=False, is_first=False,
76+ summarize_activations=True):
77+ 
78+ def network_builder(x, s):
79+ return create_inner_block(
80+ x, s, nonlinearity, weights_initializer, bias_initializer,
81+ regularizer, increase_dim, summarize_activations)
82+ 
83+ return create_link(
84+ incoming, network_builder, scope, nonlinearity, weights_initializer,
85+ regularizer, is_first, summarize_activations)
86+ 
@@ -0,0 +1,616 @@
1+# Copyright 2021 Huawei Technologies Co., Ltd
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+# vim: expandtab:ts=4:sw=4
16+from npu_bridge.npu_init import *
17+import string
18+import os
19+import threading
20+import numpy as np
21+ 
22+import tensorflow as tf
23+import tensorflow.contrib.slim as slim
24+import time
25+ 
26+config = tf.ConfigProto()
27+custom_op = config.graph_options.rewrite_options.custom_optimizers.add()
28+custom_op.name = "NpuOptimizer"
29+custom_op.parameter_map["use_off_line"].b = True
30+custom_op.parameter_map["mix_compile_mode"].b = True
31+custom_op.parameter_map["precision_mode"].s = tf.compat.as_bytes("force_fp32")#allow_mix_precision
32+#custom_op.parameter_map["modify_mixlist"].s = tf.compat.as_bytes("/home/test/ops_info.json")
33+config.graph_options.rewrite_options.remapping = RewriterConfig.OFF
34+config.graph_options.rewrite_options.memory_optimization = RewriterConfig.OFF
35+ 
36+def run_in_batches(f, data_dict, out, batch_size):
37+ """Process data in batches.
38+ 
39+ Parameters
40+ ----------
41+ f : Callable[Dict[tf.Tensor, np.ndarray] -> np.ndarray
42+ A function that maps a given input (one or multiple inpu arrays) to a
43+ single output array.
44+ data_dict : Dict[tf.Tensor, np.ndarray]
45+ Maps from symbolic input tensor to numpy data array.
46+ out : np.ndarray
47+ The computed function output will be stored in this array; must be have
48+ compatible shape and length to the output computed by `f`.
49+ batch_size : int
50+ The number of samples to compute in each call to `f`. If the length of
51+ the input array is not divisible by the batch size, the final call to
52+ `f` contains fewer examples.
53+ 
54+ """
55+ data_len = len(out)
56+ num_batches = int(data_len / batch_size)
57+ 
58+ def pad(x):
59+ x = np.asarray(x)
60+ y = np.full((batch_size, ) + x.shape[1:], x[0], dtype=x.dtype)
61+ y[:x.shape[0]] = x
62+ return y
63+ 
64+ s, e = 0, batch_size
65+ for i in range(num_batches):
66+ s, e = i * batch_size, (i + 1) * batch_size
67+ batch_data_dict = {k: v[s:e] for k, v in data_dict.items()}
68+ out[s:e] = f(batch_data_dict)
69+ if e < len(out):
70+ remaining_len = len(out) - e
71+ batch_data_dict = {k: pad(v[e:]) for k, v in data_dict.items()}
72+ out[e:] = f(batch_data_dict)[:remaining_len]
73+ 
74+ 
75+def iterate_forever(batch_size, data, *other_data):
76+ """Iterate over dataset indefenitely.
77+ 
78+ Parameters
79+ ----------
80+ batch_size : int
81+ The batch size.
82+ data : ndarray
83+ The first input array.
84+ other_data
85+ Additional input arrays; must be of type np.ndarray.
86+ 
87+ Returns
88+ -------
89+ List[np.ndarray]
90+ A dataset batch. The length of each entry in the list is `batch_size`.
91+ 
92+ """
93+ data_len = len(data)
94+ num_batches = int(data_len / batch_size)
95+ 
96+ while True:
97+ data_list = [data] + list(other_data)
98+ s, e = 0, 0
99+ for i in range(num_batches):
100+ s, e = i * batch_size, (i + 1) * batch_size
101+ batch = [x[s:e] for x in data_list]
102+ yield batch[0] if len(batch) == 1 else batch
103+ if e < data_len:
104+ batch = [x[e:] for x in data_list]
105+ yield batch[0] if len(batch) == 1 else batch
106+ 
107+ 
108+def random_shuffle_forever(batch_size, data, *other_data):
109+ """A generator that randomly selects `batch_size` entries from the data.
110+ 
111+ Parameters
112+ ----------
113+ batch_size : int
114+ The batch size.
115+ data : np.ndarray
116+ The first input array.
117+ other_data
118+ Additional input arrays; must be of type np.ndarray
119+ 
120+ Returns
121+ -------
122+ List[np.ndarray]
123+ A batch of randomly selected entries. The length of each entry in the
124+ list is `batch_size`.
125+ 
126+ """
127+ data_list = [data] + list(other_data)
128+ indices = np.arange(len(data))
129+ while True:
130+ batch_indices = np.random.choice(indices, batch_size, replace=False)
131+ batch = [x[batch_indices] for x in data_list]
132+ yield batch[0] if len(batch) == 1 else batch
133+ 
134+ 
135+def random_sample_identities_forever(batch_size, num_samples_per_id, data_x,
136+ data_y, num_fa_images=0):
137+ """A generator that randomly selects a fixed number of entries per label.
138+ 
139+ If false alarms are passed into this function, they should have a negative
140+ label, i.e., `data_y[i] < 0` if the i-th example corresponds to a false
141+ alarm.
142+ 
143+ Parameters
144+ ----------
145+ batch_size : int
146+ The batch size.
147+ num_samples_per_id : int
148+ Number of examples per label in each batch. If the `batch_size` is not
149+ divisible by `num_samples_per_id` then the batch is filled with false
150+ alarms. A warning is printed if no false alarms are available to fill
151+ up the batch.
152+ data_x : List[string] | np.ndarray
153+ The data array; either a list of filenames or a tensor of input images.
154+ data_y : List[int] | np.ndarray
155+ The label array (either as list of one-dimensional numpy array).
156+ num_fa_images : Optional[int]
157+ Number of false alarm images to include in each batch; defaults to zero.
158+ 
159+ Returns
160+ -------
161+ List[np.ndarray]
162+ Returns a list of length two where the first entry is the data array
163+ corresponding to `data_x` and the second entry is the label array
164+ corresponding to `data_y`. The elements in the list are of length
165+ `batch_size`.
166+ 
167+ """
168+ assert (batch_size - num_fa_images) % num_samples_per_id == 0
169+ num_ids_per_batch = int((batch_size - num_fa_images) / num_samples_per_id)
170+ 
171+ data_x = np.asarray(data_x)
172+ data_y = np.asarray(data_y)
173+ 
174+ unique_y = np.unique(data_y[data_y >= 0])
175+ y_to_idx = {y: np.where(data_y == y)[0] for y in unique_y}
176+ fa_indices = np.where(data_y < 0)[0]
177+ 
178+ while True:
179+ # Draw the desired number of identities.
180+ indices = np.random.choice(
181+ len(unique_y), num_ids_per_batch, replace=False)
182+ batch_unique_y = unique_y[indices]
183+ 
184+ batch_x = np.zeros((batch_size, ) + data_x.shape[1:], data_x.dtype)
185+ batch_y = np.zeros((batch_size, ), data_y.dtype)
186+ e = 0
187+ for i, y in enumerate(batch_unique_y):
188+ num_samples = min(num_samples_per_id, len(y_to_idx[y]))
189+ indices = np.random.choice(y_to_idx[y], num_samples, replace=False)
190+ s, e = e, e + num_samples
191+ batch_x[s:e] = data_x[indices]
192+ batch_y[s:e] = y
193+ 
194+ # Fill up remaining space with false alarms.
195+ num_samples = len(batch_x) - e
196+ if num_fa_images > 0:
197+ num_batch_fa_samples = min(num_samples, len(fa_indices))
198+ indices = np.random.choice(
199+ fa_indices, num_batch_fa_samples, replace=False)
200+ s, e = e, e + num_batch_fa_samples
201+ batch_x[s:e] = data_x[indices]
202+ batch_y[s:e] = data_y[indices]
203+ 
204+ # If we need to add more data, random sample ids until we have reached
205+ # the batch size.
206+ num_samples = len(batch_x) - e
207+ num_tries = 0
208+ while num_samples > 0 and num_tries < 100:
209+ y = np.random.choice(unique_y)
210+ if y in batch_unique_y:
211+ # Find a target that we have not yet in this batch.
212+ num_tries += 1
213+ continue
214+ 
215+ num_samples = min(num_samples, len(y_to_idx[y]))
216+ indices = np.random.choice(y_to_idx[y], num_samples, replace=False)
217+ s, e = e, e + num_samples
218+ batch_x[s:e] = data_x[indices]
219+ batch_y[s:e] = y
220+ num_samples = len(batch_x) - e
221+ 
222+ if e < batch_size:
223+ print("ERROR: Failed to sample a full batch. Adding corrupt data.")
224+ yield [batch_x, batch_y]
225+ 
226+ 
227+def _truncate_dataset_to_batch_size(batch_size, data, *other_data):
228+ """Truncate given input data to a multiple of the batch size.
229+ 
230+ Parameters
231+ ----------
232+ batch_size : int
233+ The batch size. The length of the truncated data is a multiple of this
234+ value.
235+ data : np.ndarray
236+ The first input array.
237+ *other_data
238+ Additional input arrays; must be of type np.ndarray.
239+ 
240+ Returns
241+ -------
242+ List[np.ndarray]
243+ The truncated data. The length of each entry in the list is a multiple
244+ of the batch size.
245+ 
246+ """
247+ num_batches = int(len(data) / batch_size)
248+ new_len = num_batches * batch_size
249+ dataset = [data] + list(other_data)
250+ if new_len < len(data):
251+ print(
252+ "WARNING dataset length is not a multiple of batch size. "
253+ "Truncating from %d to %d." % (len(data), new_len))
254+ dataset = [x[:new_len] for x in dataset]
255+ return num_batches, dataset[0] if len(dataset) == 1 else dataset
256+ 
257+ 
258+def _generate_run_id(size=6, chars=None):
259+ """Generate a random ID of length `size`.
260+ 
261+ Parameters
262+ ----------
263+ size : int
264+ chars : Optional[str]
265+ Optional list of characters to use for generating the ID.
266+ 
267+ Returns
268+ -------
269+ str
270+ Returns a random identifier of length `size`.
271+ 
272+ """
273+ if chars is None:
274+ chars = string.ascii_uppercase + string.digits
275+ import random
276+ return ''.join(random.choice(chars) for _ in range(size))
277+ 
278+ 
279+class ThreadSafeIterator(object):
280+ """
281+ This class wraps an iterator (or generator) such that only one thread at a
282+ time is granted access.
283+ 
284+ Parameters
285+ ----------
286+ iterator_or_generator
287+ An iterator or generator to be wrapped.
288+ 
289+ """
290+ 
291+ def __init__(self, iterator_or_generator):
292+ self._iterator_or_generator = iterator_or_generator
293+ self._lock = threading.Lock()
294+ 
295+ def __iter__(self):
296+ return self
297+ 
298+ def __next__(self):
299+ with self._lock:
300+ return next(self._iterator_or_generator)
301+ 
302+ def next(self):
303+ with self._lock:
304+ return self._iterator_or_generator.next()
305+ 
306+ 
307+class QueuedTrainer(object):
308+ """
309+ This class implements code to train and evaluate TensorFlow models based on
310+ TensorFlow-Slim. Image loading and preprocessing is de-coupled from the
311+ training steps using a tf.FIFOQueue.
312+ 
313+ Parameters
314+ ----------
315+ enqueue_vars : List[tf.Tensor]
316+ A list of tensors to be enqueued; usually the labels and preprocessed
317+ images.
318+ input_vars : Optional[List[tf.Tensor]]
319+ An optional list of input tensors; usually the labels and raw (not
320+ preprocessed) images or filenames to the images. The list must be of the
321+ same length as the `enqueue_vars` and there must be a one-to-one
322+ correspondence, i.e., the i-th element in `enqueue_vars` is i-th
323+ preprocessed element in `input_vars`. If None, the input_vars are set to
324+ `enqueue_vars`.
325+ num_enqueue_threads : Optional[int]
326+ Number of threads used to preprocess data in parallel.
327+ queue_capacity : Optional[int]
328+ Maximum number of elements in the queue; defaults to 512.
329+ 
330+ """
331+ 
332+ def __init__(self, enqueue_vars, input_vars=None, num_enqueue_threads=4,
333+ queue_capacity=512):
334+ if input_vars is None:
335+ input_vars = enqueue_vars
336+ self._input_vars = input_vars
337+ self._enqueue_vars = enqueue_vars
338+ 
339+ shapes = [var.get_shape().as_list()[1:] for var in enqueue_vars]
340+ dtypes = [var.dtype for var in enqueue_vars]
341+ self._queue = tf.FIFOQueue(queue_capacity, dtypes, shapes)
342+ 
343+ self._num_enqueue_threads = num_enqueue_threads
344+ self._enqueue_threads = []
345+ self._enqueue_op = self._queue.enqueue_many(self._enqueue_vars)
346+ self._stop_op = self._queue.close(cancel_pending_enqueues=True)
347+ self._coordinator = None
348+ 
349+ self._feed_generator = None
350+ self._batch_size = None
351+ self._init_fns = []
352+ 
353+ def get_input_vars(self, batch_size):
354+ """Get the top `batch_size` elements from the queue. The tensors
355+ returned by this functions should be passed on the the TensorFlow model.
356+ 
357+ Parameters
358+ ----------
359+ batch_size : int
360+ The batch size.
361+ 
362+ Returns
363+ -------
364+ List[tf.Tensor]
365+ Returns the top `batch_size` elements from the queue. There is a
366+ one-to-one correspondence between the `enqueue_vars` passed in to
367+ the constructor of this class and the tensors in the list returned
368+ by this function.
369+ 
370+ """
371+ self._batch_size = batch_size
372+ return self._queue.dequeue_many(batch_size)
373+ 
374+ def run(self, feed_generator, train_op, log_dir="/tmp/slim_trainer/",
375+ restore_path=None, variables_to_restore=None, run_id=None,
376+ max_checkpoints_to_keep=0, **kwargs):
377+ """ Run training.
378+ 
379+ Parameters
380+ ----------
381+ feed_generator : Iterator[ndarray, ...]
382+ An iterator or generator that returns batches of training data; must
383+ return a one-to-one correspondence with the `enqueue_vars` passed
384+ to the constructor of this class.
385+ train_op : tf.Tensor
386+ The training operation created with `slim.learning.create_train_op`.
387+ log_dir : Optional[str]
388+ Path to TensorFlow log directory. This value is used in conjunction
389+ with `run_id` to generate the checkpoint and summary directory;
390+ defaults to '/tmp/slim_trainer'.
391+ restore_path : Optional[str]
392+ An optional checkpoint path. If not None, resumes training from the
393+ given checkpoint.
394+ variables_to_restore : Optional[List[str]]
395+ An optional list of variable scopes. If not None, only restores
396+ variables under the given scope. This value is ignored if
397+ `restore_path` is None.
398+ run_id : Optional[str]
399+ A string that identifies this training run. The checkpoints and
400+ TensorFlow summaries are stored in `log_dir/run_id`. If None, a
401+ random ID will be generated. Point tensorboard to this directory to
402+ monitor training progress.
403+ max_checkpoints_to_keep : int
404+ Keep only the `max_checkpoints_to_keep` newest checkpoints. If 0,
405+ keep all checkpoints.
406+ kwargs:
407+ Additional named arguments passed on to tf.slim.learning.train,
408+ e.g., `number_of_steps=100` to run 100 iterations of training.
409+ 
410+ """
411+ if restore_path is not None:
412+ if variables_to_restore is None:
413+ variables_to_restore = slim.get_variables_to_restore()
414+ init_assign_op, init_feed_dict = slim.assign_from_checkpoint(
415+ restore_path, variables_to_restore)
416+ self._init_fns.append(lambda sess: sess.run(
417+ init_assign_op, init_feed_dict))
418+ self._feed_generator = ThreadSafeIterator(feed_generator)
419+ self._coordinator = tf.train.Coordinator()
420+ 
421+ if run_id is None:
422+ run_id = _generate_run_id(6)
423+ log_dir = os.path.join(log_dir, run_id)
424+ print("---------------------------------------")
425+ print("Run ID: ", run_id)
426+ print("Log directory: ", log_dir)
427+ print("---------------------------------------")
428+ 
429+ saver = tf.train.Saver(max_to_keep=max_checkpoints_to_keep)
430+ try:
431+ slim.learning.train(
432+ train_op, log_dir, self._train_step_fn, session_config=config, saver=saver,
433+ **kwargs)
434+ except UnboundLocalError:
435+ # NOTE(nwojke): Due to a bug in slim, a local variable 'total_loss'
436+ # is referenced when an exception is raised during training. We
437+ # catch the exception here because it occurs whenever we close the
438+ # queue with self._stop_all_threads().
439+ pass
440+ self._wait_for_threads()
441+ 
442+ def evaluate(self, dataset, checkpoint_dir, log_dir, run_id=None,
443+ init_op=None, eval_op=None, final_op=None,
444+ summary_op=None, variables_to_restore=None,
445+ eval_interval_secs=60, max_num_evaluations=None):
446+ """Run evaluation. Monitors files in the log directory and computes
447+ evaluation metrics. This function must be called concurrently to
448+ training (in a separate process).
449+ 
450+ WARNING: The dataset is truncated to the batch size. Thus, the computed
451+ metrics are only accurate if the dataset length is divisible by the
452+ batch size.
453+ 
454+ Parameters
455+ ----------
456+ dataset : List[T]
457+ The dataset is a list (or tuple) of data arrays. The length of the
458+ list must be the same as the `input_vars` passed to the constructor
459+ of this class and there must be a one-to-one correspondence such
460+ that `dataset[i]` corresponds to the numeric data of its symbolic
461+ equivalent in `input_vars[i]`.
462+ checkpoint_dir : str
463+ The directory where checkpoints are stored. Should be set to
464+ `log_dir` of the training process.
465+ log_dir : str
466+ Path to TensorFlow log directory where evaluation logs will be
467+ stored. This directory should be different from the `log_dir`
468+ passed to `run`.
469+ run_id : Optional[str]
470+ A string that identifies the training runrun. Should be set to
471+ `run_id` passed to `run`.
472+ init_op : Optional[tf.Tensor]
473+ Optional operation to execute prior to processing the `dataset`.
474+ eval_op : Optional[tf.Tensor]
475+ Evaluation operation; will be executed for each batch in the
476+ `dataset`.
477+ final_op : Optional[tf.Tensor]
478+ Optional operation to execute after processing the `dataset`.
479+ summary_op : Optional[tf.Tensor]
480+ Summary operation; defaults to `tf.summary.merge_all()`.
481+ variables_to_restore : Optional[List[tf.Tensor]]
482+ List of variables to restore; defaults to
483+ `slim.get_variables_to_restore()`.
484+ eval_interval_secs : Optional[int]
485+ Poll the `checkpoint_dir` every `eval_interval_secs` seconds for
486+ new checkpoints.
487+ max_num_evaluations : Optional[int]
488+ Evaluate at most `max_num_evaluations` checkpoints.
489+ 
490+ Returns
491+ -------
492+ T
493+ Returns the value of the last call to `final_op` or None.
494+ 
495+ """
496+ if run_id is None:
497+ print("---------------------------------------")
498+ print("Checkpoint directory: ", checkpoint_dir)
499+ print("Log directory: ", log_dir)
500+ print("---------------------------------------")
501+ else:
502+ checkpoint_dir = os.path.join(checkpoint_dir, run_id)
503+ log_dir = os.path.join(log_dir, run_id)
504+ print("---------------------------------------")
505+ print("Run ID: ", run_id)
506+ print("Checkpoint directory: ", checkpoint_dir)
507+ print("Log directory: ", log_dir)
508+ print("---------------------------------------")
509+ 
510+ if summary_op is None:
511+ summary_op = tf.summary.merge_all()
512+ 
513+ global_step = tf.train.get_or_create_global_step()
514+ 
515+ if variables_to_restore is None:
516+ variables_to_restore = slim.get_variables_to_restore()
517+ saver = tf.train.Saver(variables_to_restore)
518+ summary_writer = tf.summary.FileWriter(log_dir)
519+ sv = tf.train.Supervisor(
520+ graph=tf.get_default_graph(), logdir=log_dir, summary_op=None,
521+ summary_writer=None, global_step=None, saver=saver)
522+ 
523+ print("Entering evaluation loop. Waiting for checkpoints.")
524+ num_batches, dataset = _truncate_dataset_to_batch_size(
525+ self._batch_size, *dataset)
526+ 
527+ final_op_value = None
528+ num_evaluations = 0
529+ for checkpoint_path in slim.evaluation.checkpoints_iterator(
530+ checkpoint_dir, eval_interval_secs):
531+ with sv.managed_session(start_standard_services=False) as session:
532+ sv.saver.restore(session, checkpoint_path)
533+ sv.start_queue_runners(session)
534+ 
535+ print("Starting evaluation of '%s'" % checkpoint_path)
536+ self._feed_generator = iterate_forever(
537+ self._batch_size, *dataset)
538+ self._coordinator = tf.train.Coordinator()
539+ for fn in self._init_fns:
540+ fn(session)
541+ self._start_enqueue(session, num_threads=1)
542+ 
543+ if init_op is not None:
544+ session.run(init_op)
545+ 
546+ if eval_op is not None:
547+ for i in range(num_batches):
548+ session.run(eval_op)
549+ 
550+ if final_op is not None:
551+ final_op_value = session.run(final_op)
552+ else:
553+ final_op_value = None
554+ 
555+ summary_str = session.run(summary_op)
556+ global_step_value = session.run(global_step)
557+ summary_writer.add_summary(summary_str, global_step_value)
558+ summary_writer.flush()
559+ 
560+ self._stop_all_threads(session)
561+ print("Finished evaluation of '%s'" % checkpoint_path)
562+ 
563+ num_evaluations += 1
564+ if max_num_evaluations is not None \
565+ and num_evaluations >= max_num_evaluations:
566+ break
567+ return final_op_value
568+ 
569+ def _train_step_fn(self, session, train_op, global_step,
570+ train_step_kwargs):
571+ if len(self._enqueue_threads) == 0:
572+ for fn in self._init_fns:
573+ fn(session)
574+ self._start_enqueue(session)
575+ train_start = time.time()
576+ total_loss, should_stop = slim.learning.train_step(
577+ session, train_op, global_step, train_step_kwargs)
578+ print("========each step time: {:.2f}".format((time.time() - train_start)*1000))
579+ if should_stop or self._coordinator.should_stop():
580+ self._stop_all_threads(session)
581+ print("========loss: ", total_loss)
582+ return total_loss, should_stop
583+ 
584+ def _stop_all_threads(self, session):
585+ self._coordinator.request_stop()
586+ session.run(self._stop_op) # Close the queue.
587+ 
588+ def _wait_for_threads(self):
589+ self._coordinator.join(self._enqueue_threads)
590+ self._enqueue_threads = []
591+ 
592+ def _start_enqueue(self, session, num_threads=None):
593+ if num_threads is None:
594+ num_threads = self._num_enqueue_threads
595+ for _ in range(num_threads):
596+ thread = threading.Thread(
597+ target=self._run_enqueue_thread, args=(session, ))
598+ thread.start()
599+ self._enqueue_threads.append(thread)
600+ 
601+ def _run_enqueue_thread(self, session):
602+ try:
603+ for data in self._feed_generator:
604+ if self._coordinator.should_stop():
605+ break
606+ try:
607+ feed_dict = {
608+ var: value for var, value in
609+ zip(self._input_vars, data)}
610+ session.run(self._enqueue_op, feed_dict=feed_dict)
611+ except (tf.errors.CancelledError, tf.errors.AbortedError):
612+ # We have been requested to stop enqueuing data.
613+ break
614+ except Exception as e:
615+ print("EnqueueError:", e)
616+ self._stop_all_threads(session)
@@ -0,0 +1,25 @@
1+#!/bin/bash
2+### Do not need to Configure CANN Environment on Modelarts Platform, because it has been set already.
3+### Modelarts Platform command for train
4+export TF_CPP_MIN_LOG_LEVEL=2 ## Tensorflow api print Log Config
5+export ASCEND_SLOG_PRINT_TO_STDOUT=0 ## Print log on terminal on(1), off(0)
6+ 
7+code_dir=${1}
8+data_dir=${2}
9+result_dir=${3}
10+obs_url=${4}
11+ 
12+current_time=`date "+%Y-%m-%d-%H-%M-%S"`
13+ 
14+python3.7 ${code_dir}/train_market1501.py \
15+ --dataset_dir=${data_dir} \
16+ #--result=${result_dir} \
17+ #--obs_dir=${obs_url} \
18+ #--chip='npu' \
19+ #--loss_mode=cosine-softmax \
20+ --run_id=cosine-softmax \
21+ #--platform='modelarts' \
22+ #--npu_dump_data=False \
23+ #--npu_dump_graph=False \
24+ #--npu_profiling=False \
25+ #--npu_auto_tune=False 2>&1 | tee ${result_dir}/${current_time}_train_npu.log
@@ -0,0 +1,25 @@
1+#!/bin/bash
2+### Do not need to Configure CANN Environment on Modelarts Platform, because it has been set already.
3+### Modelarts Platform command for train
4+export TF_CPP_MIN_LOG_LEVEL=2 ## Tensorflow api print Log Config
5+export ASCEND_SLOG_PRINT_TO_STDOUT=0 ## Print log on terminal on(1), off(0)
6+ 
7+code_dir=${1}
8+data_dir=${2}
9+result_dir=${3}
10+obs_url=${4}
11+ 
12+current_time=`date "+%Y-%m-%d-%H-%M-%S"`
13+ 
14+python3.7 ${code_dir}/train_market1501.py
15+ #--dataset_dir=${data_dir} \
16+ #--result=${result_dir} \
17+ #--obs_dir=${obs_url} \
18+ #--chip='npu' \
19+ #--loss_mode=cosine-softmax \
20+ #--run_id=cosine-softmax \
21+ #--platform='modelarts' \
22+ #--npu_dump_data=False \
23+ #--npu_dump_graph=False \
24+ #--npu_profiling=False \
25+ #--npu_auto_tune=False 2>&1 | tee ${result_dir}/${current_time}_train_npu.log
@@ -0,0 +1,30 @@
1+#!/bin/bash
2+### Apulis Platform command for train (CANN Version-20.2)
3+## Set Ascend Log Level, if u wanna to print on terminal, you should open 'ASCEND_SLOG_PRINT_TO_STDOUT'.
4+## refer to link: https://support.huaweicloud.com/Graphdevg-cann202training1/atlasag_25_0077.html
5+export ASCEND_SLOG_PRINT_TO_STDOUT=0 ## Print log on terminal on(1), off(0)
6+export ASCEND_GLOBAL_LOG_LEVEL=3 ## Ascend log level. debug(0), info(1), warning(2), error(3)
7+export TF_CPP_MIN_LOG_LEVEL=2 ## Tensorflow api print Log Config
8+ 
9+## Configure Environment for Auto Tune
10+## refer to link:https://support.huaweicloud.com/developmenttg-cann330alphaXtraining/atlasautotune_16_0014.html
11+export install_path=/home/HwHiAiUser/Ascend/ascend-toolkit/latest
12+export PATH=${install_path}/fwkacllib/bin:$PATH
13+export LD_LIBRARY_PATH=${install_path}/fwkacllib/lib64:$LD_LIBRARY_PATH
14+export PYTHONPATH=${install_path}/fwkacllib/python/site-packages:$PYTHONPATH
15+ 
16+code_dir=$(cd "$(dirname "$0")"; cd ..; pwd)
17+echo "===>>>Python boot file dir: ${code_dir}"
18+ 
19+current_time=`date "+%Y-%m-%d-%H-%M-%S"`
20+ 
21+python3.7 ${code_dir}/train.py \
22+ --chip='npu' \
23+ --dataset=/data/dataset/storage/flowers/images \
24+ --result=./log \
25+ --num_classes=5 \
26+ --train_step=2 \
27+ --npu_dump_data=False \
28+ --npu_dump_graph=False \
29+ --npu_profiling=False \
30+ --npu_auto_tune=False 2>&1 | tee ${code_dir}/${current_time}_train_npu.log