Initial commit
This commit is contained in:
10
.vscode/sftp.json
vendored
Normal file
10
.vscode/sftp.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Deeplearning",
|
||||
"host": "192.168.40.83",
|
||||
"protocol": "sftp",
|
||||
"port": 22,
|
||||
"username": "deeplearning",
|
||||
"password": "cfca1234",
|
||||
"remotePath": "/home/deeplearning/work/Deeplearning/TensorFlow/DeepWritingID/DeepHWS_ID/",
|
||||
"uploadOnSave": true
|
||||
}
|
||||
661
LICENSE
Normal file
661
LICENSE
Normal file
@@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
@@ -1,2 +0,0 @@
|
||||
# DeepHSV
|
||||
source codes for paper: DeepHSV: User-independent Offline Signature Verification Using Two-Channel CNN
|
||||
104
dataset/dataset_paris.py
Normal file
104
dataset/dataset_paris.py
Normal file
@@ -0,0 +1,104 @@
|
||||
"""
|
||||
@file: dataset_paris.py
|
||||
@time: 2018/7/31 15:03
|
||||
@desc:Create the input data pipeline using `tf.data`
|
||||
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import tensorflow as tf
|
||||
|
||||
image_width = None
|
||||
image_height = None
|
||||
images_dir = None
|
||||
channels = 1
|
||||
|
||||
|
||||
def _read_image(filename, is_augment):
|
||||
image_string = tf.read_file(tf.string_join([images_dir, filename]))
|
||||
image_decoded = tf.image.decode_png(image_string, channels=channels)
|
||||
|
||||
true_constant = tf.constant(1, dtype=tf.int32, name="true_constant")
|
||||
image_decoded = tf.cond(tf.equal(true_constant, is_augment),
|
||||
lambda: tf.image.flip_left_right(image_decoded),
|
||||
lambda: image_decoded)
|
||||
image_resized = tf.image.resize_images(image_decoded, [image_width, image_height])
|
||||
return image_resized
|
||||
|
||||
|
||||
def _parse_function(item):
|
||||
is_aug = tf.string_to_number(item[3], out_type=tf.int32)
|
||||
image0 = _read_image(item[0], is_aug)
|
||||
image1 = _read_image(item[1], is_aug)
|
||||
|
||||
image = tf.concat([image0, image1], 2)
|
||||
|
||||
return image, tf.string_to_number(item[2])
|
||||
|
||||
|
||||
def _input_fn(params, is_training, is_augment=False, pos_repeating=1, only_label=None):
|
||||
"""Train input function.
|
||||
|
||||
Args:
|
||||
listfile_path: listfile has 3 item per line
|
||||
params: contains hyperparameters of the model (ex: data_dir, image's width and height.)
|
||||
"""
|
||||
listfile_path = params.signature_train_list if is_training else params.signature_val_list
|
||||
data = []
|
||||
shuffle_neg = []
|
||||
size_per_signer = params.positive_size + params.negative_size
|
||||
file = open(listfile_path)
|
||||
for i, line in enumerate(file.readlines()):
|
||||
items = line.split(' ')
|
||||
file0 = items[0]
|
||||
file1 = items[1]
|
||||
label = int(items[2])
|
||||
if (only_label is not None and label != only_label) or label == 2:
|
||||
continue
|
||||
|
||||
repeating = 1
|
||||
if is_training and pos_repeating > 0 and i % size_per_signer == 0:
|
||||
"""the number of positive/negative pairs is 276/996,
|
||||
so we need to expand positive pairs, or reduce the negative pairs"""
|
||||
shuffle_neg = np.arange(params.positive_size, size_per_signer)
|
||||
np.random.shuffle(shuffle_neg)
|
||||
shuffle_neg = shuffle_neg[:params.positive_size * pos_repeating]
|
||||
if is_training and pos_repeating > 0:
|
||||
"""expand positive pairs"""
|
||||
if label == 2:
|
||||
repeating = 1 if (i % params.negative_size) > params.positive_size * pos_repeating else 0
|
||||
repeating = 0
|
||||
elif label == 0:
|
||||
"""reduce negative pairs """
|
||||
repeating = 1 if i % size_per_signer in shuffle_neg else 0
|
||||
elif label == 1:
|
||||
repeating = pos_repeating
|
||||
|
||||
for j in range(repeating):
|
||||
"""file0, file1, label, is_augment"""
|
||||
data.append((file0, file1, label, 0))
|
||||
if is_augment and is_training:
|
||||
data.append((file0, file1, label, 1))
|
||||
# data.append((file1, file0, label))
|
||||
file.close()
|
||||
np.random.shuffle(data)
|
||||
print("examples of data: -> %d" % len(data))
|
||||
|
||||
dataset = tf.data.Dataset.from_tensor_slices(np.array(data))
|
||||
dataset = dataset.map(_parse_function, num_parallel_calls=params.num_parallel_calls)
|
||||
dataset = dataset.shuffle(10000)
|
||||
dataset = dataset.repeat(params.num_epochs)
|
||||
dataset = dataset.apply(tf.contrib.data.batch_and_drop_remainder(
|
||||
params.batch_size * params.num_gpus))
|
||||
dataset = dataset.prefetch(10)
|
||||
return dataset
|
||||
|
||||
|
||||
def input_fn(params, is_training, repeating=1, is_augment=False, only_label=None):
|
||||
global image_width, image_height, images_dir, channels
|
||||
image_width = params.image_width
|
||||
image_height = params.image_height
|
||||
images_dir = params.images_dir
|
||||
channels = params.channels
|
||||
return _input_fn(params, is_training, pos_repeating=repeating, is_augment=is_augment, only_label=only_label)
|
||||
|
||||
111
dataset/generate_list_bhsig260.py
Normal file
111
dataset/generate_list_bhsig260.py
Normal file
@@ -0,0 +1,111 @@
|
||||
"""
|
||||
@file: dataset_bhsig260.py
|
||||
@time: 2018/6/20 15:03
|
||||
@desc:Create the paris list of BHSig260 Database
|
||||
|
||||
"""
|
||||
|
||||
import copy
|
||||
import os
|
||||
import sys
|
||||
|
||||
import imageio
|
||||
import numpy as np
|
||||
|
||||
num_genuine = 24
|
||||
num_forged = 30
|
||||
|
||||
|
||||
# 生成数组l的全部组合(长度k)
|
||||
def combine(l, k):
|
||||
answers = []
|
||||
one = [0] * k
|
||||
|
||||
def next_c(li=0, ni=0):
|
||||
if ni == k:
|
||||
answers.append(copy.copy(one))
|
||||
return
|
||||
for lj in range(li, len(l)):
|
||||
one[ni] = l[lj]
|
||||
next_c(lj + 1, ni + 1)
|
||||
|
||||
next_c()
|
||||
return answers
|
||||
|
||||
|
||||
# 生成两个数组间的全部组合
|
||||
def combine_2list(list1, list2):
|
||||
answers = []
|
||||
for i1 in list1:
|
||||
for i2 in list2:
|
||||
answers.append([i1, i2])
|
||||
return answers
|
||||
|
||||
|
||||
def generate_list(data_dir, train_size, filename_pre, listfile_name):
|
||||
root_dir = os.path.basename(data_dir)
|
||||
signers_list = os.listdir(data_dir)
|
||||
list_file_train = open(listfile_name + '_train.txt', 'w')
|
||||
list_file_test = open(listfile_name + '_val.txt', 'w')
|
||||
|
||||
train_indexs = np.arange(0, len(signers_list), 1)
|
||||
np.random.shuffle(train_indexs)
|
||||
train_indexs = train_indexs[:train_size]
|
||||
|
||||
for i, signer in enumerate(signers_list):
|
||||
list_file = list_file_train if i in train_indexs else list_file_test
|
||||
genuine_genuine_suf = combine(list(range(1, num_genuine + 1)), 2)
|
||||
for item in genuine_genuine_suf:
|
||||
genuine0 = "%s/%s/%s-%d-G-%02d%s" % (root_dir, signer, filename_pre, int(signer), item[0], '.jpg')
|
||||
genuine1 = "%s/%s/%s-%d-G-%02d%s" % (root_dir, signer, filename_pre, int(signer), item[1], '.jpg')
|
||||
line = genuine0 + ' ' + genuine1 + ' 1\n'
|
||||
list_file.write(line)
|
||||
|
||||
genuine_forged_suf = combine_2list(list(range(1, num_genuine + 1)), list(range(1, num_forged + 1)))
|
||||
for item in genuine_forged_suf:
|
||||
genuine = "%s/%s/%s-%d-G-%02d%s" % (root_dir, signer, filename_pre, int(signer), item[0], '.jpg')
|
||||
forged = "%s/%s/%s-%d-F-%02d%s" % (root_dir, signer, filename_pre, int(signer), item[1], '.jpg')
|
||||
line = genuine + ' ' + forged + ' 0\n'
|
||||
list_file.write(line)
|
||||
|
||||
list_file_train.close()
|
||||
list_file_test.close()
|
||||
|
||||
|
||||
def rename(dir_path):
|
||||
for root, dirs, files in os.walk(dir_path):
|
||||
for file in files:
|
||||
if not file.endswith('.jpg'):
|
||||
continue
|
||||
new_filename = file.replace('-S-00', '-S-')
|
||||
new_filename = new_filename.replace('-S-0', '-S-')
|
||||
os.rename(os.path.join(root, file), os.path.join(root, new_filename))
|
||||
|
||||
|
||||
def tif_to_jpg(tif_dir, jpg_dir):
|
||||
for root, dirs, files in os.walk(tif_dir):
|
||||
to_dir = root.replace(tif_dir, jpg_dir)
|
||||
if not os.path.exists(to_dir):
|
||||
os.mkdir(to_dir)
|
||||
for file in files:
|
||||
if not file.endswith('.tif'):
|
||||
continue
|
||||
image = imageio.imread(os.path.join(root, file))
|
||||
jpg_file = file.replace('.tif', '.jpg')
|
||||
imageio.imwrite(os.path.join(to_dir, jpg_file), image)
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
|
||||
rename('/home/deeplearning/work/Deeplearning/dataset/writingID/offline/BHSig260_jpgs/')
|
||||
# generate_list('/home/deeplearning/work/Deeplearning/dataset/writingID/offline/BHSig260_jpgs/Hindi', 100, 'H-S',
|
||||
# '../experiments/data_list/bhsig260_Hindi')
|
||||
# generate_list('/home/deeplearning/work/Deeplearning/dataset/writingID/offline/BHSig260_jpgs/Bengali', 50,
|
||||
# 'B-S',
|
||||
# '../experiments/data_list/bhsig260_Bengali')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
96
dataset/generate_list_cedar.py
Normal file
96
dataset/generate_list_cedar.py
Normal file
@@ -0,0 +1,96 @@
|
||||
"""
|
||||
@file: generate_list_cedar.py
|
||||
@time: 2018/6/20 15:03
|
||||
@desc:Create the paris list of CEDAR Database
|
||||
|
||||
"""
|
||||
|
||||
import copy
|
||||
import os
|
||||
import sys
|
||||
|
||||
import imageio
|
||||
import numpy as np
|
||||
|
||||
num_genuine = 24
|
||||
num_forged = 24
|
||||
|
||||
|
||||
# 生成数组l的全部组合(长度k)
|
||||
def combine(l, k):
|
||||
answers = []
|
||||
one = [0] * k
|
||||
|
||||
def next_c(li=0, ni=0):
|
||||
if ni == k:
|
||||
answers.append(copy.copy(one))
|
||||
return
|
||||
for lj in range(li, len(l)):
|
||||
one[ni] = l[lj]
|
||||
next_c(lj + 1, ni + 1)
|
||||
|
||||
next_c()
|
||||
return answers
|
||||
|
||||
|
||||
# 生成两个数组间的全部组合
|
||||
def combine_2list(list1, list2):
|
||||
answers = []
|
||||
for i1 in list1:
|
||||
for i2 in list2:
|
||||
answers.append([i1, i2])
|
||||
return answers
|
||||
|
||||
|
||||
def generate_list(train_size, listfile_name):
|
||||
signers_list = list(range(1, 56))
|
||||
list_file_train = open(listfile_name + '_train.txt', 'w')
|
||||
list_file_test = open(listfile_name + '_val.txt', 'w')
|
||||
|
||||
train_indexs = np.arange(0, len(signers_list), 1)
|
||||
np.random.shuffle(train_indexs)
|
||||
train_indexs = train_indexs[:train_size]
|
||||
|
||||
for i, signer in enumerate(signers_list):
|
||||
list_file = list_file_train if i in train_indexs else list_file_test
|
||||
genuine_genuine_suf = combine(list(range(1, num_genuine + 1)), 2)
|
||||
for item in genuine_genuine_suf:
|
||||
genuine0 = "%s%d_%d%s" % ('full_org/original_', int(signer), item[0], '.png')
|
||||
genuine1 = "%s%d_%d%s" % ('full_org/original_', int(signer), item[1], '.png')
|
||||
line = genuine0 + ' ' + genuine1 + ' 1\n'
|
||||
list_file.write(line)
|
||||
|
||||
genuine_forged_suf = combine_2list(list(range(1, num_genuine + 1)), list(range(1, num_forged + 1)))
|
||||
for item in genuine_forged_suf:
|
||||
genuine = "%s%d_%d%s" % ('full_org/original_', int(signer), item[0], '.png')
|
||||
forged = "%s%d_%d%s" % ('full_forg/forgeries_', int(signer), item[1], '.png')
|
||||
line = genuine + ' ' + forged + ' 0\n'
|
||||
list_file.write(line)
|
||||
|
||||
list_file_train.close()
|
||||
list_file_test.close()
|
||||
|
||||
|
||||
def tif_to_jpg(tif_dir, jpg_dir):
|
||||
for root, dirs, files in os.walk(tif_dir):
|
||||
to_dir = root.replace(tif_dir, jpg_dir)
|
||||
if not os.path.exists(to_dir):
|
||||
os.mkdir(to_dir)
|
||||
for file in files:
|
||||
if not file.endswith('.tif'):
|
||||
continue
|
||||
image = imageio.imread(os.path.join(root, file))
|
||||
jpg_file = file.replace('.tif', '.jpg')
|
||||
imageio.imwrite(os.path.join(to_dir, jpg_file), image)
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
|
||||
generate_list(50, '../experiments/data_list/cedar')
|
||||
# generate_list(100, '../experiments/data_list/bhsig260_Hindi')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
97
dataset/generate_list_firmas.py
Normal file
97
dataset/generate_list_firmas.py
Normal file
@@ -0,0 +1,97 @@
|
||||
"""
|
||||
@file: model.py
|
||||
@time: 2018/4/17 15:03
|
||||
@desc: Generate the list of data pairs
|
||||
|
||||
"""
|
||||
|
||||
import copy
|
||||
import os
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
image_dir = '/home/deeplearning/work/Deeplearning/dataset/writingID/offline/firmas/'
|
||||
list_filename_train = '../experiments/data_list/firmas_pairs_c_train.txt'
|
||||
list_filename_test = '../experiments/data_list/firmas_pairs_c_val.txt'
|
||||
num_genuine = 24
|
||||
num_forged = 30
|
||||
|
||||
|
||||
# 生成数组l的全部组合(长度k)
|
||||
def combine(l, k):
|
||||
answers = []
|
||||
one = [0] * k
|
||||
|
||||
def next_c(li=0, ni=0):
|
||||
if ni == k:
|
||||
answers.append(copy.copy(one))
|
||||
return
|
||||
for lj in range(li, len(l)):
|
||||
one[ni] = l[lj]
|
||||
next_c(lj + 1, ni + 1)
|
||||
|
||||
next_c()
|
||||
return answers
|
||||
|
||||
|
||||
# 生成两个数组间的全部组合
|
||||
def combine_2list(list1, list2):
|
||||
answers = []
|
||||
for i1 in list1:
|
||||
for i2 in list2:
|
||||
answers.append([i1, i2])
|
||||
return answers
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
|
||||
signers_list = os.listdir(image_dir)
|
||||
list_file_train = open(list_filename_train, 'w')
|
||||
list_file_test = open(list_filename_test, 'w')
|
||||
|
||||
for signer in signers_list:
|
||||
list_file = list_file_train if int(signer) <= 3500 else list_file_test
|
||||
genuine_genuine_suf = combine(list(range(1, num_genuine + 1)), 2)
|
||||
for item in genuine_genuine_suf:
|
||||
genuine0 = signer + '/c-' + signer + "-%02d" % (item[0]) + '.jpg'
|
||||
genuine1 = signer + '/c-' + signer + "-%02d" % (item[1]) + '.jpg'
|
||||
line = genuine0 + ' ' + genuine1 + ' 1\n'
|
||||
list_file.write(line)
|
||||
|
||||
genuine_forged_suf = combine_2list(list(range(1, num_genuine + 1)), list(range(1, num_forged + 1)))
|
||||
for item in genuine_forged_suf:
|
||||
genuine = signer + '/c-' + signer + "-%02d" % (item[0]) + '.jpg'
|
||||
forged = signer + '/cf-' + signer + "-%02d" % (item[1]) + '.jpg'
|
||||
line = genuine + ' ' + forged + ' 0\n'
|
||||
list_file.write(line)
|
||||
|
||||
"""随机伪造情况,每个writer 和其他writer组合"""
|
||||
random_forged_nums = 2880000
|
||||
# random_forged_val_nums = 2880000 * 0.15
|
||||
|
||||
writers = np.arange(1, 4001, 1)
|
||||
writers = np.split(writers, 2)
|
||||
writers_part1 = writers[0]
|
||||
writers_part2 = writers[1]
|
||||
genuine_forged_suf = combine_2list(writers_part1, writers_part2)
|
||||
np.random.shuffle(genuine_forged_suf)
|
||||
i = 0
|
||||
for item in genuine_forged_suf:
|
||||
if i > random_forged_nums:
|
||||
break
|
||||
i += 1
|
||||
list_file = list_file_train if i % 6 != 0 else list_file_test
|
||||
genuine = '%03d' % item[0] + '/c-' + '%03d' % item[0] + "-09" + '.jpg'
|
||||
forged = '%03d' % item[1] + '/c-' + '%03d' % item[1] + "-09" + '.jpg'
|
||||
line = genuine + ' ' + forged + ' 2\n'
|
||||
list_file.write(line)
|
||||
|
||||
list_file_train.close()
|
||||
list_file_test.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
27
dataset/params.json
Normal file
27
dataset/params.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"model": "Inception_2logits",
|
||||
"signature_train_list": "./experiments/data_list/firmas_pairs_c_train.txt",
|
||||
"signature_val_list": "./experiments/data_list/firmas_pairs_c_val.txt",
|
||||
"images_dir": "/home/deeplearning/work/Deeplearning/dataset/writingID/offline/firmas_binarized/",
|
||||
"is_augment": false,
|
||||
"learning_rate": 1e-5,
|
||||
"batch_size": 32,
|
||||
"num_epochs": 1,
|
||||
"use_batch_norm": true,
|
||||
"bn_momentum": 0.9,
|
||||
"margin": 5,
|
||||
"embedding_size": 64,
|
||||
"keep_prob": 0.4,
|
||||
"squared": false,
|
||||
"image_width": 220,
|
||||
"image_height": 155,
|
||||
"positive_size": 276,
|
||||
"negative_size": 720,
|
||||
"channels": 1,
|
||||
"num_parallel_calls": 4,
|
||||
"save_summary_steps": 100,
|
||||
"save_checkpoints_steps": 1000,
|
||||
"num_gpus": 3,
|
||||
"keep_checkpoint_max": 25,
|
||||
"eval_steps": 10
|
||||
}
|
||||
45
dataset/preprosess_images.py
Normal file
45
dataset/preprosess_images.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# encoding: utf-8
|
||||
|
||||
"""
|
||||
@author: lichuang
|
||||
@license: (C) Copyright 2010, CFCA
|
||||
@file: preprosess_images.py
|
||||
@time: 2018/5/8 18:
|
||||
@desc: regularize images, binaries, turn into black background
|
||||
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
import imageio
|
||||
import numpy as np
|
||||
|
||||
dir_to_process = '/home/deeplearning/work/Deeplearning/dataset/writingID/offline/firmas/'
|
||||
dir_processed = '/home/deeplearning/work/Deeplearning/dataset/writingID/offline/firmas_binarized/'
|
||||
|
||||
|
||||
def _normalize_images(images_dir, processed_dir, reverse):
|
||||
"""binaries, turn into black background """
|
||||
for root, dirs, files in os.walk(images_dir):
|
||||
for name in files:
|
||||
new_path = os.path.join(processed_dir, os.path.split(root)[-1])
|
||||
if not os.path.exists(new_path):
|
||||
os.mkdir(new_path)
|
||||
if name.lower().endswith('.jpg'):
|
||||
image = imageio.imread(os.path.join(root, name))
|
||||
image[np.where(image < 230)] = 0
|
||||
image[np.where(image >= 230)] = 255
|
||||
if reverse:
|
||||
image = 255 - image
|
||||
imageio.imwrite(os.path.join(new_path, name), image)
|
||||
print('all images processed!')
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
_normalize_images(dir_to_process, dir_processed, False)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
281
models.py
Normal file
281
models.py
Normal file
@@ -0,0 +1,281 @@
|
||||
# encoding: utf-8
|
||||
|
||||
"""
|
||||
@file: models.py
|
||||
@time: 2018/4/17 15:03
|
||||
@desc: 4 models: Siamese, SiameseInception, 2ChannelsCNN, 2ChannelsSoftmax
|
||||
|
||||
"""
|
||||
|
||||
import tensorflow as tf
|
||||
from tensorflow.contrib import layers
|
||||
from tensorflow.contrib.framework.python.ops import arg_scope
|
||||
from tensorflow.contrib.layers.python.layers import layers as layers_lib
|
||||
|
||||
import net.inception_v3 as inception_v3
|
||||
import utils
|
||||
|
||||
|
||||
def _embedding_alexnet(is_training, images, params):
|
||||
with tf.variable_scope('Siamese', 'CFCASiamese', [images], reuse=tf.AUTO_REUSE):
|
||||
with arg_scope(
|
||||
[layers.conv2d], activation_fn=tf.nn.relu):
|
||||
net = layers.conv2d(
|
||||
images, 96, [11, 11], 4, padding='VALID', scope='conv1')
|
||||
# net = layers.batch_norm(net, decay=0.9, epsilon=1e-06, is_training=is_training)
|
||||
net = layers_lib.max_pool2d(net, [3, 3], 2, scope='pool1')
|
||||
net = layers.conv2d(net, 256, [5, 5], scope='conv2')
|
||||
# net = layers.batch_norm(net, decay=0.9, epsilon=1e-06, is_training=is_training)
|
||||
net = layers_lib.max_pool2d(net, [3, 3], 2, scope='pool2')
|
||||
net = layers_lib.dropout(
|
||||
net, keep_prob=0.7, is_training=is_training)
|
||||
net = layers.conv2d(net, 384, [3, 3], scope='conv3')
|
||||
net = layers.conv2d(net, 256, [3, 3], scope='conv4')
|
||||
net = layers_lib.max_pool2d(net, [3, 3], 2, scope='pool5')
|
||||
net = layers_lib.dropout(
|
||||
net, keep_prob=0.7, is_training=is_training)
|
||||
net = layers_lib.flatten(net, scope='flatten1')
|
||||
net = layers_lib.fully_connected(net, 1024, scope='fc1',
|
||||
weights_regularizer=layers.l2_regularizer(0.0005))
|
||||
net = layers_lib.dropout(
|
||||
net, keep_prob=0.5, is_training=is_training)
|
||||
net = layers_lib.fully_connected(net, params.embedding_size, scope='fc2',
|
||||
weights_regularizer=layers.l2_regularizer(0.0005))
|
||||
return net
|
||||
|
||||
|
||||
def _embedding_inception(is_training, images, params):
|
||||
logits, endpoints = inception_v3.inception_v3(
|
||||
images, num_classes=params.embedding_size, is_training=is_training,
|
||||
dropout_keep_prob=params.keep_prob, reuse=tf.AUTO_REUSE, scope='InceptionV3')
|
||||
return logits
|
||||
|
||||
|
||||
def _embedding_2logits(is_training, embeddings, labels):
|
||||
"""embeddings to 2 logits and losss"""
|
||||
logits = layers_lib.fully_connected(
|
||||
embeddings, 2, scope='fc3', reuse=tf.AUTO_REUSE)
|
||||
logits_array = tf.split(logits, 2, 1)
|
||||
logits_diff = tf.subtract(logits_array[0], logits_array[1])
|
||||
|
||||
if labels is not None:
|
||||
loss = tf.reduce_mean(
|
||||
tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=tf.cast(labels, tf.int64)))
|
||||
return loss, logits_diff
|
||||
else:
|
||||
return None, logits_diff
|
||||
|
||||
|
||||
def _calculate_eucd2(embedding1, embedding2):
|
||||
eucd2 = tf.pow(tf.subtract(embedding1, embedding2), 2)
|
||||
eucd2 = tf.reduce_sum(eucd2, 1)
|
||||
eucd = tf.sqrt(eucd2 + 1e-6, name="eucd")
|
||||
return tf.reshape(eucd2, [-1, 1]), tf.reshape(eucd, [-1, 1])
|
||||
|
||||
|
||||
def _loss_siamese(images, labels, params, is_training, embedding_func):
|
||||
"""<SigNet: Convolutional Siamese Network for Writer
|
||||
Independent Offline Signature Verification>"""
|
||||
images = tf.split(images, 2, axis=3)
|
||||
images0 = tf.reshape(
|
||||
images[0], [-1, params.image_width, params.image_height, 1])
|
||||
images1 = tf.reshape(
|
||||
images[1], [-1, params.image_width, params.image_height, 1])
|
||||
|
||||
"""When using Siamese, The Complex network such as Inception will
|
||||
cause overfitting even in first epoch"""
|
||||
embeddings0 = embedding_func(is_training, images0, params)
|
||||
embeddings1 = embedding_func(is_training, images1, params)
|
||||
|
||||
eucd2, eucd = _calculate_eucd2(embeddings0, embeddings1)
|
||||
if labels is not None:
|
||||
labels_t = tf.reshape(labels, [-1, 1])
|
||||
labels_f = tf.reshape(tf.subtract(
|
||||
1.0, labels, name="1-yi"), [-1, 1]) # labels_ = !labels;
|
||||
|
||||
c = tf.constant(int(params.margin), dtype=tf.float32, name="C")
|
||||
pos = tf.multiply(labels_t, eucd2, name="yi_x_eucd2")
|
||||
neg = tf.multiply(labels_f, tf.pow(tf.maximum(
|
||||
tf.subtract(c, eucd), 0), 2), name="Nyi_x_C-eucd_xx_2")
|
||||
|
||||
losses = tf.add(pos, neg, name="losses")
|
||||
loss = tf.reduce_mean(losses, name="loss")
|
||||
return loss, eucd
|
||||
else:
|
||||
return None, eucd
|
||||
|
||||
|
||||
def _loss_siamese_alexnet(images, labels, params, is_training):
|
||||
return _loss_siamese(images, labels, params, is_training, _embedding_alexnet)
|
||||
|
||||
|
||||
def _loss_siamese_inception(images, labels, params, is_training):
|
||||
return _loss_siamese(images, labels, params, is_training, _embedding_inception)
|
||||
|
||||
|
||||
def _loss_inception_2logits(images, labels, params, is_training):
|
||||
images = tf.split(images, 2, axis=3)
|
||||
images0 = tf.reshape(
|
||||
images[0], [-1, params.image_width, params.image_height, 1])
|
||||
images1 = tf.reshape(
|
||||
images[1], [-1, params.image_width, params.image_height, 1])
|
||||
embeddings0 = _embedding_inception(is_training, images0, params)
|
||||
embeddings1 = _embedding_inception(is_training, images1, params)
|
||||
embeddings = tf.concat([embeddings0, embeddings1], axis=1)
|
||||
return _embedding_2logits(is_training, embeddings, labels)
|
||||
|
||||
|
||||
def _loss_2channels_softmax_alex(images, labels, params, is_training):
|
||||
# params.embedding_size = 2
|
||||
embeddings = _embedding_alexnet(is_training, images, params)
|
||||
logits = layers_lib.fully_connected(
|
||||
embeddings, 2, scope='fc3', reuse=tf.AUTO_REUSE)
|
||||
# logits = embeddings
|
||||
logits_array = tf.split(logits, 2, 1)
|
||||
logits_diff = tf.subtract(logits_array[0], logits_array[1])
|
||||
|
||||
if labels is not None:
|
||||
loss = tf.reduce_mean(
|
||||
tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=tf.cast(labels, tf.int64)))
|
||||
return loss, logits_diff
|
||||
else:
|
||||
return None, logits_diff
|
||||
|
||||
|
||||
def _loss_2channels_softmax(images, labels, params, is_training):
|
||||
logits, endpoints = inception_v3.inception_v3(
|
||||
images, num_classes=2, is_training=is_training,
|
||||
dropout_keep_prob=params.keep_prob, reuse=tf.AUTO_REUSE, scope='InceptionV3')
|
||||
logits_array = tf.split(logits, 2, 1)
|
||||
logits_diff = tf.subtract(logits_array[0], logits_array[1])
|
||||
|
||||
if labels is not None:
|
||||
loss = tf.reduce_mean(
|
||||
tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=tf.cast(labels, tf.int64)))
|
||||
return loss, logits_diff
|
||||
else:
|
||||
return None, logits_diff
|
||||
|
||||
|
||||
def _loss_2channels(images, labels, params, is_training):
|
||||
"""<Learning to Compare Image Patches via Convolutional Neural Networks>"""
|
||||
|
||||
logits, endpoints = inception_v3.inception_v3(
|
||||
images, num_classes=1, is_training=is_training,
|
||||
dropout_keep_prob=params.keep_prob, reuse=tf.AUTO_REUSE, scope='InceptionV3')
|
||||
|
||||
if labels is not None:
|
||||
""" convert y from {0,1} to {-1,1}"""
|
||||
labels = tf.multiply(labels, 2.0)
|
||||
labels = tf.subtract(labels, 1.0)
|
||||
labels = tf.reshape(labels, [-1, 1])
|
||||
loss = tf.maximum(0.0, tf.subtract(1.0, tf.multiply(labels, logits)))
|
||||
return tf.reduce_mean(loss), tf.subtract(1.0, logits)
|
||||
else:
|
||||
return None, tf.subtract(1.0, logits)
|
||||
|
||||
|
||||
def _normlize_distance(distance):
|
||||
"""normalization of distance"""
|
||||
max_val = tf.reduce_max(distance)
|
||||
min_val = tf.reduce_min(distance)
|
||||
distance_norm = tf.div(tf.subtract(distance, min_val),
|
||||
tf.subtract(max_val, min_val))
|
||||
return distance_norm
|
||||
|
||||
|
||||
models = {"Siamese": _loss_siamese_alexnet,
|
||||
"SiameseInception": _loss_siamese_inception,
|
||||
"Inception_2logits": _loss_inception_2logits,
|
||||
"2ChannelsAlexnet": _loss_2channels_softmax_alex,
|
||||
"2ChannelsCNN": _loss_2channels,
|
||||
"2ChannelsSoftmax": _loss_2channels_softmax}
|
||||
|
||||
|
||||
def model_fn_signature(features, labels, mode, params):
|
||||
"""Model function for tf.estimator
|
||||
|
||||
Args:
|
||||
features: input batch of images
|
||||
labels:True or not
|
||||
mode: can be one of tf.estimator.ModeKeys.{TRAIN, EVAL }
|
||||
params: contains hyper parameters of the model (ex: `params.learning_rate`)
|
||||
|
||||
Returns:
|
||||
model_spec: tf.estimator.EstimatorSpec object
|
||||
"""
|
||||
is_training = (mode == tf.estimator.ModeKeys.TRAIN)
|
||||
loss_function = models[params.model]
|
||||
|
||||
losses_all_tower = []
|
||||
distance_all_tower = []
|
||||
images_all_tower = tf.split(features, params.num_gpus, axis=0)
|
||||
labels_all_tower = None
|
||||
if labels is not None:
|
||||
labels = tf.reshape(labels, [-1])
|
||||
labels_all_tower = tf.split(labels, params.num_gpus, axis=0)
|
||||
|
||||
for i in range(params.num_gpus):
|
||||
worker_device = '/{}:{}'.format('gpu', i)
|
||||
images_tower = images_all_tower[i]
|
||||
|
||||
device_setter = utils.local_device_setter(
|
||||
ps_device_type='gpu',
|
||||
worker_device=worker_device,
|
||||
ps_strategy=tf.contrib.training.GreedyLoadBalancingStrategy(
|
||||
params.num_gpus, tf.contrib.training.byte_size_load_fn))
|
||||
with tf.device(device_setter):
|
||||
if labels_all_tower is not None:
|
||||
loss, distance = loss_function(
|
||||
images_tower, labels_all_tower[i], params, is_training)
|
||||
losses_all_tower.append(loss)
|
||||
else:
|
||||
_, distance = loss_function(
|
||||
images_tower, None, params, is_training)
|
||||
distance_all_tower.append(distance)
|
||||
|
||||
consolidation_device = '/cpu:0'
|
||||
with tf.device(consolidation_device):
|
||||
distance = tf.concat(distance_all_tower, 0)
|
||||
if mode == tf.estimator.ModeKeys.PREDICT:
|
||||
predictions = {'distance': distance}
|
||||
return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)
|
||||
|
||||
loss = tf.reduce_mean(losses_all_tower, name='loss_mean')
|
||||
labels = tf.reshape(labels, [-1, 1])
|
||||
labels_reversal = tf.reshape(tf.subtract(
|
||||
1.0, labels), [-1, 1]) # labels_ = !labels;
|
||||
positive_distance = tf.reduce_mean(tf.multiply(labels, distance))
|
||||
negative_distance = tf.reduce_mean(
|
||||
tf.multiply(labels_reversal, distance))
|
||||
tf.summary.scalar('loss', loss)
|
||||
tf.summary.scalar('positive_distance', positive_distance)
|
||||
tf.summary.scalar('negative_distance', negative_distance)
|
||||
|
||||
distance_norm = _normlize_distance(distance)
|
||||
metric_ops = tf.metrics.auc(labels_reversal, distance_norm)
|
||||
tf.summary.scalar('auc', metric_ops[1])
|
||||
|
||||
if mode == tf.estimator.ModeKeys.EVAL:
|
||||
sec_at_spe_metric = tf.metrics.sensitivity_at_specificity(
|
||||
labels_reversal, distance_norm, 0.90)
|
||||
eval_metric_ops = {'evaluation_auc': metric_ops,
|
||||
'sec_at_spe': sec_at_spe_metric}
|
||||
|
||||
return tf.estimator.EstimatorSpec(mode, loss=loss, eval_metric_ops=eval_metric_ops)
|
||||
|
||||
else:
|
||||
|
||||
logging_hook = tf.train.LoggingTensorHook({"positive_distance": positive_distance,
|
||||
"negative_distance": negative_distance,
|
||||
"auc": metric_ops[1]}, every_n_iter=100)
|
||||
|
||||
# optimizer = tf.train.RMSPropOptimizer(params.learning_rate)
|
||||
optimizer = tf.train.AdamOptimizer(params.learning_rate)
|
||||
global_step = tf.train.get_global_step()
|
||||
update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
|
||||
with tf.control_dependencies(update_ops):
|
||||
train_op = optimizer.minimize(
|
||||
loss, global_step=global_step, colocate_gradients_with_ops=True)
|
||||
|
||||
return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op, training_hooks=[logging_hook])
|
||||
736
net/inception_v3.py
Normal file
736
net/inception_v3.py
Normal file
@@ -0,0 +1,736 @@
|
||||
# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ==============================================================================
|
||||
"""Contains the definition for inception v3 classification network."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
from tensorflow.contrib import layers
|
||||
from tensorflow.contrib.framework.python.ops import arg_scope
|
||||
from tensorflow.contrib.layers.python.layers import initializers
|
||||
from tensorflow.contrib.layers.python.layers import layers as layers_lib
|
||||
from tensorflow.contrib.layers.python.layers import regularizers
|
||||
from tensorflow.python.framework import ops
|
||||
from tensorflow.python.ops import array_ops
|
||||
from tensorflow.python.ops import init_ops
|
||||
from tensorflow.python.ops import nn_ops
|
||||
from tensorflow.python.ops import variable_scope
|
||||
|
||||
trunc_normal = lambda stddev: init_ops.truncated_normal_initializer(0.0, stddev)
|
||||
|
||||
|
||||
def inception_v3_base(inputs,
|
||||
final_endpoint='Mixed_7a',
|
||||
min_depth=16,
|
||||
depth_multiplier=1.0,
|
||||
scope=None,
|
||||
is_training=False):
|
||||
"""Inception model from http://arxiv.org/abs/1512.00567.
|
||||
|
||||
Constructs an Inception v3 network from inputs to the given final endpoint.
|
||||
This method can construct the network up to the final inception block
|
||||
Mixed_7c.
|
||||
|
||||
Note that the names of the layers in the paper do not correspond to the names
|
||||
of the endpoints registered by this function although they build the same
|
||||
network.
|
||||
|
||||
Here is a mapping from the old_names to the new names:
|
||||
Old name | New name
|
||||
=======================================
|
||||
conv0 | Conv2d_1a_3x3
|
||||
conv1 | Conv2d_2a_3x3
|
||||
conv2 | Conv2d_2b_3x3
|
||||
pool1 | MaxPool_3a_3x3
|
||||
conv3 | Conv2d_3b_1x1
|
||||
conv4 | Conv2d_4a_3x3
|
||||
pool2 | MaxPool_5a_3x3
|
||||
mixed_35x35x256a | Mixed_5b
|
||||
mixed_35x35x288a | Mixed_5c
|
||||
mixed_35x35x288b | Mixed_5d
|
||||
mixed_17x17x768a | Mixed_6a
|
||||
mixed_17x17x768b | Mixed_6b
|
||||
mixed_17x17x768c | Mixed_6c
|
||||
mixed_17x17x768d | Mixed_6d
|
||||
mixed_17x17x768e | Mixed_6e
|
||||
mixed_8x8x1280a | Mixed_7a
|
||||
mixed_8x8x2048a | Mixed_7b
|
||||
mixed_8x8x2048b | Mixed_7c
|
||||
|
||||
Args:
|
||||
inputs: a tensor of size [batch_size, height, width, channels].
|
||||
final_endpoint: specifies the endpoint to construct the network up to. It
|
||||
can be one of ['Conv2d_1a_3x3', 'Conv2d_2a_3x3', 'Conv2d_2b_3x3',
|
||||
'MaxPool_3a_3x3', 'Conv2d_3b_1x1', 'Conv2d_4a_3x3', 'MaxPool_5a_3x3',
|
||||
'Mixed_5b', 'Mixed_5c', 'Mixed_5d', 'Mixed_6a', 'Mixed_6b', 'Mixed_6c',
|
||||
'Mixed_6d', 'Mixed_6e', 'Mixed_7a', 'Mixed_7b', 'Mixed_7c'].
|
||||
min_depth: Minimum depth value (number of channels) for all convolution ops.
|
||||
Enforced when depth_multiplier < 1, and not an active constraint when
|
||||
depth_multiplier >= 1.
|
||||
depth_multiplier: Float multiplier for the depth (number of channels)
|
||||
for all convolution ops. The value must be greater than zero. Typical
|
||||
usage will be to set this value in (0, 1) to reduce the number of
|
||||
parameters or computation cost of the model.
|
||||
scope: Optional variable_scope.
|
||||
|
||||
Returns:
|
||||
tensor_out: output tensor corresponding to the final_endpoint.
|
||||
end_points: a set of activations for external use, for example summaries or
|
||||
losses.
|
||||
|
||||
Raises:
|
||||
ValueError: if final_endpoint is not set to one of the predefined values,
|
||||
or depth_multiplier <= 0
|
||||
:param is_training:
|
||||
"""
|
||||
# end_points will collect relevant activations for external use, for example
|
||||
# summaries or losses.
|
||||
end_points = {}
|
||||
|
||||
if depth_multiplier <= 0:
|
||||
raise ValueError('depth_multiplier is not greater than zero.')
|
||||
depth = lambda d: max(int(d * depth_multiplier), min_depth)
|
||||
|
||||
with variable_scope.variable_scope(scope, 'InceptionV3', [inputs]):
|
||||
with arg_scope(
|
||||
[layers.conv2d, layers_lib.max_pool2d, layers_lib.avg_pool2d],
|
||||
stride=1,
|
||||
padding='VALID'):
|
||||
# 299 x 299 x 3
|
||||
end_point = 'Conv2d_1a_3x3'
|
||||
net = layers.conv2d(inputs, depth(32), [3, 3], stride=2, scope=end_point)
|
||||
end_points[end_point] = net
|
||||
if end_point == final_endpoint:
|
||||
return net, end_points
|
||||
# 149 x 149 x 32
|
||||
end_point = 'Conv2d_2a_3x3'
|
||||
net = layers.conv2d(net, depth(32), [3, 3], scope=end_point)
|
||||
end_points[end_point] = net
|
||||
if end_point == final_endpoint:
|
||||
return net, end_points
|
||||
# 147 x 147 x 32
|
||||
end_point = 'Conv2d_2b_3x3'
|
||||
net = layers.conv2d(
|
||||
net, depth(64), [3, 3], padding='SAME', scope=end_point)
|
||||
end_points[end_point] = net
|
||||
if end_point == final_endpoint:
|
||||
return net, end_points
|
||||
# 147 x 147 x 64
|
||||
end_point = 'MaxPool_3a_3x3'
|
||||
net = layers_lib.max_pool2d(net, [3, 3], stride=2, scope=end_point)
|
||||
end_points[end_point] = net
|
||||
if end_point == final_endpoint:
|
||||
return net, end_points
|
||||
# 73 x 73 x 64
|
||||
end_point = 'Conv2d_3b_1x1'
|
||||
net = layers.conv2d(net, depth(80), [1, 1], scope=end_point)
|
||||
net = layers_lib.dropout(net, keep_prob=0.9, is_training=is_training)
|
||||
end_points[end_point] = net
|
||||
if end_point == final_endpoint:
|
||||
return net, end_points
|
||||
# 73 x 73 x 80.
|
||||
end_point = 'Conv2d_4a_3x3'
|
||||
net = layers.conv2d(net, depth(192), [3, 3], scope=end_point)
|
||||
end_points[end_point] = net
|
||||
if end_point == final_endpoint:
|
||||
return net, end_points
|
||||
# 71 x 71 x 192.
|
||||
end_point = 'MaxPool_5a_3x3'
|
||||
net = layers_lib.max_pool2d(net, [3, 3], stride=2, scope=end_point)
|
||||
end_points[end_point] = net
|
||||
if end_point == final_endpoint:
|
||||
return net, end_points
|
||||
# 35 x 35 x 192.
|
||||
|
||||
# Inception blocks
|
||||
with arg_scope(
|
||||
[layers.conv2d, layers_lib.max_pool2d, layers_lib.avg_pool2d],
|
||||
stride=1,
|
||||
padding='SAME'):
|
||||
# mixed: 35 x 35 x 256.
|
||||
end_point = 'Mixed_5b'
|
||||
with variable_scope.variable_scope(end_point):
|
||||
with variable_scope.variable_scope('Branch_0'):
|
||||
branch_0 = layers.conv2d(
|
||||
net, depth(64), [1, 1], scope='Conv2d_0a_1x1')
|
||||
with variable_scope.variable_scope('Branch_1'):
|
||||
branch_1 = layers.conv2d(
|
||||
net, depth(48), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_1 = layers.conv2d(
|
||||
branch_1, depth(64), [5, 5], scope='Conv2d_0b_5x5')
|
||||
with variable_scope.variable_scope('Branch_2'):
|
||||
branch_2 = layers.conv2d(
|
||||
net, depth(64), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(96), [3, 3], scope='Conv2d_0b_3x3')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(96), [3, 3], scope='Conv2d_0c_3x3')
|
||||
with variable_scope.variable_scope('Branch_3'):
|
||||
branch_3 = layers_lib.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
|
||||
branch_3 = layers.conv2d(
|
||||
branch_3, depth(32), [1, 1], scope='Conv2d_0b_1x1')
|
||||
net = array_ops.concat([branch_0, branch_1, branch_2, branch_3], 3)
|
||||
end_points[end_point] = net
|
||||
if end_point == final_endpoint:
|
||||
return net, end_points
|
||||
|
||||
# mixed_1: 35 x 35 x 288.
|
||||
end_point = 'Mixed_5c'
|
||||
with variable_scope.variable_scope(end_point):
|
||||
with variable_scope.variable_scope('Branch_0'):
|
||||
branch_0 = layers.conv2d(
|
||||
net, depth(64), [1, 1], scope='Conv2d_0a_1x1')
|
||||
with variable_scope.variable_scope('Branch_1'):
|
||||
branch_1 = layers.conv2d(
|
||||
net, depth(48), [1, 1], scope='Conv2d_0b_1x1')
|
||||
branch_1 = layers.conv2d(
|
||||
branch_1, depth(64), [5, 5], scope='Conv_1_0c_5x5')
|
||||
with variable_scope.variable_scope('Branch_2'):
|
||||
branch_2 = layers.conv2d(
|
||||
net, depth(64), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(96), [3, 3], scope='Conv2d_0b_3x3')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(96), [3, 3], scope='Conv2d_0c_3x3')
|
||||
with variable_scope.variable_scope('Branch_3'):
|
||||
branch_3 = layers_lib.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
|
||||
branch_3 = layers.conv2d(
|
||||
branch_3, depth(64), [1, 1], scope='Conv2d_0b_1x1')
|
||||
net = array_ops.concat([branch_0, branch_1, branch_2, branch_3], 3)
|
||||
end_points[end_point] = net
|
||||
if end_point == final_endpoint:
|
||||
return net, end_points
|
||||
|
||||
# mixed_2: 35 x 35 x 288.
|
||||
end_point = 'Mixed_5d'
|
||||
with variable_scope.variable_scope(end_point):
|
||||
with variable_scope.variable_scope('Branch_0'):
|
||||
branch_0 = layers.conv2d(
|
||||
net, depth(64), [1, 1], scope='Conv2d_0a_1x1')
|
||||
with variable_scope.variable_scope('Branch_1'):
|
||||
branch_1 = layers.conv2d(
|
||||
net, depth(48), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_1 = layers.conv2d(
|
||||
branch_1, depth(64), [5, 5], scope='Conv2d_0b_5x5')
|
||||
with variable_scope.variable_scope('Branch_2'):
|
||||
branch_2 = layers.conv2d(
|
||||
net, depth(64), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(96), [3, 3], scope='Conv2d_0b_3x3')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(96), [3, 3], scope='Conv2d_0c_3x3')
|
||||
with variable_scope.variable_scope('Branch_3'):
|
||||
branch_3 = layers_lib.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
|
||||
branch_3 = layers.conv2d(
|
||||
branch_3, depth(64), [1, 1], scope='Conv2d_0b_1x1')
|
||||
net = array_ops.concat([branch_0, branch_1, branch_2, branch_3], 3)
|
||||
end_points[end_point] = net
|
||||
if end_point == final_endpoint:
|
||||
return net, end_points
|
||||
|
||||
net = layers_lib.dropout(net, keep_prob=0.8, is_training=is_training)
|
||||
# mixed_3: 17 x 17 x 768.
|
||||
end_point = 'Mixed_6a'
|
||||
with variable_scope.variable_scope(end_point):
|
||||
with variable_scope.variable_scope('Branch_0'):
|
||||
branch_0 = layers.conv2d(
|
||||
net,
|
||||
depth(384), [3, 3],
|
||||
stride=2,
|
||||
padding='VALID',
|
||||
scope='Conv2d_1a_1x1')
|
||||
with variable_scope.variable_scope('Branch_1'):
|
||||
branch_1 = layers.conv2d(
|
||||
net, depth(64), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_1 = layers.conv2d(
|
||||
branch_1, depth(96), [3, 3], scope='Conv2d_0b_3x3')
|
||||
branch_1 = layers.conv2d(
|
||||
branch_1,
|
||||
depth(96), [3, 3],
|
||||
stride=2,
|
||||
padding='VALID',
|
||||
scope='Conv2d_1a_1x1')
|
||||
with variable_scope.variable_scope('Branch_2'):
|
||||
branch_2 = layers_lib.max_pool2d(
|
||||
net, [3, 3], stride=2, padding='VALID', scope='MaxPool_1a_3x3')
|
||||
net = array_ops.concat([branch_0, branch_1, branch_2], 3)
|
||||
end_points[end_point] = net
|
||||
if end_point == final_endpoint:
|
||||
return net, end_points
|
||||
|
||||
# mixed4: 17 x 17 x 768.
|
||||
end_point = 'Mixed_6b'
|
||||
with variable_scope.variable_scope(end_point):
|
||||
with variable_scope.variable_scope('Branch_0'):
|
||||
branch_0 = layers.conv2d(
|
||||
net, depth(192), [1, 1], scope='Conv2d_0a_1x1')
|
||||
with variable_scope.variable_scope('Branch_1'):
|
||||
branch_1 = layers.conv2d(
|
||||
net, depth(128), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_1 = layers.conv2d(
|
||||
branch_1, depth(128), [1, 7], scope='Conv2d_0b_1x7')
|
||||
branch_1 = layers.conv2d(
|
||||
branch_1, depth(192), [7, 1], scope='Conv2d_0c_7x1')
|
||||
with variable_scope.variable_scope('Branch_2'):
|
||||
branch_2 = layers.conv2d(
|
||||
net, depth(128), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(128), [7, 1], scope='Conv2d_0b_7x1')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(128), [1, 7], scope='Conv2d_0c_1x7')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(128), [7, 1], scope='Conv2d_0d_7x1')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(192), [1, 7], scope='Conv2d_0e_1x7')
|
||||
with variable_scope.variable_scope('Branch_3'):
|
||||
branch_3 = layers_lib.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
|
||||
branch_3 = layers.conv2d(
|
||||
branch_3, depth(192), [1, 1], scope='Conv2d_0b_1x1')
|
||||
net = array_ops.concat([branch_0, branch_1, branch_2, branch_3], 3)
|
||||
end_points[end_point] = net
|
||||
if end_point == final_endpoint:
|
||||
return net, end_points
|
||||
|
||||
# mixed_5: 17 x 17 x 768.
|
||||
end_point = 'Mixed_6c'
|
||||
with variable_scope.variable_scope(end_point):
|
||||
with variable_scope.variable_scope('Branch_0'):
|
||||
branch_0 = layers.conv2d(
|
||||
net, depth(192), [1, 1], scope='Conv2d_0a_1x1')
|
||||
with variable_scope.variable_scope('Branch_1'):
|
||||
branch_1 = layers.conv2d(
|
||||
net, depth(160), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_1 = layers.conv2d(
|
||||
branch_1, depth(160), [1, 7], scope='Conv2d_0b_1x7')
|
||||
branch_1 = layers.conv2d(
|
||||
branch_1, depth(192), [7, 1], scope='Conv2d_0c_7x1')
|
||||
with variable_scope.variable_scope('Branch_2'):
|
||||
branch_2 = layers.conv2d(
|
||||
net, depth(160), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(160), [7, 1], scope='Conv2d_0b_7x1')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(160), [1, 7], scope='Conv2d_0c_1x7')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(160), [7, 1], scope='Conv2d_0d_7x1')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(192), [1, 7], scope='Conv2d_0e_1x7')
|
||||
with variable_scope.variable_scope('Branch_3'):
|
||||
branch_3 = layers_lib.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
|
||||
branch_3 = layers.conv2d(
|
||||
branch_3, depth(192), [1, 1], scope='Conv2d_0b_1x1')
|
||||
net = array_ops.concat([branch_0, branch_1, branch_2, branch_3], 3)
|
||||
end_points[end_point] = net
|
||||
if end_point == final_endpoint:
|
||||
return net, end_points
|
||||
# mixed_6: 17 x 17 x 768.
|
||||
end_point = 'Mixed_6d'
|
||||
with variable_scope.variable_scope(end_point):
|
||||
with variable_scope.variable_scope('Branch_0'):
|
||||
branch_0 = layers.conv2d(
|
||||
net, depth(192), [1, 1], scope='Conv2d_0a_1x1')
|
||||
with variable_scope.variable_scope('Branch_1'):
|
||||
branch_1 = layers.conv2d(
|
||||
net, depth(160), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_1 = layers.conv2d(
|
||||
branch_1, depth(160), [1, 7], scope='Conv2d_0b_1x7')
|
||||
branch_1 = layers.conv2d(
|
||||
branch_1, depth(192), [7, 1], scope='Conv2d_0c_7x1')
|
||||
with variable_scope.variable_scope('Branch_2'):
|
||||
branch_2 = layers.conv2d(
|
||||
net, depth(160), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(160), [7, 1], scope='Conv2d_0b_7x1')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(160), [1, 7], scope='Conv2d_0c_1x7')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(160), [7, 1], scope='Conv2d_0d_7x1')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(192), [1, 7], scope='Conv2d_0e_1x7')
|
||||
with variable_scope.variable_scope('Branch_3'):
|
||||
branch_3 = layers_lib.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
|
||||
branch_3 = layers.conv2d(
|
||||
branch_3, depth(192), [1, 1], scope='Conv2d_0b_1x1')
|
||||
net = array_ops.concat([branch_0, branch_1, branch_2, branch_3], 3)
|
||||
end_points[end_point] = net
|
||||
if end_point == final_endpoint:
|
||||
return net, end_points
|
||||
|
||||
# mixed_7: 17 x 17 x 768.
|
||||
end_point = 'Mixed_6e'
|
||||
with variable_scope.variable_scope(end_point):
|
||||
with variable_scope.variable_scope('Branch_0'):
|
||||
branch_0 = layers.conv2d(
|
||||
net, depth(192), [1, 1], scope='Conv2d_0a_1x1')
|
||||
with variable_scope.variable_scope('Branch_1'):
|
||||
branch_1 = layers.conv2d(
|
||||
net, depth(192), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_1 = layers.conv2d(
|
||||
branch_1, depth(192), [1, 7], scope='Conv2d_0b_1x7')
|
||||
branch_1 = layers.conv2d(
|
||||
branch_1, depth(192), [7, 1], scope='Conv2d_0c_7x1')
|
||||
with variable_scope.variable_scope('Branch_2'):
|
||||
branch_2 = layers.conv2d(
|
||||
net, depth(192), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(192), [7, 1], scope='Conv2d_0b_7x1')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(192), [1, 7], scope='Conv2d_0c_1x7')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(192), [7, 1], scope='Conv2d_0d_7x1')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(192), [1, 7], scope='Conv2d_0e_1x7')
|
||||
with variable_scope.variable_scope('Branch_3'):
|
||||
branch_3 = layers_lib.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
|
||||
branch_3 = layers.conv2d(
|
||||
branch_3, depth(192), [1, 1], scope='Conv2d_0b_1x1')
|
||||
net = array_ops.concat([branch_0, branch_1, branch_2, branch_3], 3)
|
||||
end_points[end_point] = net
|
||||
if end_point == final_endpoint:
|
||||
return net, end_points
|
||||
|
||||
net = layers_lib.dropout(net, keep_prob=0.8, is_training=is_training)
|
||||
# mixed_8: 8 x 8 x 1280.
|
||||
end_point = 'Mixed_7a'
|
||||
with variable_scope.variable_scope(end_point):
|
||||
with variable_scope.variable_scope('Branch_0'):
|
||||
branch_0 = layers.conv2d(
|
||||
net, depth(192), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_0 = layers.conv2d(
|
||||
branch_0,
|
||||
depth(320), [3, 3],
|
||||
stride=2,
|
||||
padding='VALID',
|
||||
scope='Conv2d_1a_3x3')
|
||||
with variable_scope.variable_scope('Branch_1'):
|
||||
branch_1 = layers.conv2d(
|
||||
net, depth(192), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_1 = layers.conv2d(
|
||||
branch_1, depth(192), [1, 7], scope='Conv2d_0b_1x7')
|
||||
branch_1 = layers.conv2d(
|
||||
branch_1, depth(192), [7, 1], scope='Conv2d_0c_7x1')
|
||||
branch_1 = layers.conv2d(
|
||||
branch_1,
|
||||
depth(192), [3, 3],
|
||||
stride=2,
|
||||
padding='VALID',
|
||||
scope='Conv2d_1a_3x3')
|
||||
with variable_scope.variable_scope('Branch_2'):
|
||||
branch_2 = layers_lib.max_pool2d(
|
||||
net, [3, 3], stride=2, padding='VALID', scope='MaxPool_1a_3x3')
|
||||
net = array_ops.concat([branch_0, branch_1, branch_2], 3)
|
||||
end_points[end_point] = net
|
||||
if end_point == final_endpoint:
|
||||
return net, end_points
|
||||
# mixed_9: 8 x 8 x 2048.
|
||||
end_point = 'Mixed_7b'
|
||||
with variable_scope.variable_scope(end_point):
|
||||
with variable_scope.variable_scope('Branch_0'):
|
||||
branch_0 = layers.conv2d(
|
||||
net, depth(320), [1, 1], scope='Conv2d_0a_1x1')
|
||||
with variable_scope.variable_scope('Branch_1'):
|
||||
branch_1 = layers.conv2d(
|
||||
net, depth(384), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_1 = array_ops.concat(
|
||||
[
|
||||
layers.conv2d(
|
||||
branch_1, depth(384), [1, 3], scope='Conv2d_0b_1x3'),
|
||||
layers.conv2d(
|
||||
branch_1, depth(384), [3, 1], scope='Conv2d_0b_3x1')
|
||||
],
|
||||
3)
|
||||
with variable_scope.variable_scope('Branch_2'):
|
||||
branch_2 = layers.conv2d(
|
||||
net, depth(448), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(384), [3, 3], scope='Conv2d_0b_3x3')
|
||||
branch_2 = array_ops.concat(
|
||||
[
|
||||
layers.conv2d(
|
||||
branch_2, depth(384), [1, 3], scope='Conv2d_0c_1x3'),
|
||||
layers.conv2d(
|
||||
branch_2, depth(384), [3, 1], scope='Conv2d_0d_3x1')
|
||||
],
|
||||
3)
|
||||
with variable_scope.variable_scope('Branch_3'):
|
||||
branch_3 = layers_lib.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
|
||||
branch_3 = layers.conv2d(
|
||||
branch_3, depth(192), [1, 1], scope='Conv2d_0b_1x1')
|
||||
net = array_ops.concat([branch_0, branch_1, branch_2, branch_3], 3)
|
||||
end_points[end_point] = net
|
||||
if end_point == final_endpoint:
|
||||
return net, end_points
|
||||
|
||||
# mixed_10: 8 x 8 x 2048.
|
||||
end_point = 'Mixed_7c'
|
||||
with variable_scope.variable_scope(end_point):
|
||||
with variable_scope.variable_scope('Branch_0'):
|
||||
branch_0 = layers.conv2d(
|
||||
net, depth(320), [1, 1], scope='Conv2d_0a_1x1')
|
||||
with variable_scope.variable_scope('Branch_1'):
|
||||
branch_1 = layers.conv2d(
|
||||
net, depth(384), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_1 = array_ops.concat(
|
||||
[
|
||||
layers.conv2d(
|
||||
branch_1, depth(384), [1, 3], scope='Conv2d_0b_1x3'),
|
||||
layers.conv2d(
|
||||
branch_1, depth(384), [3, 1], scope='Conv2d_0c_3x1')
|
||||
],
|
||||
3)
|
||||
with variable_scope.variable_scope('Branch_2'):
|
||||
branch_2 = layers.conv2d(
|
||||
net, depth(448), [1, 1], scope='Conv2d_0a_1x1')
|
||||
branch_2 = layers.conv2d(
|
||||
branch_2, depth(384), [3, 3], scope='Conv2d_0b_3x3')
|
||||
branch_2 = array_ops.concat(
|
||||
[
|
||||
layers.conv2d(
|
||||
branch_2, depth(384), [1, 3], scope='Conv2d_0c_1x3'),
|
||||
layers.conv2d(
|
||||
branch_2, depth(384), [3, 1], scope='Conv2d_0d_3x1')
|
||||
],
|
||||
3)
|
||||
with variable_scope.variable_scope('Branch_3'):
|
||||
branch_3 = layers_lib.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
|
||||
branch_3 = layers.conv2d(
|
||||
branch_3, depth(192), [1, 1], scope='Conv2d_0b_1x1')
|
||||
net = array_ops.concat([branch_0, branch_1, branch_2, branch_3], 3)
|
||||
end_points[end_point] = net
|
||||
if end_point == final_endpoint:
|
||||
return net, end_points
|
||||
raise ValueError('Unknown final endpoint %s' % final_endpoint)
|
||||
|
||||
|
||||
def inception_v3(inputs,
|
||||
num_classes=1000,
|
||||
is_training=True,
|
||||
dropout_keep_prob=0.8,
|
||||
min_depth=16,
|
||||
depth_multiplier=1.0,
|
||||
prediction_fn=layers_lib.softmax,
|
||||
spatial_squeeze=True,
|
||||
reuse=None,
|
||||
scope='InceptionV3'):
|
||||
"""Inception model from http://arxiv.org/abs/1512.00567.
|
||||
|
||||
"Rethinking the Inception Architecture for Computer Vision"
|
||||
|
||||
Christian Szegedy, Vincent Vanhoucke, Sergey Ioffe, Jonathon Shlens,
|
||||
Zbigniew Wojna.
|
||||
|
||||
With the default arguments this method constructs the exact model defined in
|
||||
the paper. However, one can experiment with variations of the inception_v3
|
||||
network by changing arguments dropout_keep_prob, min_depth and
|
||||
depth_multiplier.
|
||||
|
||||
The default image size used to train this network is 299x299.
|
||||
|
||||
Args:
|
||||
inputs: a tensor of size [batch_size, height, width, channels].
|
||||
num_classes: number of predicted classes.
|
||||
is_training: whether is training or not.
|
||||
dropout_keep_prob: the percentage of activation values that are retained.
|
||||
min_depth: Minimum depth value (number of channels) for all convolution ops.
|
||||
Enforced when depth_multiplier < 1, and not an active constraint when
|
||||
depth_multiplier >= 1.
|
||||
depth_multiplier: Float multiplier for the depth (number of channels)
|
||||
for all convolution ops. The value must be greater than zero. Typical
|
||||
usage will be to set this value in (0, 1) to reduce the number of
|
||||
parameters or computation cost of the model.
|
||||
prediction_fn: a function to get predictions out of logits.
|
||||
spatial_squeeze: if True, logits is of shape is [B, C], if false logits is
|
||||
of shape [B, 1, 1, C], where B is batch_size and C is number of classes.
|
||||
To use this parameter, the input images must be smaller
|
||||
than 300x300 pixels, in which case the output logit layer
|
||||
does not contain spatial information and can be removed.
|
||||
reuse: whether or not the network and its variables should be reused. To be
|
||||
able to reuse 'scope' must be given.
|
||||
scope: Optional variable_scope.
|
||||
|
||||
Returns:
|
||||
logits: the pre-softmax activations, a tensor of size
|
||||
[batch_size, num_classes]
|
||||
end_points: a dictionary from components of the network to the corresponding
|
||||
activation.
|
||||
|
||||
Raises:
|
||||
ValueError: if 'depth_multiplier' is less than or equal to zero.
|
||||
"""
|
||||
if depth_multiplier <= 0:
|
||||
raise ValueError('depth_multiplier is not greater than zero.')
|
||||
depth = lambda d: max(int(d * depth_multiplier), min_depth)
|
||||
|
||||
with variable_scope.variable_scope(
|
||||
scope, 'InceptionV3', [inputs, num_classes], reuse=reuse) as scope:
|
||||
with arg_scope(
|
||||
[layers_lib.batch_norm, layers_lib.dropout], is_training=is_training):
|
||||
net, end_points = inception_v3_base(
|
||||
inputs,
|
||||
scope=scope,
|
||||
is_training=is_training,
|
||||
min_depth=min_depth,
|
||||
depth_multiplier=depth_multiplier)
|
||||
|
||||
# Auxiliary Head logits
|
||||
with arg_scope(
|
||||
[layers.conv2d, layers_lib.max_pool2d, layers_lib.avg_pool2d],
|
||||
stride=1,
|
||||
padding='SAME'):
|
||||
aux_logits = end_points['Mixed_6e']
|
||||
with variable_scope.variable_scope('AuxLogits'):
|
||||
aux_logits = layers_lib.avg_pool2d(
|
||||
aux_logits, [5, 5],
|
||||
stride=3,
|
||||
padding='VALID',
|
||||
scope='AvgPool_1a_5x5')
|
||||
aux_logits = layers.conv2d(
|
||||
aux_logits, depth(128), [1, 1], scope='Conv2d_1b_1x1')
|
||||
|
||||
# Shape of feature map before the final layer.
|
||||
kernel_size = _reduced_kernel_size_for_small_input(aux_logits, [5, 5])
|
||||
aux_logits = layers.conv2d(
|
||||
aux_logits,
|
||||
depth(768),
|
||||
kernel_size,
|
||||
weights_initializer=trunc_normal(0.01),
|
||||
padding='VALID',
|
||||
scope='Conv2d_2a_{}x{}'.format(*kernel_size))
|
||||
aux_logits = layers.conv2d(
|
||||
aux_logits,
|
||||
num_classes, [1, 1],
|
||||
activation_fn=None,
|
||||
normalizer_fn=None,
|
||||
weights_initializer=trunc_normal(0.001),
|
||||
scope='Conv2d_2b_1x1')
|
||||
if spatial_squeeze:
|
||||
aux_logits = array_ops.squeeze(
|
||||
aux_logits, [1, 2], name='SpatialSqueeze')
|
||||
end_points['AuxLogits'] = aux_logits
|
||||
|
||||
# Final pooling and prediction
|
||||
with variable_scope.variable_scope('Logits'):
|
||||
kernel_size = _reduced_kernel_size_for_small_input(net, [8, 8])
|
||||
net = layers_lib.avg_pool2d(
|
||||
net,
|
||||
kernel_size,
|
||||
padding='VALID',
|
||||
scope='AvgPool_1a_{}x{}'.format(*kernel_size))
|
||||
# 1 x 1 x 2048
|
||||
net = layers_lib.dropout(
|
||||
net, keep_prob=dropout_keep_prob, scope='Dropout_1b')
|
||||
end_points['PreLogits'] = net
|
||||
# 2048
|
||||
logits = layers.conv2d(
|
||||
net,
|
||||
num_classes, [1, 1],
|
||||
activation_fn=None,
|
||||
normalizer_fn=None,
|
||||
scope='Conv2d_1c_1x1')
|
||||
if spatial_squeeze:
|
||||
logits = array_ops.squeeze(logits, [1, 2], name='SpatialSqueeze')
|
||||
# 1000
|
||||
end_points['Logits'] = logits
|
||||
end_points['Predictions'] = prediction_fn(logits, scope='Predictions')
|
||||
return logits, end_points
|
||||
|
||||
|
||||
inception_v3.default_image_size = 299
|
||||
|
||||
|
||||
def _reduced_kernel_size_for_small_input(input_tensor, kernel_size):
|
||||
"""Define kernel size which is automatically reduced for small input.
|
||||
|
||||
If the shape of the input images is unknown at graph construction time this
|
||||
function assumes that the input images are is large enough.
|
||||
|
||||
Args:
|
||||
input_tensor: input tensor of size [batch_size, height, width, channels].
|
||||
kernel_size: desired kernel size of length 2: [kernel_height, kernel_width]
|
||||
|
||||
Returns:
|
||||
a tensor with the kernel size.
|
||||
|
||||
TODO(jrru): Make this function work with unknown shapes. Theoretically, this
|
||||
can be done with the code below. Problems are two-fold: (1) If the shape was
|
||||
known, it will be lost. (2) inception.tf.contrib.slim.ops._two_element_tuple
|
||||
cannot
|
||||
handle tensors that define the kernel size.
|
||||
shape = tf.shape(input_tensor)
|
||||
return = tf.stack([tf.minimum(shape[1], kernel_size[0]),
|
||||
tf.minimum(shape[2], kernel_size[1])])
|
||||
|
||||
"""
|
||||
shape = input_tensor.get_shape().as_list()
|
||||
if shape[1] is None or shape[2] is None:
|
||||
kernel_size_out = kernel_size
|
||||
else:
|
||||
kernel_size_out = [
|
||||
min(shape[1], kernel_size[0]), min(shape[2], kernel_size[1])
|
||||
]
|
||||
return kernel_size_out
|
||||
|
||||
|
||||
def inception_v3_arg_scope(weight_decay=0.00004,
|
||||
batch_norm_var_collection='moving_vars',
|
||||
batch_norm_decay=0.9997,
|
||||
batch_norm_epsilon=0.001,
|
||||
updates_collections=ops.GraphKeys.UPDATE_OPS,
|
||||
use_fused_batchnorm=True):
|
||||
"""Defines the default InceptionV3 arg scope.
|
||||
|
||||
Args:
|
||||
weight_decay: The weight decay to use for regularizing the model.
|
||||
batch_norm_var_collection: The name of the collection for the batch norm
|
||||
variables.
|
||||
batch_norm_decay: Decay for batch norm moving average
|
||||
batch_norm_epsilon: Small float added to variance to avoid division by zero
|
||||
updates_collections: Collections for the update ops of the layer
|
||||
use_fused_batchnorm: Enable fused batchnorm.
|
||||
|
||||
Returns:
|
||||
An `arg_scope` to use for the inception v3 model.
|
||||
"""
|
||||
batch_norm_params = {
|
||||
# Decay for the moving averages.
|
||||
'decay': batch_norm_decay,
|
||||
# epsilon to prevent 0s in variance.
|
||||
'epsilon': batch_norm_epsilon,
|
||||
# collection containing update_ops.
|
||||
'updates_collections': updates_collections,
|
||||
# Use fused batch norm if possible.
|
||||
'fused': use_fused_batchnorm,
|
||||
# collection containing the moving mean and moving variance.
|
||||
'variables_collections': {
|
||||
'beta': None,
|
||||
'gamma': None,
|
||||
'moving_mean': [batch_norm_var_collection],
|
||||
'moving_variance': [batch_norm_var_collection],
|
||||
}
|
||||
}
|
||||
|
||||
# Set weight_decay for weights in Conv and FC layers.
|
||||
with arg_scope(
|
||||
[layers.conv2d, layers_lib.fully_connected],
|
||||
weights_regularizer=regularizers.l2_regularizer(weight_decay)):
|
||||
with arg_scope(
|
||||
[layers.conv2d],
|
||||
weights_initializer=initializers.variance_scaling_initializer(),
|
||||
activation_fn=nn_ops.relu,
|
||||
normalizer_fn=layers_lib.batch_norm,
|
||||
normalizer_params=batch_norm_params) as sc:
|
||||
return sc
|
||||
|
||||
70
run.py
Normal file
70
run.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""
|
||||
@file: model.py
|
||||
@time: 2018/4/17 15:03
|
||||
@desc:Train and evaluate the model
|
||||
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
|
||||
import tensorflow as tf
|
||||
|
||||
import utils
|
||||
from dataset.dataset_paris import input_fn
|
||||
from models import model_fn_signature as model_fn
|
||||
from utils import Params
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--model_dir', default='experiments/test')
|
||||
parser.add_argument('--mode', default='evaluate')
|
||||
|
||||
if __name__ == '__main__':
|
||||
tf.reset_default_graph()
|
||||
tf.logging.set_verbosity(tf.logging.INFO)
|
||||
|
||||
# Load the parameters from json file
|
||||
args = parser.parse_args()
|
||||
json_path = 'dataset/params.json'
|
||||
assert os.path.isfile(json_path), "No json configuration file found at {}".format(json_path)
|
||||
params = Params(json_path)
|
||||
|
||||
config = tf.estimator.RunConfig(tf_random_seed=229,
|
||||
model_dir=args.model_dir,
|
||||
save_checkpoints_steps=params.save_checkpoints_steps,
|
||||
save_summary_steps=params.save_summary_steps,
|
||||
keep_checkpoint_max=params.keep_checkpoint_max)
|
||||
estimator = tf.estimator.Estimator(model_fn, params=params, config=config)
|
||||
|
||||
if args.mode.lower() == 'train':
|
||||
""" model:{"Siamese", "SiameseInception", "2ChannelsCNN", "2ChannelsSoftmax" """
|
||||
tf.logging.info("Starting training model : {} ".format(params.model))
|
||||
estimator.train(lambda: input_fn(params, is_training=True, repeating=1, is_augment=True))
|
||||
# estimator.train(lambda: input_fn(params, is_training=True, repeating=1, is_augment=False))
|
||||
res = estimator.evaluate(lambda: input_fn(params, is_training=False, is_augment=False))
|
||||
|
||||
elif args.mode.lower() == 'predict':
|
||||
res = estimator.predict(lambda: input_fn(params, is_training=False, is_augment=False, only_label=0))
|
||||
distance_negative = [x['distance'] for x in res]
|
||||
res = estimator.predict(lambda: input_fn(params, is_training=False, is_augment=False, only_label=1))
|
||||
distance_positive = [x['distance'] for x in res]
|
||||
utils.compute_eer(distance_positive=distance_positive, distance_negative=distance_negative)
|
||||
utils.visualize(distance_positive=distance_positive, distance_negative=distance_negative)
|
||||
|
||||
else:
|
||||
tf.logging.info("Evaluation on test set.")
|
||||
res = estimator.evaluate(lambda: input_fn(params, is_training=False, is_augment=False))
|
||||
|
||||
"""evaluate from first checkpoint to last"""
|
||||
# checkpoint_file = open(args.model_dir + '/checkpoint', 'r')
|
||||
# checkpoint_lines = list(checkpoint_file.readlines())
|
||||
# checkpoint_file.close()
|
||||
# for i in range(1, len(checkpoint_lines)):
|
||||
# checkpoint = checkpoint_lines[i].split('\"')[-2]
|
||||
#
|
||||
# res = estimator.evaluate(
|
||||
# lambda: input_fn(params, False, False),
|
||||
# steps=100,
|
||||
# checkpoint_path=args.model_dir + '/' + checkpoint)
|
||||
# for key in res:
|
||||
# print("{}: {}".format(key, res[key]))
|
||||
197
utils.py
Normal file
197
utils.py
Normal file
@@ -0,0 +1,197 @@
|
||||
"""
|
||||
@file: model.py
|
||||
@time: 2018/4/17 15:03
|
||||
@desc:General utility functions
|
||||
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import sys
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import six
|
||||
from tensorflow.core.framework import node_def_pb2
|
||||
from tensorflow.python.framework import device as pydev
|
||||
from tensorflow.python.training import device_setter
|
||||
|
||||
|
||||
class Params:
|
||||
"""Class that loads hyperparameters from a json file.
|
||||
|
||||
Example:
|
||||
```
|
||||
params = Params(json_path)
|
||||
print(params.learning_rate)
|
||||
params.learning_rate = 0.5 # change the value of learning_rate in params
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(self, json_path):
|
||||
self.update(json_path)
|
||||
|
||||
def save(self, json_path):
|
||||
"""Saves parameters to json file"""
|
||||
with open(json_path, 'w') as f:
|
||||
json.dump(self.__dict__, f, indent=4)
|
||||
|
||||
def update(self, json_path):
|
||||
"""Loads parameters from json file"""
|
||||
with open(json_path) as f:
|
||||
params = json.load(f)
|
||||
self.__dict__.update(params)
|
||||
|
||||
@property
|
||||
def dict(self):
|
||||
"""Gives dict-like access to Params instance by `params.dict['learning_rate']`"""
|
||||
return self.__dict__
|
||||
|
||||
|
||||
def visualize(distance_positive, distance_negative):
|
||||
kwargs = dict(histtype='stepfilled', alpha=0.5, normed=True, bins=40)
|
||||
plt.hist(distance_positive, **kwargs)
|
||||
plt.hist(distance_negative, **kwargs)
|
||||
|
||||
plt.title('visualize distance')
|
||||
plt.show()
|
||||
|
||||
|
||||
def compute_eer(distance_positive, distance_negative):
|
||||
all_true = len(distance_negative)
|
||||
all_false = len(distance_positive)
|
||||
distance_positive = np.column_stack((np.array(distance_positive), np.zeros(len(distance_positive))))
|
||||
distance_negative = np.column_stack((np.array(distance_negative), np.ones(len(distance_negative))))
|
||||
distance = np.vstack((distance_positive, distance_negative))
|
||||
distance = distance[distance[:, 0].argsort(), :] # sort by first column
|
||||
# np.savetxt('distribution_siamese.txt', distance)
|
||||
distance = np.matrix(distance)
|
||||
|
||||
min_dis = sys.maxsize
|
||||
min_th = sys.maxsize
|
||||
eer = sys.maxsize
|
||||
fa = all_false
|
||||
miss = 0
|
||||
|
||||
for i in range(0, all_true + all_false):
|
||||
if distance[i, 1] == 1:
|
||||
miss += 1
|
||||
else:
|
||||
fa -= 1
|
||||
|
||||
fa_rate = float(fa) / all_false
|
||||
miss_rate = float(miss) / all_true
|
||||
|
||||
if abs(fa_rate - miss_rate) < min_dis:
|
||||
min_dis = abs(fa_rate - miss_rate)
|
||||
eer = max(fa_rate, miss_rate)
|
||||
min_th = distance[i, 0]
|
||||
|
||||
print('eer:', eer, ' threshold:', min_th)
|
||||
return [eer, min_th]
|
||||
|
||||
|
||||
def set_logger(log_path):
|
||||
"""Sets the logger to log info in terminal and file `log_path`.
|
||||
|
||||
In general, it is useful to have a logger so that every output to the terminal is saved
|
||||
in a permanent file. Here we save it to `model_dir/train.log`.
|
||||
|
||||
Example:
|
||||
```
|
||||
logging.info("Starting training...")
|
||||
```
|
||||
|
||||
Args:
|
||||
log_path: (string) where to log
|
||||
"""
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
if not logger.handlers:
|
||||
# Logging to a file
|
||||
file_handler = logging.FileHandler(log_path)
|
||||
file_handler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s: %(message)s'))
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
# Logging to console
|
||||
stream_handler = logging.StreamHandler()
|
||||
stream_handler.setFormatter(logging.Formatter('%(message)s'))
|
||||
logger.addHandler(stream_handler)
|
||||
|
||||
|
||||
def save_dict_to_json(d, json_path):
|
||||
"""Saves dict of floats in json file
|
||||
|
||||
Args:
|
||||
d: (dict) of float-castable values (np.float, int, float, etc.)
|
||||
json_path: (string) path to json file
|
||||
"""
|
||||
with open(json_path, 'w') as f:
|
||||
# We need to convert the values to float for json (it doesn't accept np.array, np.float, )
|
||||
d = {k: float(v) for k, v in d.items()}
|
||||
json.dump(d, f, indent=4)
|
||||
|
||||
|
||||
def local_device_setter(num_devices=1,
|
||||
ps_device_type='cpu',
|
||||
worker_device='/cpu:0',
|
||||
ps_ops=None,
|
||||
ps_strategy=None):
|
||||
if ps_ops is None:
|
||||
ps_ops = ['Variable', 'VariableV2', 'VarHandleOp']
|
||||
|
||||
if ps_strategy is None:
|
||||
ps_strategy = device_setter._RoundRobinStrategy(num_devices)
|
||||
if not six.callable(ps_strategy):
|
||||
raise TypeError("ps_strategy must be callable")
|
||||
|
||||
def _local_device_chooser(op):
|
||||
current_device = pydev.DeviceSpec.from_string(op.device or "")
|
||||
|
||||
node_def = op if isinstance(op, node_def_pb2.NodeDef) else op.node_def
|
||||
if node_def.op in ps_ops:
|
||||
ps_device_spec = pydev.DeviceSpec.from_string(
|
||||
'/{}:{}'.format(ps_device_type, ps_strategy(op)))
|
||||
|
||||
ps_device_spec.merge_from(current_device)
|
||||
return ps_device_spec.to_string()
|
||||
else:
|
||||
worker_device_spec = pydev.DeviceSpec.from_string(worker_device or "")
|
||||
worker_device_spec.merge_from(current_device)
|
||||
return worker_device_spec.to_string()
|
||||
|
||||
return _local_device_chooser
|
||||
|
||||
|
||||
def reduced_kernel_size_for_small_input(input_tensor, kernel_size):
|
||||
"""Define kernel size which is automatically reduced for small input.
|
||||
|
||||
If the shape of the input images is unknown at graph construction time this
|
||||
function assumes that the input images are is large enough.
|
||||
|
||||
Args:
|
||||
input_tensor: input tensor of size [batch_size, height, width, channels].
|
||||
kernel_size: desired kernel size of length 2: [kernel_height, kernel_width]
|
||||
|
||||
Returns:
|
||||
a tensor with the kernel size.
|
||||
|
||||
TODO(jrru): Make this function work with unknown shapes. Theoretically, this
|
||||
can be done with the code below. Problems are two-fold: (1) If the shape was
|
||||
known, it will be lost. (2) inception.tf.contrib.slim.ops._two_element_tuple
|
||||
cannot
|
||||
handle tensors that define the kernel size.
|
||||
shape = tf.shape(input_tensor)
|
||||
return = tf.stack([tf.minimum(shape[1], kernel_size[0]),
|
||||
tf.minimum(shape[2], kernel_size[1])])
|
||||
|
||||
"""
|
||||
shape = input_tensor.get_shape().as_list()
|
||||
if shape[1] is None or shape[2] is None:
|
||||
kernel_size_out = kernel_size
|
||||
else:
|
||||
kernel_size_out = [
|
||||
min(shape[1], kernel_size[0]), min(shape[2], kernel_size[1])
|
||||
]
|
||||
return kernel_size_out
|
||||
BIN
visualize/ROC_Bengali.png
Normal file
BIN
visualize/ROC_Bengali.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 232 KiB |
BIN
visualize/ROC_CEDAR_with_backgroud.png
Normal file
BIN
visualize/ROC_CEDAR_with_backgroud.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 189 KiB |
BIN
visualize/ROC_GPDS.png
Normal file
BIN
visualize/ROC_GPDS.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 229 KiB |
BIN
visualize/ROC_Hindi.png
Normal file
BIN
visualize/ROC_Hindi.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 229 KiB |
19008
visualize/distribution_2Channel2logit.txt
Normal file
19008
visualize/distribution_2Channel2logit.txt
Normal file
File diff suppressed because it is too large
Load Diff
19776
visualize/distribution_2Channel2logit_Bengali.txt
Normal file
19776
visualize/distribution_2Channel2logit_Bengali.txt
Normal file
File diff suppressed because it is too large
Load Diff
4224
visualize/distribution_2Channel2logit_CEDAR.txt
Normal file
4224
visualize/distribution_2Channel2logit_CEDAR.txt
Normal file
File diff suppressed because it is too large
Load Diff
59712
visualize/distribution_2Channel2logit_Hindi.txt
Normal file
59712
visualize/distribution_2Channel2logit_Hindi.txt
Normal file
File diff suppressed because it is too large
Load Diff
19008
visualize/distribution_2ChannelCNN.txt
Normal file
19008
visualize/distribution_2ChannelCNN.txt
Normal file
File diff suppressed because it is too large
Load Diff
19776
visualize/distribution_2ChannelsCNN_Bengali.txt
Normal file
19776
visualize/distribution_2ChannelsCNN_Bengali.txt
Normal file
File diff suppressed because it is too large
Load Diff
8448
visualize/distribution_2ChannelsCNN_CEDAR.txt
Normal file
8448
visualize/distribution_2ChannelsCNN_CEDAR.txt
Normal file
File diff suppressed because it is too large
Load Diff
59712
visualize/distribution_2ChannelsCNN_Hindi.txt
Normal file
59712
visualize/distribution_2ChannelsCNN_Hindi.txt
Normal file
File diff suppressed because it is too large
Load Diff
19008
visualize/distribution_siamese.txt
Normal file
19008
visualize/distribution_siamese.txt
Normal file
File diff suppressed because it is too large
Load Diff
19776
visualize/distribution_siamese_Bengali.txt
Normal file
19776
visualize/distribution_siamese_Bengali.txt
Normal file
File diff suppressed because it is too large
Load Diff
8448
visualize/distribution_siamese_CEDAR.txt
Normal file
8448
visualize/distribution_siamese_CEDAR.txt
Normal file
File diff suppressed because it is too large
Load Diff
59712
visualize/distribution_siamese_Hindi.txt
Normal file
59712
visualize/distribution_siamese_Hindi.txt
Normal file
File diff suppressed because it is too large
Load Diff
101
visualize/visualization.py
Normal file
101
visualize/visualization.py
Normal file
@@ -0,0 +1,101 @@
|
||||
"""
|
||||
@file: model.py
|
||||
@time: 2018/6/17 15:03
|
||||
@desc:
|
||||
|
||||
"""
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import tensorflow as tf
|
||||
from sklearn.metrics import confusion_matrix, accuracy_score, roc_auc_score, roc_curve
|
||||
|
||||
|
||||
def compute_er(y_true, y_prob):
|
||||
fpr, tpr, thresholds = roc_curve(y_true, y_prob, pos_label=True)
|
||||
|
||||
sum_sensitivity_specificity_train = tpr + (1 - fpr)
|
||||
best_threshold_id = np.argmax(sum_sensitivity_specificity_train)
|
||||
best_threshold = thresholds[best_threshold_id]
|
||||
|
||||
y = y_prob > best_threshold
|
||||
|
||||
cm_test = confusion_matrix(y_true, y)
|
||||
acc_test = accuracy_score(y_true, y)
|
||||
auc_test = roc_auc_score(y_true, y)
|
||||
|
||||
print('Test Accuracy: %s ' % acc_test)
|
||||
print('Test AUC: %s ' % auc_test)
|
||||
print('Test Confusion Matrix:')
|
||||
print(cm_test)
|
||||
|
||||
tpr_score = float(cm_test[1][1]) / (cm_test[1][1] + cm_test[1][0])
|
||||
fpr_score = float(cm_test[0][1]) / (cm_test[0][0] + cm_test[0][1])
|
||||
|
||||
return fpr, tpr
|
||||
|
||||
|
||||
def read_y_prob(filename):
|
||||
TwoChannel2logit = np.loadtxt(filename)
|
||||
siamese = np.split(TwoChannel2logit, 2, axis=1)
|
||||
y_true = siamese[1]
|
||||
y_prob = siamese[0]
|
||||
return y_true, y_prob
|
||||
|
||||
|
||||
def visualize_roc():
|
||||
y_true_2logit, y_prob_2logit = read_y_prob('distribution_2Channel2logit_CEDAR.txt')
|
||||
y_true_1logit, y_prob_1logit = read_y_prob('distribution_2ChannelsCNN_CEDAR.txt')
|
||||
y_true_siamese, y_prob_siamese = read_y_prob('distribution_siamese_CEDAR.txt')
|
||||
fpr_siamese, tpr_siamese = compute_er(y_true_siamese, y_prob_siamese)
|
||||
fpr_1logit, tpr_1logit = compute_er(y_true_1logit, y_prob_1logit)
|
||||
fpr_2logit, tpr_2logit = compute_er(y_true_2logit, y_prob_2logit)
|
||||
|
||||
fig = plt.figure(figsize=(5, 5))
|
||||
ax2 = fig.add_subplot(111)
|
||||
curve1 = ax2.plot(fpr_siamese, tpr_siamese)
|
||||
curve2 = ax2.plot(fpr_1logit, tpr_1logit)
|
||||
curve3 = ax2.plot(fpr_2logit, tpr_2logit)
|
||||
curve4 = ax2.plot([0, 1], [0, 1], color='navy', linestyle='--')
|
||||
# dot = ax2.plot(fpr_score, tpr_score, marker='o', color='black')
|
||||
# ax2.text(fpr_score, tpr_score, s='(%.3f,%.3f)' % (fpr_score, tpr_score))
|
||||
plt.xlim([0.0, 1.0])
|
||||
plt.ylim([0.0, 1.0])
|
||||
plt.xlabel('False Positive Rate')
|
||||
plt.ylabel('True Positive Rate')
|
||||
# plt.title('ROC curve (Test), AUC = %.4f' % auc_test)
|
||||
params = {'legend.fontsize': 15,
|
||||
'legend.handlelength': 2}
|
||||
plt.rcParams.update(params)
|
||||
plt.legend(['Siamese', '2ChannelCNN', '2Channel2logit'])
|
||||
plt.savefig('ROC_CEDAR_with_backgroud', dpi=500)
|
||||
plt.show()
|
||||
|
||||
|
||||
visualize_roc()
|
||||
|
||||
|
||||
def get_auc():
|
||||
writer_val = tf.summary.FileWriter('C:\work\Projects\HWS_ID\\test\\2Channels\\val')
|
||||
writer_train = tf.summary.FileWriter('C:\work\Projects\HWS_ID\\test\\2Channels\\train')
|
||||
auc_var = tf.Variable(0.0)
|
||||
tf.summary.scalar("auc", auc_var)
|
||||
write_op = tf.summary.merge_all()
|
||||
session = tf.InteractiveSession()
|
||||
session.run(tf.global_variables_initializer())
|
||||
|
||||
for e in tf.train.summary_iterator(
|
||||
"C:\work\Projects\HWS_ID\\test\\2Channels\\2channelscnn.Deep-Ubantu"):
|
||||
for v in e.summary.value:
|
||||
if 'auc' in v.tag:
|
||||
summary = session.run(write_op, {auc_var: v.simple_value})
|
||||
writer_train.add_summary(summary, e.step)
|
||||
writer_train.flush()
|
||||
|
||||
for e in tf.train.summary_iterator(
|
||||
"C:\work\Projects\HWS_ID\\test\\2Channels\\2channelsoftmax.Deep-Ubantu"):
|
||||
for v in e.summary.value:
|
||||
if 'auc' in v.tag:
|
||||
summary = session.run(write_op, {auc_var: v.simple_value})
|
||||
writer_val.add_summary(summary, e.step)
|
||||
writer_val.flush()
|
||||
Reference in New Issue
Block a user