diff --git a/.gitattributes b/.gitattributes index a6344aac8c09253b3b630fb776ae94478aa0275b..b6fbd6fd81edb55cf3cef0d436aae2992c479fe1 100644 --- a/.gitattributes +++ b/.gitattributes @@ -33,3 +33,19 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text *.zip filter=lfs diff=lfs merge=lfs -text *.zst filter=lfs diff=lfs merge=lfs -text *tfevents* filter=lfs diff=lfs merge=lfs -text +assets/dynamic_accent_banner.jpg filter=lfs diff=lfs merge=lfs -text +prompts/clip0_waxal_yor_yor_1258.wav filter=lfs diff=lfs merge=lfs -text +prompts/clip1_waxal_yor_yor_1205.wav filter=lfs diff=lfs merge=lfs -text +prompts/clip2_waxal_yor_yor_1263.wav filter=lfs diff=lfs merge=lfs -text +prompts/clip3_waxal_yor_yor_1218.wav filter=lfs diff=lfs merge=lfs -text +prompts/voice_waxal_ibo_5.wav filter=lfs diff=lfs merge=lfs -text +prompts/voice_waxal_yor_5.wav filter=lfs diff=lfs merge=lfs -text +samples/flash_ibo_waxal_ibo_5_default.mp3 filter=lfs diff=lfs merge=lfs -text +samples/pro_cs_anchor_default.mp3 filter=lfs diff=lfs merge=lfs -text +samples/pro_cs_anchor_many_pauses.mp3 filter=lfs diff=lfs merge=lfs -text +samples/pro_cs_anchor_slow.mp3 filter=lfs diff=lfs merge=lfs -text +samples/pro_cs_waxal_ibo_5_many_pauses.mp3 filter=lfs diff=lfs merge=lfs -text +samples/pro_ibo_anchor_many_pauses.mp3 filter=lfs diff=lfs merge=lfs -text +samples/pro_ibo_anchor_slow.mp3 filter=lfs diff=lfs merge=lfs -text +samples/pro_ibo_waxal_ibo_5_default.mp3 filter=lfs diff=lfs merge=lfs -text +samples/pro_ibo_waxal_ibo_5_many_pauses.mp3 filter=lfs diff=lfs merge=lfs -text diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..2d58298e6eda10e7204abb52722efbc840db2390 --- /dev/null +++ b/LICENSE @@ -0,0 +1,428 @@ +Attribution-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + including for purposes of Section 3(b); and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. + diff --git a/LICENSE-CODE b/LICENSE-CODE new file mode 100644 index 0000000000000000000000000000000000000000..d645695673349e3947e8e5ae42332d0ac3164cd7 --- /dev/null +++ b/LICENSE-CODE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..19f3bf2010cd81ac6ff56a44e85dee643c840188 --- /dev/null +++ b/README.md @@ -0,0 +1,247 @@ +--- +license: cc-by-sa-4.0 +language: + - en + - pcm + - yo + - ig + - ha +base_model: + - FunAudioLLM/Fun-CosyVoice3-0.5B-2512 +pipeline_tag: text-to-speech +library_name: pytorch +tags: + - text-to-speech + - code-switching + - nigerian-languages + - igbo + - yoruba + - hausa + - nigerian-pidgin + - cosyvoice3 + - instruction-tts + - dpo + - experimental +datasets: + - google/WaxalNLP + - McGill-NLP/NaijaS2ST + - AfriSpeech/open-bible-speech-african + - nkowaokwu/ibo-dict + - google/fleurs +--- + +

+ +# Dynamic Accent Pro v1.1 + +**Experimental prompt-controlled Nigerian TTS and code-switching technology** by Modular. English, Nigerian Pidgin, Igbo, Yoruba and Hausa, mixed inside one sentence and spoken by one consistent voice, with a plain-English **style instruction** (`--instruct`) that steers pace and pausing. + +Pro v1.1 is a research release, not a production system. It is the Flash recipe (CosyVoice3 LM fine-tuned on licensed Nigerian-language speech, then DPO) with a new instruction layer on top. Read **What works and what does not** before using it. + +## What works and what does not + +| control | status | how | +|---|---|---| +| **Pace** ("speak slowly", "speak fast") | **works** | read by the LM from the instruction, sharpened by classifier-free guidance (`--guidance 2`, the default). Slow vs fast is ordered correctly on 18/18 held-out items; slow ≈ 0.89× and fast ≈ 1.15× the default articulation rate | +| **Pauses** ("plenty of pauses", "hardly any pauses") | **works through a text step** | the instruction also edits the text before synthesis (`pause_text.py`: inserts the model's learned `[pause]` tag for "many", strips commas and pause tags for "few"). Many ≈ 1.28× default pause rate; few ≈ 0.91×. The LM alone barely moves pauses, so `--no_text_pauses` mostly turns this off | +| Calm / lively tone | **not advertised** | trained, but indistinguishable from chance in our measurements. The words are accepted and ignored in effect | +| Emotion (happy, sad, angry, …) | **no** | not trained, no data | +| Code-switching from **tags** (`… …`) | works (the core Dynamic Accent capability) | language embeddings + a speaker token keep one voice across switches | +| Code-switching from a **request** (`--codeswitch_prompt "mostly English, Igbo greetings"`) | **experimental, separate step** | a text LLM ([Qwen/Qwen3-32B](https://huggingface.co/Qwen/Qwen3-32B), Apache-2.0, not bundled) rewrites plain text into tagged code-switched text *before* synthesis. It is not a TTS capability. Its Pidgin and Hausa are usable; **its Igbo and Yoruba beyond simple greetings are weak and often wrong**, so always read the printed tagged text and fix it | + +Without `--instruct`, `infer.py` uses Flash-DPO (the default version). Passing `--instruct` switches to Pro automatically unless you set `--version`; `--version pro` without an instruction uses Pro's default prompt, trained on native-like clips. + +## Versions and voices + +| version | LM | notes | +|---|---|---| +| `pro` (used automatically with `--instruct`) | `checkpoints/pro/flash_instruct` | Pro v1.1: Flash-DPO → instruction SFT → DPO polish. Instruction control, CFG 2.0 on the instruction, text pause step | +| `flash` (**default**) | `checkpoints/flash/flash_dpo` | Flash-DPO (no instruction control), the default when no `--instruct` is given: it sounds more natural on plain code-switched text. A/B: `--compare` renders the same text with both | + +Both use the stock Fun-CosyVoice3-0.5B-2512 flow and vocoder, zero-shot from the voice clip, and the same text front-end (numbers verbalised; missing Yoruba/Igbo diacritics restored by `checkpoints/flash/diacritics_byt5`). + +| voice (`--voice`) | who | +|---|---| +| `anchor_male` (default) | WAXAL Media Trust Yoruba speaker 4 (male), the voice both models were trained around | +| `waxal_ibo_5_female` | WAXAL Igbo speaker 5 (female), zero-shot from a 6.6 s clip | +| `waxal_yor_5_female` | WAXAL Yoruba speaker 5 (female), zero-shot; short prompt, so short pauses and more clipped endings | + +## Setup (single CUDA GPU) + +```bash +git clone --recursive https://github.com/FunAudioLLM/CosyVoice.git +cd CosyVoice && git checkout 074ca6dc9e80a2f424f1f74b48bdd7d3fea531cc && git submodule update --init --recursive && cd .. +huggingface-cli download Modularcomputing/dynamic-accent-pro-v1.1 --local-dir dynamic-accent-pro +pip install -r dynamic-accent-pro/requirements.txt +``` + +Fun-CosyVoice3-0.5B-2512 downloads on first run (`--cv3_dir` / `CV3_DIR` to reuse a copy). Tested on an H100 in bf16; a 16 GB GPU is enough for TTS. The optional code-switch step needs a GPU that holds Qwen3-32B in bf16 (~65 GB); `--cs_llm Qwen/Qwen3-14B` is lighter and worse. + +## Usage + +```bash +cd dynamic-accent-pro +# default prompt (native-like pace and pauses) +python infer.py --cosyvoice_dir ../CosyVoice --text "Ndewo nụ, ụmụnna m. Tonight we will talk about the new market road." --out out.wav + +# pace +python infer.py --cosyvoice_dir ../CosyVoice --instruct "Please speak slowly and take your time." --text "..." --out slow.wav +python infer.py --cosyvoice_dir ../CosyVoice --instruct "Speak fast, like a quick everyday conversation." --text "..." --out fast.wav + +# pauses (text step + instruction) +python infer.py --cosyvoice_dir ../CosyVoice --instruct "Speak with plenty of pauses between phrases." --text "..." --out many.wav +python infer.py --cosyvoice_dir ../CosyVoice --instruct "Say it in one smooth flow with hardly any pauses." --text "..." --out few.wav + +# female Igbo voice +python infer.py --cosyvoice_dir ../CosyVoice --voice waxal_ibo_5_female --text "Kedu ka ị mere? Abeg no vex, I go call you for evening." --out ibo_f.wav + +# experimental: plain text + a code-switch request -> Qwen3-32B writes the tagged text (check it!) -> TTS +python infer.py --cosyvoice_dir ../CosyVoice --codeswitch_prompt "Nigerian Pidgin with an Igbo greeting" \ + --text "My brother, the work is too much today, but we will finish it." --out cs.wav + +# A/B the same text, seed and voice with Flash and Pro -> cmp_flash.wav, cmp_pro.wav +python infer.py --cosyvoice_dir ../CosyVoice --compare --compare_versions flash,pro --text "..." --out cmp.wav +``` + +| flag | what it does | +|---|---| +| `--instruct "..."` | `pro`: natural-language style instruction. Reliable: slow / fast; plenty of / hardly any pauses. Phrasings close to the examples above work best | +| `--guidance G` | `pro`: classifier-free guidance strength on the instruction (default 2.0; 0 = off, weaker pace control) | +| `--no_text_pauses` | `pro`: do not edit the text for pause instructions | +| `--codeswitch_prompt "..."` | optional text-LLM step before synthesis (see above); output is validated (tags, length, loops, back-translation meaning check) and falls back to the input text | +| `--cs_llm` | the LLM for that step (default `Qwen/Qwen3-32B`) | +| `--version pro\|flash`, `--compare`, `--compare_versions` | model version / A/B rendering | +| `--voice`, `--list_voices` | voice selection | +| `--auto_tag`, `--tag_only`, `--force_lang`, `--lang_map` | language tagging of untagged text (see `tagger.py`) | +| `--frontend codeswitch\|native\|accent`, `--frontend_lang`, `--no_diacritics` | text front-end | +| `--seed N` | sampling seed (default 0). Try 2–3 seeds and keep the best take | +| `--tail_pad N` | append N silence speech tokens before the vocoder so the last syllable is not clipped (opt-in; 6 tested) | + +Text: wrap each language span in ``, ``, ``, `` or ``; untagged text is read as English unless `--auto_tag` / `--force_lang` is given. Write Yoruba and Igbo with diacritics where you can. GPU generation is not bit-reproducible (bf16 near-ties). + +## Samples + +`samples/` holds MP3 renders for all five languages and a code-switched sentence, both main voices, with the default prompt and the slow / fast / few-pauses / many-pauses instructions, plus a Flash-DPO render of the same text and seed for A/B (`samples/meta.json` lists text, voice and instruction per file). `samples/codeswitch_frontend/` shows the experimental Qwen3-32B step: input text, request, the tagged text it produced, and the audio. The Igbo and Yoruba demo sentences have **not yet been checked by a native speaker**. + +## Results + +Automatic regression set: 197 held-out sentences per voice (30 each of Igbo, Yoruba, Hausa, Pidgin, English; 24 code-switched; 23 paragraphs), default prompt, seed 0. CER from automatic ASR judges (per language, same judges for every system), pace and pause rate relative to native recordings of the same languages, clipped ending = less than 0.1 s of trailing silence, SIM = speaker similarity to the prompt clip, UTMOSv2 = predicted naturalness. + +| voice | group | system | CER | CER no-tone | pace × native | pauses × native | clipped endings | SIM | UTMOSv2 | +|---|---|---|---|---|---|---|---|---|---| +| anchor_male | all | Flash-DPO | 0.162 | 0.135 | 0.99 | 0.75 | 7% | 0.784 | 3.17 | +| anchor_male | all | **Pro v1.1** | 0.167 | 0.141 | 0.99 | 1.02 | 8% | 0.789 | 3.16 | +| anchor_male | ibo | Flash-DPO | 0.189 | 0.158 | 1.06 | 0.40 | 7% | 0.762 | 3.06 | +| anchor_male | ibo | **Pro v1.1** | 0.216 | 0.181 | 1.02 | 0.62 | 3% | 0.779 | 3.01 | +| anchor_male | yor | Flash-DPO | 0.230 | 0.144 | 1.08 | 0.30 | 3% | 0.762 | 2.99 | +| anchor_male | yor | **Pro v1.1** | 0.218 | 0.141 | 1.06 | 0.68 | 3% | 0.770 | 3.02 | +| anchor_male | hau | Flash-DPO | 0.123 | 0.123 | 0.83 | 0.77 | 0% | 0.788 | 3.26 | +| anchor_male | hau | **Pro v1.1** | 0.147 | 0.147 | 0.81 | 1.19 | 3% | 0.796 | 3.19 | +| anchor_male | pcm | Flash-DPO | 0.227 | 0.227 | 0.97 | 0.72 | 10% | 0.782 | 3.26 | +| anchor_male | pcm | **Pro v1.1** | 0.199 | 0.199 | 1.01 | 0.97 | 3% | 0.779 | 3.25 | +| anchor_male | eng | Flash-DPO | 0.026 | 0.026 | 0.94 | 1.85 | 17% | 0.790 | 3.26 | +| anchor_male | eng | **Pro v1.1** | 0.031 | 0.031 | 0.94 | 1.89 | 7% | 0.796 | 3.26 | +| anchor_male | cs | Flash-DPO | 0.208 | 0.159 | 1.02 | 1.00 | 12% | 0.773 | 3.09 | +| anchor_male | cs | **Pro v1.1** | 0.213 | 0.162 | 1.05 | 1.27 | 25% | 0.766 | 3.22 | +| anchor_male | para | Flash-DPO | 0.131 | 0.106 | 1.06 | 1.08 | 0% | 0.843 | 3.28 | +| anchor_male | para | **Pro v1.1** | 0.149 | 0.126 | 1.05 | 1.29 | 13% | 0.843 | 3.16 | +| waxal_ibo_5_female | all | Flash-DPO | 0.165 | 0.134 | 1.03 | 0.95 | 11% | 0.700 | 3.51 | +| waxal_ibo_5_female | all | **Pro v1.1** | 0.152 | 0.123 | 1.01 | 0.84 | 12% | 0.698 | 3.45 | +| waxal_ibo_5_female | ibo | Flash-DPO | 0.215 | 0.171 | 1.09 | 0.52 | 7% | 0.697 | 3.41 | +| waxal_ibo_5_female | ibo | **Pro v1.1** | 0.178 | 0.145 | 1.07 | 0.58 | 0% | 0.682 | 3.40 | +| waxal_ibo_5_female | yor | Flash-DPO | 0.251 | 0.150 | 1.18 | 0.86 | 7% | 0.661 | 3.45 | +| waxal_ibo_5_female | yor | **Pro v1.1** | 0.224 | 0.118 | 1.11 | 0.38 | 10% | 0.671 | 3.36 | +| waxal_ibo_5_female | hau | Flash-DPO | 0.117 | 0.117 | 0.86 | 1.20 | 7% | 0.700 | 3.51 | +| waxal_ibo_5_female | hau | **Pro v1.1** | 0.132 | 0.132 | 0.85 | 0.74 | 13% | 0.676 | 3.47 | +| waxal_ibo_5_female | pcm | Flash-DPO | 0.184 | 0.183 | 1.02 | 0.92 | 13% | 0.674 | 3.63 | +| waxal_ibo_5_female | pcm | **Pro v1.1** | 0.152 | 0.151 | 1.02 | 0.83 | 20% | 0.690 | 3.48 | +| waxal_ibo_5_female | eng | Flash-DPO | 0.031 | 0.031 | 0.99 | 1.34 | 10% | 0.726 | 3.60 | +| waxal_ibo_5_female | eng | **Pro v1.1** | 0.031 | 0.031 | 0.93 | 1.62 | 13% | 0.745 | 3.52 | +| waxal_ibo_5_female | cs | Flash-DPO | 0.230 | 0.179 | 1.01 | 1.32 | 29% | 0.682 | 3.50 | +| waxal_ibo_5_female | cs | **Pro v1.1** | 0.220 | 0.174 | 1.07 | 1.22 | 17% | 0.660 | 3.40 | +| waxal_ibo_5_female | para | Flash-DPO | 0.133 | 0.106 | 1.07 | 0.99 | 4% | 0.773 | 3.44 | +| waxal_ibo_5_female | para | **Pro v1.1** | 0.141 | 0.114 | 1.05 | 1.20 | 9% | 0.772 | 3.50 | + +Native-speaker reference: pace 1.00 / pauses 1.00 by definition. Summary: Pro v1.1 matches Flash-DPO on overall CER for the anchor voice (0.167 vs 0.162) and beats it for the female Igbo voice (0.152 vs 0.165); it pauses much closer to native in Igbo and Yoruba on the anchor voice (0.62× / 0.68× vs 0.40× / 0.30×); it is worse than Flash-DPO on anchor Igbo CER (0.216 vs 0.189) and on Hausa for both voices. + +Instruction adherence (anchor voice, 18 held-out Igbo/Yoruba/Hausa/Pidgin/English/code-switch items, CFG 2, text pause step on; "direction" = share of items moving the requested way vs the default prompt): + +| instruction | articulation rate × default | pause rate × default | direction correct | +|---|---|---|---| +| Please speak slowly and take your time. | 0.89 | 1.16 | 89% | +| Speak fast, like a quick everyday conversation. | 1.15 | 0.87 | 94% | +| Say it in one smooth flow with hardly any pauses. | 0.92 | 0.91 | 56% | +| Speak with plenty of pauses between phrases. | 0.90 | 1.28 | 78% | +| Use a calm, gentle, even tone. *(not advertised)* | 0.98 | 1.02 | 50% | +| Sound lively and expressive. *(not advertised)* | 0.96 | 0.94 | 44% | + +Slow vs fast on the same item: correct order on 100% of items; many vs few pauses: 67%; lively vs calm: 50% (chance). Note that "slow" also adds pauses (1.16× pause rate). 18 items is a small set; treat these as indicative. + +## Limitations + +- **Experimental.** Pace control is real; pause control depends on the text step; calm/lively and emotion are not available. +- **Anchor Igbo** CER is still higher than Flash-DPO (0.216 vs 0.189); **Hausa** CER is worse than Flash-DPO for both voices. Flash-DPO remains available as `--version flash`. +- English stays over-paused relative to native English (about 1.9× native pause rate on the anchor voice, the same as Flash-DPO). +- Clipped endings remain on code-switched sentences and paragraphs (13–25% on the anchor voice). `--tail_pad 6` (six silence tokens appended before the vocoder, inference-only) removed every measured clipped ending in testing with no consistent CER change (+0.005 overall, within seed noise); it is opt-in until it has been checked by ear. +- The code-switch request step (Qwen3-32B) is a separate text model: weak Igbo/Yoruba beyond greetings, sometimes refuses to switch and falls back to the input text. +- Pidgin has little training data and no reliable ASR judge; Yoruba tone quality needs native listening. +- Female voices are zero-shot. Not evaluated for numbers, abbreviations or URLs. +- Do not use it to impersonate real people. The voices are WAXAL Media Trust speakers, released under CC-BY-SA-4.0 for speech technology. + +## Training + +1. **Flash-DPO** (the `flash` version): CosyVoice3 LM fine-tuned on the licensed set below (language embeddings, speaker token, adversarial speaker classifier, rate embedding), then DPO on 1,206 preference pairs. +2. **Instruction SFT** (3,000 steps): ~100.9k licensed clips labelled from the audio (articulation rate, pause rate, energy/F0 variation); each training item gets a natural-language instruction built from its labels in the CosyVoice3 system-prompt slot, plus explicit pause/energy control embeddings; 18% of items keep the default prompt, drawn from native-like clips. +3. **DPO polish** (134 steps, β 0.1, lr 1e-6, SFT weight 0.1): 700 prompts × 4 samples from the SFT model, 564 preference pairs; reward = CER + pace and pause-rate match to native + a clipped-ending penalty + a small UTMOS guard. + +Inference: classifier-free guidance on the instruction (conditional = instruction + controls, unconditional = default prompt), g = 2. + +## What is in the repo + +| path | what it is | licence | +|---|---|---| +| `checkpoints/pro/flash_instruct/` | Pro v1.1 LM (`best.pt`, sha256 `87c3a7e9…4cab3db`), `config.json`, `spk2id.json`, DPO `args.json`, `log.jsonl` | CC-BY-SA-4.0 | +| `checkpoints/flash/flash_dpo/` | Flash-DPO LM (`best.pt`, sha256 `f59a6fc0…7e3a307`) | CC-BY-SA-4.0 | +| `checkpoints/flash/diacritics_byt5/` | Yoruba/Igbo diacritic restoration (fine-tune of google/byt5-small) | CC-BY-SA-4.0 | +| `prompts/` | voice clips and transcripts (WAXAL) | CC-BY-SA-4.0 | +| `samples/` | demo renders | CC-BY-SA-4.0 | +| `config.json` | repo-level manifest (also lets the Hub count downloads) | Apache-2.0 | +| `settings.json` | versions, voices, defaults | Apache-2.0 | +| `infer.py`, `flash_lm.py`, `flash_instruct.py`, `instruct_text.py`, `pause_text.py`, `cs_frontend.py`, `tagger.py`, `frontend/`, `code/` | inference, model, instruction, text pause step, code-switch step, tagger, front-end, reference training code | Apache-2.0 | + +## Training data and attribution + +Licensed data only. The weights are trained on, and the voice prompts taken from: + +| dataset | licence | attribution | +|---|---|---| +| [WAXAL](https://huggingface.co/datasets/google/WaxalNLP), Media Trust TTS subsets (Igbo, Yoruba, Hausa, Nigerian Pidgin); all voice prompts | CC-BY-SA-4.0 | Google and Media Trust, WAXAL (arXiv:2602.02734) | +| [Open.Bible Igbo](https://huggingface.co/datasets/AfriSpeech/open-bible-speech-african) | CC-BY-SA-4.0 | Biblica, Inc. via open.bible | +| [NaijaS2ST](https://huggingface.co/datasets/McGill-NLP/NaijaS2ST) (Igbo) | CC-BY-4.0 | McGill-NLP (arXiv:2604.16287) | +| [IgboAPI](https://huggingface.co/datasets/nkowaokwu/ibo-dict) | CC-BY-4.0 | Nkọwa okwu / IgboAPI contributors | +| [IgboSynCorp](https://doi.org/10.7910/dvn/yb9fwk) (+ pseudo-transcripts) | CC0-1.0 | University of Ibadan and Afe Babalola University (Lacuna Fund) | +| [FLEURS](https://huggingface.co/datasets/google/fleurs) | CC-BY-4.0 | Google (Conneau et al., 2022) | +| [BibleTTS](https://masakhane-io.github.io/bibleTTS/) (Yoruba, Hausa) | CC-BY-SA-4.0 | Meyer et al. (2022) | +| [OpenSLR SLR70](https://openslr.org/70/), [SLR86](https://openslr.org/86/) | CC-BY-SA-4.0 | Google | +| synthetic code-switch pairs (concatenated clips from the sets above) | follows the sources | after Zhang et al. (2019) | +| base model [Fun-CosyVoice3-0.5B-2512](https://huggingface.co/FunAudioLLM/Fun-CosyVoice3-0.5B-2512) | Apache-2.0 | FunAudioLLM / Alibaba Tongyi Lab | +| optional [Qwen3-32B](https://huggingface.co/Qwen/Qwen3-32B) (not bundled; code-switch step only) | Apache-2.0 | Qwen team, Alibaba | + +DPO preference pairs are the model's own renders, scored automatically. Excluded on licence grounds: African Voices / WaZoBiaSpeech (cards ban TTS use), NaijaVoices (non-commercial), and non-commercial TTS weights. + +## Licence + +- **Model weights, voice prompts and samples** (`checkpoints/`, `prompts/`, `samples/`): **[CC-BY-SA-4.0](LICENSE)**. Commercial use is allowed; credit Modular and the datasets above; derivatives of the weights must be shared under CC-BY-SA-4.0. +- **Code** (`*.py`, `frontend/`, `code/`, `settings.json`, `config.json`): **[Apache-2.0](LICENSE-CODE)**. +- Fun-CosyVoice3-0.5B-2512, byt5-small and Qwen3 are Apache-2.0; their notices apply to the parts they contribute. + +## Citation + +```bibtex +@misc{dynamicaccentpro2026, + title = {Dynamic Accent Pro v1.1: prompt-controlled code-switching TTS for Nigerian languages}, + author = {Modular}, + year = {2026}, + url = {https://huggingface.co/Modularcomputing/dynamic-accent-pro-v1.1} +} +``` diff --git a/assets/dynamic_accent_banner.jpg b/assets/dynamic_accent_banner.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bdd5926340742caccd1a16e97a9e3ba321b601a1 --- /dev/null +++ b/assets/dynamic_accent_banner.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0b51f3df37d126969b4f4b5e6737e9d92d6e91e5db78bac0cc47bd6d16ca55be +size 305338 diff --git a/checkpoints/flash/diacritics_byt5/added_tokens.json b/checkpoints/flash/diacritics_byt5/added_tokens.json new file mode 100644 index 0000000000000000000000000000000000000000..93c190b5690dd55aac16723222a9909e2be0faec --- /dev/null +++ b/checkpoints/flash/diacritics_byt5/added_tokens.json @@ -0,0 +1,127 @@ +{ + "": 259, + "": 359, + "": 360, + "": 361, + "": 362, + "": 363, + "": 364, + "": 365, + "": 366, + "": 367, + "": 368, + "": 269, + "": 369, + "": 370, + "": 371, + "": 372, + "": 373, + "": 374, + "": 375, + "": 376, + "": 377, + "": 378, + "": 270, + "": 379, + "": 380, + "": 381, + "": 382, + "": 383, + "": 271, + "": 272, + "": 273, + "": 274, + "": 275, + "": 276, + "": 277, + "": 278, + "": 260, + "": 279, + "": 280, + "": 281, + "": 282, + "": 283, + "": 284, + "": 285, + "": 286, + "": 287, + "": 288, + "": 261, + "": 289, + "": 290, + "": 291, + "": 292, + "": 293, + "": 294, + "": 295, + "": 296, + "": 297, + "": 298, + "": 262, + "": 299, + "": 300, + "": 301, + "": 302, + "": 303, + "": 304, + "": 305, + "": 306, + "": 307, + "": 308, + "": 263, + "": 309, + "": 310, + "": 311, + "": 312, + "": 313, + "": 314, + "": 315, + "": 316, + "": 317, + "": 318, + "": 264, + "": 319, + "": 320, + "": 321, + "": 322, + "": 323, + "": 324, + "": 325, + "": 326, + "": 327, + "": 328, + "": 265, + "": 329, + "": 330, + "": 331, + "": 332, + "": 333, + "": 334, + "": 335, + "": 336, + "": 337, + "": 338, + "": 266, + "": 339, + "": 340, + "": 341, + "": 342, + "": 343, + "": 344, + "": 345, + "": 346, + "": 347, + "": 348, + "": 267, + "": 349, + "": 350, + "": 351, + "": 352, + "": 353, + "": 354, + "": 355, + "": 356, + "": 357, + "": 358, + "": 268 +} diff --git a/checkpoints/flash/diacritics_byt5/config.json b/checkpoints/flash/diacritics_byt5/config.json new file mode 100644 index 0000000000000000000000000000000000000000..ce3a77003e116c5c4c76983272e65ba799534905 --- /dev/null +++ b/checkpoints/flash/diacritics_byt5/config.json @@ -0,0 +1,32 @@ +{ + "architectures": [ + "T5ForConditionalGeneration" + ], + "classifier_dropout": 0.0, + "d_ff": 3584, + "d_kv": 64, + "d_model": 1472, + "decoder_start_token_id": 0, + "dense_act_fn": "gelu_new", + "dropout_rate": 0.1, + "eos_token_id": 1, + "feed_forward_proj": "gated-gelu", + "gradient_checkpointing": false, + "initializer_factor": 1.0, + "is_encoder_decoder": true, + "is_gated_act": true, + "layer_norm_epsilon": 1e-06, + "model_type": "t5", + "num_decoder_layers": 4, + "num_heads": 6, + "num_layers": 12, + "pad_token_id": 0, + "relative_attention_max_distance": 128, + "relative_attention_num_buckets": 32, + "tie_word_embeddings": false, + "tokenizer_class": "ByT5Tokenizer", + "torch_dtype": "float32", + "transformers_version": "4.51.3", + "use_cache": true, + "vocab_size": 384 +} diff --git a/checkpoints/flash/diacritics_byt5/generation_config.json b/checkpoints/flash/diacritics_byt5/generation_config.json new file mode 100644 index 0000000000000000000000000000000000000000..a685340b4034fa14db5622f76e58f3a867da4487 --- /dev/null +++ b/checkpoints/flash/diacritics_byt5/generation_config.json @@ -0,0 +1,7 @@ +{ + "_from_model_config": true, + "decoder_start_token_id": 0, + "eos_token_id": 1, + "pad_token_id": 0, + "transformers_version": "4.51.3" +} diff --git a/checkpoints/flash/diacritics_byt5/model.safetensors b/checkpoints/flash/diacritics_byt5/model.safetensors new file mode 100644 index 0000000000000000000000000000000000000000..e595c20bcd114d0cd84517d4bf404b523b07001d --- /dev/null +++ b/checkpoints/flash/diacritics_byt5/model.safetensors @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27c2f5f06306a51af9f6107c38aebff2606d7589cd4f870a4478c7b7047f789e +size 1198571496 diff --git a/checkpoints/flash/diacritics_byt5/special_tokens_map.json b/checkpoints/flash/diacritics_byt5/special_tokens_map.json new file mode 100644 index 0000000000000000000000000000000000000000..04fd58b5fbf6a36fda564a656b14c137ef045689 --- /dev/null +++ b/checkpoints/flash/diacritics_byt5/special_tokens_map.json @@ -0,0 +1,150 @@ +{ + "additional_special_tokens": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "eos_token": { + "content": "", + "lstrip": false, + "normalized": true, + "rstrip": false, + "single_word": false + }, + "pad_token": { + "content": "", + "lstrip": false, + "normalized": true, + "rstrip": false, + "single_word": false + }, + "unk_token": { + "content": "", + "lstrip": false, + "normalized": true, + "rstrip": false, + "single_word": false + } +} diff --git a/checkpoints/flash/diacritics_byt5/tokenizer_config.json b/checkpoints/flash/diacritics_byt5/tokenizer_config.json new file mode 100644 index 0000000000000000000000000000000000000000..5b1fe24c1b1962f69a7754a26e6770ae7bcb7764 --- /dev/null +++ b/checkpoints/flash/diacritics_byt5/tokenizer_config.json @@ -0,0 +1,1163 @@ +{ + "added_tokens_decoder": { + "0": { + "content": "", + "lstrip": false, + "normalized": true, + "rstrip": false, + "single_word": false, + "special": true + }, + "1": { + "content": "", + "lstrip": false, + "normalized": true, + "rstrip": false, + "single_word": false, + "special": true + }, + "2": { + "content": "", + "lstrip": false, + "normalized": true, + "rstrip": false, + "single_word": false, + "special": true + }, + "259": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "260": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "261": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "262": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "263": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "264": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "265": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "266": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "267": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "268": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "269": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "270": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "271": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "272": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "273": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "274": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "275": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "276": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "277": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "278": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "279": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "280": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "281": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "282": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "283": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "284": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "285": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "286": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "287": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "288": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "289": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "290": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "291": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "292": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "293": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "294": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "295": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "296": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "297": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "298": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "299": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "300": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "301": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "302": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "303": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "304": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "305": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "306": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "307": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "308": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "309": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "310": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "311": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "312": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "313": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "314": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "315": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "316": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "317": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "318": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "319": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "320": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "321": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "322": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "323": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "324": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "325": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "326": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "327": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "328": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "329": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "330": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "331": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "332": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "333": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "334": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "335": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "336": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "337": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "338": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "339": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "340": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "341": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "342": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "343": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "344": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "345": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "346": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "347": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "348": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "349": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "350": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "351": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "352": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "353": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "354": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "355": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "356": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "357": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "358": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "359": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "360": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "361": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "362": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "363": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "364": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "365": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "366": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "367": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "368": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "369": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "370": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "371": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "372": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "373": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "374": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "375": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "376": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "377": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "378": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "379": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "380": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "381": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "382": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "383": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + } + }, + "additional_special_tokens": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "clean_up_tokenization_spaces": false, + "eos_token": "", + "extra_ids": 0, + "extra_special_tokens": {}, + "model_max_length": 1000000000000000019884624838656, + "pad_token": "", + "tokenizer_class": "ByT5Tokenizer", + "unk_token": "" +} diff --git a/checkpoints/flash/flash_dpo/args.json b/checkpoints/flash/flash_dpo/args.json new file mode 100644 index 0000000000000000000000000000000000000000..21b2f5231dd8a30487d5b39483620b6db14983cd --- /dev/null +++ b/checkpoints/flash/flash_dpo/args.json @@ -0,0 +1 @@ +{"cmd": "train", "lm": "/home/dev/flash/runs/flash_lm/best.pt", "out": "/home/dev/flash/runs/flash_dpo", "shard": "0/1", "k": 4, "lr": 1e-06, "beta": 0.1, "sft": 0.1, "bs": 8, "epochs": 2} \ No newline at end of file diff --git a/checkpoints/flash/flash_dpo/best.pt b/checkpoints/flash/flash_dpo/best.pt new file mode 100644 index 0000000000000000000000000000000000000000..9befb1c57aa8102f186ad2ad804cc474dac06ca8 --- /dev/null +++ b/checkpoints/flash/flash_dpo/best.pt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f59a6fc0dacc331844cf8b513ec99e4d5c75f2583115037c8d38ba9ff7e3a307 +size 2047760470 diff --git a/checkpoints/flash/flash_dpo/config.json b/checkpoints/flash/flash_dpo/config.json new file mode 100644 index 0000000000000000000000000000000000000000..1b141563d94c515225915f8d3361906c84bc8768 --- /dev/null +++ b/checkpoints/flash/flash_dpo/config.json @@ -0,0 +1,9 @@ +{ + "engine": "cosyvoice3", + "base": "FunAudioLLM/Fun-CosyVoice3-0.5B-2512", + "rate": "normal", + "sampler": "silexcap", + "one_pass": true, + "sha256_best_pt": "f59a6fc0dacc331844cf8b513ec99e4d5c75f2583115037c8d38ba9ff7e3a307", + "training": "Flash SFT (CV3 LM, Phase 3 licensed manifest re-weighted + IgboSynCorp pseudo-transcripts) then DPO: 1,206 preference pairs, 286 steps, beta 0.1, lr 1e-6, val pref-acc 0.65" +} \ No newline at end of file diff --git a/checkpoints/flash/flash_dpo/log.jsonl b/checkpoints/flash/flash_dpo/log.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..156562f9c6640e4e35585eaa52fa5594d330d4be --- /dev/null +++ b/checkpoints/flash/flash_dpo/log.jsonl @@ -0,0 +1,31 @@ +{"step": 0, "val": {"acc": 0.0, "margin": 0.0}} +{"step": 10, "of": 286, "loss": 0.9068, "acc": 0.625, "margin": 0.046, "gn": 57.21, "min": 0.1} +{"step": 20, "of": 286, "loss": 0.9388, "acc": 0.5, "margin": -0.002, "gn": 66.55, "min": 0.1} +{"step": 30, "of": 286, "loss": 0.9699, "acc": 0.375, "margin": -0.035, "gn": 61.96, "min": 0.2} +{"step": 40, "of": 286, "loss": 0.938, "acc": 0.375, "margin": 0.011, "gn": 63.67, "min": 0.3} +{"step": 50, "of": 286, "loss": 0.8633, "acc": 0.875, "margin": 0.15, "gn": 52.25, "min": 0.3} +{"step": 60, "of": 286, "loss": 0.939, "acc": 0.625, "margin": 0.002, "gn": 63.83, "min": 0.4} +{"step": 70, "of": 286, "loss": 0.8483, "acc": 0.75, "margin": 0.189, "gn": 69.48, "min": 0.4} +{"step": 80, "of": 286, "loss": 1.0202, "acc": 0.375, "margin": -0.139, "gn": 80.15, "min": 0.5} +{"step": 90, "of": 286, "loss": 0.8866, "acc": 0.625, "margin": 0.142, "gn": 59.47, "min": 0.5} +{"step": 100, "of": 286, "loss": 0.8947, "acc": 0.625, "margin": 0.134, "gn": 60.78, "min": 0.6} +{"step": 110, "of": 286, "loss": 0.9459, "acc": 0.5, "margin": 0.027, "gn": 66.75, "min": 0.6} +{"step": 120, "of": 286, "loss": 1.0125, "acc": 0.375, "margin": -0.11, "gn": 65.48, "min": 0.7} +{"step": 130, "of": 286, "loss": 0.8009, "acc": 0.75, "margin": 0.365, "gn": 50.92, "min": 0.8} +{"step": 140, "of": 286, "loss": 0.8328, "acc": 0.75, "margin": 0.277, "gn": 60.21, "min": 0.8} +{"epoch": 1, "step": 143, "val": {"acc": 0.6, "margin": 0.15548671048794252}} +{"step": 150, "of": 286, "loss": 0.5242, "acc": 1.0, "margin": 1.345, "gn": 31.71, "min": 0.9} +{"step": 160, "of": 286, "loss": 0.476, "acc": 1.0, "margin": 1.509, "gn": 28.95, "min": 0.9} +{"step": 170, "of": 286, "loss": 0.5501, "acc": 1.0, "margin": 1.171, "gn": 29.98, "min": 1.0} +{"step": 180, "of": 286, "loss": 0.474, "acc": 1.0, "margin": 1.492, "gn": 26.95, "min": 1.0} +{"step": 190, "of": 286, "loss": 0.5711, "acc": 1.0, "margin": 1.032, "gn": 34.13, "min": 1.1} +{"step": 200, "of": 286, "loss": 0.5283, "acc": 1.0, "margin": 1.181, "gn": 32.39, "min": 1.1} +{"step": 210, "of": 286, "loss": 0.6212, "acc": 0.875, "margin": 0.86, "gn": 36.1, "min": 1.2} +{"step": 220, "of": 286, "loss": 0.5229, "acc": 1.0, "margin": 1.227, "gn": 32.65, "min": 1.2} +{"step": 230, "of": 286, "loss": 0.5555, "acc": 1.0, "margin": 1.367, "gn": 35.05, "min": 1.3} +{"step": 240, "of": 286, "loss": 0.5312, "acc": 1.0, "margin": 1.314, "gn": 36.6, "min": 1.3} +{"step": 250, "of": 286, "loss": 0.493, "acc": 1.0, "margin": 1.513, "gn": 28.32, "min": 1.3} +{"step": 260, "of": 286, "loss": 0.5076, "acc": 1.0, "margin": 1.31, "gn": 25.17, "min": 1.4} +{"step": 270, "of": 286, "loss": 0.434, "acc": 1.0, "margin": 1.739, "gn": 23.64, "min": 1.4} +{"step": 280, "of": 286, "loss": 0.4263, "acc": 1.0, "margin": 1.907, "gn": 24.99, "min": 1.5} +{"epoch": 2, "step": 286, "val": {"acc": 0.65, "margin": 0.17853533582141}} diff --git a/checkpoints/flash/flash_dpo/spk2id.json b/checkpoints/flash/flash_dpo/spk2id.json new file mode 100644 index 0000000000000000000000000000000000000000..99c495c8ea3d04d296083d029d8d329659f42ea0 --- /dev/null +++ b/checkpoints/flash/flash_dpo/spk2id.json @@ -0,0 +1 @@ +{"bible_hau": 0, "bible_yor": 1, "fleurs_eng_FEMALE_c000": 2, "fleurs_eng_FEMALE_c001": 3, "fleurs_eng_FEMALE_c002": 4, "fleurs_eng_FEMALE_c003": 5, "fleurs_eng_FEMALE_c004": 6, "fleurs_eng_FEMALE_c005": 7, "fleurs_eng_FEMALE_c006": 8, "fleurs_eng_FEMALE_c007": 9, "fleurs_eng_FEMALE_c008": 10, "fleurs_eng_FEMALE_c009": 11, "fleurs_eng_FEMALE_c010": 12, "fleurs_eng_FEMALE_c012": 13, "fleurs_eng_MALE_c000": 14, "fleurs_eng_MALE_c001": 15, "fleurs_eng_MALE_c002": 16, "fleurs_eng_MALE_c003": 17, "fleurs_eng_MALE_c004": 18, "fleurs_eng_MALE_c005": 19, "fleurs_eng_MALE_c006": 20, "fleurs_eng_MALE_c007": 21, "fleurs_hau_FEMALE_c000": 22, "fleurs_hau_FEMALE_c001": 23, "fleurs_hau_FEMALE_c003": 24, "fleurs_hau_FEMALE_misc": 25, "fleurs_hau_MALE_c000": 26, "fleurs_hau_MALE_c002": 27, "fleurs_hau_MALE_c003": 28, "fleurs_hau_MALE_c004": 29, "fleurs_hau_MALE_misc": 30, "fleurs_ibo_FEMALE_c000": 31, "fleurs_ibo_FEMALE_c001": 32, "fleurs_ibo_FEMALE_c003": 33, "fleurs_ibo_FEMALE_c004": 34, "fleurs_ibo_FEMALE_misc": 35, "fleurs_ibo_MALE_c000": 36, "fleurs_ibo_MALE_c001": 37, "fleurs_ibo_MALE_c002": 38, "fleurs_ibo_MALE_c004": 39, "fleurs_ibo_MALE_c005": 40, "fleurs_ibo_MALE_misc": 41, "fleurs_yor_FEMALE_c000": 42, "fleurs_yor_FEMALE_c001": 43, "fleurs_yor_FEMALE_c002": 44, "fleurs_yor_MALE_c000": 45, "fleurs_yor_MALE_c001": 46, "fleurs_yor_MALE_misc": 47, "igboapi_pspk00000": 48, "igboapi_pspk00001": 49, "igboapi_pspk00002": 50, "igboapi_pspk00003": 51, "igboapi_pspk00004": 52, "igboapi_pspk00005": 53, "igboapi_pspk00006": 54, "igboapi_pspk00007": 55, "igboapi_pspk00008": 56, "igboapi_pspk00009": 57, "igboapi_pspk00010": 58, "igboapi_pspk00011": 59, "igboapi_pspk00012": 60, "igboapi_pspk00013": 61, "igboapi_pspk00014": 62, "igboapi_pspk00015": 63, "igboapi_pspk00016": 64, "igboapi_pspk00017": 65, "igboapi_pspk00018": 66, "igboapi_pspk00019": 67, "igboapi_pspk00020": 68, "igboapi_pspk00022": 69, "igboapi_pspk00023": 70, "igboapi_pspk00024": 71, "igboapi_pspk00025": 72, "igboapi_pspk00026": 73, "igboapi_pspk00027": 74, "igboapi_pspk00028": 75, "igboapi_pspk00029": 76, "igboapi_pspk00030": 77, "igboapi_pspk00031": 78, "igboapi_pspk00032": 79, "igboapi_pspk00033": 80, "igboapi_pspk00034": 81, "igboapi_pspk00035": 82, "igboapi_pspk00036": 83, "igboapi_pspk00037": 84, "igboapi_pspk00038": 85, "igboapi_pspk00039": 86, "igboapi_pspk00040": 87, "igboapi_pspk00041": 88, "igboapi_pspk00042": 89, "igboapi_pspk00043": 90, "igboapi_pspk00044": 91, "igboapi_pspk00045": 92, "igboapi_pspk00046": 93, "igboapi_pspk00047": 94, "igboapi_pspk00048": 95, "igboapi_pspk00049": 96, "igboapi_pspk00050": 97, "igboapi_pspk00051": 98, "igboapi_pspk00052": 99, "igboapi_pspk00053": 100, "igboapi_pspk00054": 101, "igboapi_pspk00055": 102, "igboapi_pspk00056": 103, "igboapi_pspk00057": 104, "igboapi_pspk00058": 105, "igboapi_pspk00059": 106, "igboapi_pspk00060": 107, "igboapi_pspk00061": 108, "igboapi_pspk00062": 109, "igboapi_pspk00063": 110, "igboapi_pspk00064": 111, "igboapi_pspk00065": 112, "igboapi_pspk00066": 113, "igboapi_pspk00067": 114, "igboapi_pspk00068": 115, "igboapi_pspk00069": 116, "igboapi_pspk00070": 117, "igboapi_pspk00071": 118, "igboapi_pspk00072": 119, "igboapi_pspk00073": 120, "igboapi_pspk00074": 121, "igboapi_pspk00075": 122, "igboapi_pspk00076": 123, "igboapi_pspk00077": 124, "igboapi_pspk00078": 125, "igboapi_pspk00079": 126, "igboapi_pspk00080": 127, "igboapi_pspk00081": 128, "igboapi_pspk00082": 129, "igboapi_pspk00083": 130, "igboapi_pspk00084": 131, "igboapi_pspk00085": 132, "igboapi_pspk00086": 133, "igboapi_pspk00087": 134, "igboapi_pspk00088": 135, "igboapi_pspk00090": 136, "igboapi_pspk00091": 137, "igboapi_pspk00092": 138, "igboapi_pspk00093": 139, "igboapi_pspk00094": 140, "igboapi_pspk00095": 141, "igboapi_pspk00096": 142, "igboapi_pspk00097": 143, "igboapi_pspk00098": 144, "igboapi_pspk00099": 145, "igboapi_pspk00100": 146, "igboapi_pspk00101": 147, "igboapi_pspk00102": 148, "igboapi_pspk00103": 149, "igboapi_pspk00104": 150, "igboapi_pspk00105": 151, "igboapi_pspk00106": 152, "igboapi_pspk00107": 153, "igboapi_pspk00108": 154, "igboapi_pspk00109": 155, "igboapi_pspk00110": 156, "igboapi_pspk00111": 157, "igboapi_pspk00112": 158, "igboapi_pspk00113": 159, "igboapi_pspk00114": 160, "igboapi_pspk00115": 161, "igboapi_pspk00116": 162, "igboapi_pspk00117": 163, "igboapi_pspk00118": 164, "igboapi_pspk00119": 165, "igboapi_pspk00120": 166, "igboapi_pspk00121": 167, "igboapi_pspk00122": 168, "igboapi_pspk00123": 169, "igboapi_pspk00124": 170, "igboapi_pspk00125": 171, "igboapi_pspk00126": 172, "igboapi_pspk00127": 173, "igboapi_pspk00128": 174, "igboapi_pspk00130": 175, "igboapi_pspk00131": 176, "igboapi_pspk00132": 177, "igboapi_pspk00133": 178, "igboapi_pspk00134": 179, "igboapi_pspk00135": 180, "igboapi_pspk00136": 181, "igboapi_pspk00137": 182, "igboapi_pspk00138": 183, "igboapi_pspk00139": 184, "igboapi_pspk00140": 185, "igboapi_pspk00141": 186, "igboapi_pspk00142": 187, "igboapi_pspk00143": 188, "igboapi_pspk00144": 189, "igboapi_pspk00145": 190, "igboapi_pspk00146": 191, "igboapi_pspk00147": 192, "igboapi_pspk00148": 193, "igboapi_pspk00149": 194, "igboapi_pspk00150": 195, "igboapi_pspk00151": 196, "igboapi_pspk00152": 197, "igboapi_pspk00153": 198, "igboapi_pspk00154": 199, "igboapi_pspk00155": 200, "igboapi_pspk00156": 201, "igboapi_pspk00157": 202, "igboapi_pspk00158": 203, "igboapi_pspk00159": 204, "igboapi_pspk00160": 205, "igboapi_pspk00161": 206, "igboapi_pspk00162": 207, "igboapi_pspk00163": 208, "igboapi_pspk00164": 209, "igboapi_pspk00165": 210, "igboapi_pspk00166": 211, "igboapi_pspk00167": 212, "igboapi_pspk00168": 213, "igboapi_pspk00169": 214, "igboapi_pspk00170": 215, "igboapi_pspk00171": 216, "igboapi_pspk00172": 217, "igboapi_pspk00173": 218, "igboapi_pspk00174": 219, "igboapi_pspk00175": 220, "igboapi_pspk00176": 221, "igboapi_pspk00177": 222, "igboapi_pspk00178": 223, "igboapi_pspk00179": 224, "igboapi_pspk00180": 225, "igboapi_pspk00181": 226, "igboapi_pspk00182": 227, "igboapi_pspk00183": 228, "igboapi_pspk00184": 229, "igboapi_pspk00185": 230, "igboapi_pspk00186": 231, "igboapi_pspk00187": 232, "igboapi_pspk00188": 233, "igboapi_pspk00189": 234, "igboapi_pspk00190": 235, "igboapi_pspk00191": 236, "igboapi_pspk00192": 237, "igboapi_pspk00193": 238, "igboapi_pspk00194": 239, "igboapi_pspk00195": 240, "igboapi_pspk00196": 241, "igboapi_pspk00197": 242, "igboapi_pspk00198": 243, "igboapi_pspk00199": 244, "igboapi_pspk00200": 245, "igboapi_pspk00201": 246, "igboapi_pspk00202": 247, "igboapi_pspk00203": 248, "igboapi_pspk00204": 249, "igboapi_pspk00205": 250, "igboapi_pspk00206": 251, "igboapi_pspk00207": 252, "igboapi_pspk00208": 253, "igboapi_pspk00209": 254, "igboapi_pspk00210": 255, "igboapi_pspk00211": 256, "igboapi_pspk00212": 257, "igboapi_pspk00213": 258, "igboapi_pspk00214": 259, "igboapi_pspk00215": 260, "igboapi_pspk00216": 261, "igboapi_pspk00217": 262, "igboapi_pspk00218": 263, "igboapi_pspk00219": 264, "igboapi_pspk00220": 265, "igboapi_pspk00221": 266, "igboapi_pspk00222": 267, "igboapi_pspk00223": 268, "igboapi_pspk00224": 269, "igboapi_pspk00225": 270, "igboapi_pspk00226": 271, "igboapi_pspk00227": 272, "igboapi_pspk00228": 273, "igboapi_pspk00229": 274, "igboapi_pspk00230": 275, "igboapi_pspk00231": 276, "igboapi_pspk00232": 277, "igboapi_pspk00233": 278, "igboapi_pspk00234": 279, "igboapi_pspk00235": 280, "igboapi_pspk00236": 281, "igboapi_pspk00237": 282, "igboapi_pspk00238": 283, "igboapi_pspk00239": 284, "igboapi_pspk00240": 285, "igboapi_pspk00241": 286, "igboapi_pspk00242": 287, "igboapi_pspk00243": 288, "igboapi_pspk00244": 289, "igboapi_pspk00245": 290, "igboapi_pspk00246": 291, "igboapi_pspk00247": 292, "igboapi_pspk00248": 293, "igboapi_pspk00249": 294, "igboapi_pspk00250": 295, "igboapi_pspk00251": 296, "igboapi_pspk00252": 297, "igboapi_pspk00253": 298, "igboapi_pspk00254": 299, "igboapi_pspk00255": 300, "igboapi_pspk00256": 301, "igboapi_pspk00257": 302, "igboapi_pspk00258": 303, "igboapi_pspk00259": 304, "igboapi_pspk00260": 305, "igboapi_pspk00261": 306, "igboapi_pspk00262": 307, "igboapi_pspk00263": 308, "igboapi_pspk00264": 309, "igboapi_pspk00265": 310, "igboapi_pspk00266": 311, "igboapi_pspk00267": 312, "igboapi_pspk00268": 313, "igboapi_pspk00269": 314, "igboapi_pspk00270": 315, "igboapi_pspk00271": 316, "igboapi_pspk00272": 317, "igboapi_pspk00273": 318, "igboapi_pspk00274": 319, "igboapi_pspk00275": 320, "igboapi_pspk00276": 321, "igboapi_pspk00277": 322, "igboapi_pspk00278": 323, "igboapi_pspk00279": 324, "igboapi_pspk00280": 325, "igboapi_pspk00281": 326, "igboapi_pspk00282": 327, "igboapi_pspk00283": 328, "igboapi_pspk00284": 329, "igboapi_pspk00285": 330, "igboapi_pspk00286": 331, "igboapi_pspk00287": 332, "igboapi_pspk00288": 333, "igboapi_pspk00289": 334, "igboapi_pspk00290": 335, "igboapi_pspk00291": 336, "igboapi_pspk00292": 337, "igboapi_pspk00293": 338, "igboapi_pspk00294": 339, "igboapi_pspk00295": 340, "igboapi_pspk00296": 341, "igboapi_pspk00297": 342, "igboapi_pspk00298": 343, "igboapi_pspk00299": 344, "igboapi_pspk00300": 345, "igboapi_pspk00301": 346, "igboapi_pspk00302": 347, "igboapi_pspk00303": 348, "igboapi_pspk00304": 349, "igboapi_pspk00305": 350, "igboapi_pspk00306": 351, "igboapi_pspk00307": 352, "igboapi_pspk00308": 353, "igboapi_pspk00309": 354, "igboapi_pspk00310": 355, "igboapi_pspk00311": 356, "igboapi_pspk00312": 357, "igboapi_pspk00313": 358, "igboapi_pspk00314": 359, "igboapi_pspk00315": 360, "igboapi_pspk00316": 361, "igboapi_pspk00317": 362, "igboapi_pspk00318": 363, "igboapi_pspk00319": 364, "igboapi_pspk00320": 365, "igboapi_pspk00321": 366, "igboapi_pspk00322": 367, "igboapi_pspk00323": 368, "igboapi_pspk00324": 369, "igboapi_pspk00325": 370, "igboapi_pspk00326": 371, "igboapi_pspk00327": 372, "igboapi_pspk00328": 373, "igboapi_pspk00329": 374, "igboapi_pspk00330": 375, "igboapi_pspk00331": 376, "igboapi_pspk00332": 377, "igboapi_pspk00333": 378, "igboapi_pspk00335": 379, "igboapi_pspk00336": 380, "igboapi_pspk00337": 381, "igboapi_pspk00338": 382, "igboapi_pspk00339": 383, "igboapi_pspk00340": 384, "igboapi_pspk00341": 385, "igboapi_pspk00342": 386, "igboapi_pspk00343": 387, "igboapi_pspk00344": 388, "igboapi_pspk00345": 389, "igboapi_pspk00346": 390, "igboapi_pspk00347": 391, "igboapi_pspk00348": 392, "igboapi_pspk00349": 393, "igboapi_pspk00350": 394, "igboapi_pspk00351": 395, "igboapi_pspk00352": 396, "igboapi_pspk00353": 397, "igboapi_pspk00354": 398, "igboapi_pspk00355": 399, "igboapi_pspk00356": 400, "igboapi_pspk00357": 401, "igboapi_pspk00358": 402, "igboapi_pspk00359": 403, "igboapi_pspk00360": 404, "igboapi_pspk00361": 405, "igboapi_pspk00362": 406, "igboapi_pspk00363": 407, "igboapi_pspk00364": 408, "igboapi_pspk00365": 409, "igboapi_pspk00366": 410, "igboapi_pspk00367": 411, "igboapi_pspk00368": 412, "igboapi_pspk00369": 413, "igboapi_pspk00370": 414, "igboapi_pspk00371": 415, "igboapi_pspk00372": 416, "igboapi_pspk00373": 417, "igboapi_pspk00374": 418, "igboapi_pspk00375": 419, "igboapi_pspk00376": 420, "igboapi_pspk00377": 421, "igboapi_pspk00378": 422, "igboapi_pspk00379": 423, "igboapi_pspk00380": 424, "igboapi_pspk00381": 425, "igboapi_pspk00382": 426, "igboapi_pspk00383": 427, "igboapi_pspk00384": 428, "igboapi_pspk00385": 429, "igboapi_pspk00386": 430, "igboapi_pspk00387": 431, "igboapi_pspk00388": 432, "igboapi_pspk00389": 433, "igboapi_pspk00390": 434, "igboapi_pspk00391": 435, "igboapi_pspk00392": 436, "igboapi_pspk00393": 437, "igboapi_pspk00394": 438, "igboapi_pspk00395": 439, "igboapi_pspk00396": 440, "igboapi_pspk00397": 441, "igboapi_pspk00398": 442, "igboapi_pspk00399": 443, "igboapi_pspk00400": 444, "igboapi_pspk00401": 445, "igboapi_pspk00402": 446, "igboapi_pspk00403": 447, "igboapi_pspk00404": 448, "igboapi_pspk00405": 449, "igboapi_pspk00406": 450, "igboapi_pspk00407": 451, "igboapi_pspk00408": 452, "igboapi_pspk00409": 453, "igboapi_pspk00410": 454, "igboapi_pspk00411": 455, "igboapi_pspk00412": 456, "igboapi_pspk00413": 457, "igboapi_pspk00414": 458, "igboapi_pspk00415": 459, "igboapi_pspk00416": 460, "igboapi_pspk00417": 461, "igboapi_pspk00418": 462, "igboapi_pspk00419": 463, "igboapi_pspk00420": 464, "igboapi_pspk00421": 465, "igboapi_pspk00422": 466, "igboapi_pspk00423": 467, "igboapi_pspk00424": 468, "igboapi_pspk00425": 469, "igboapi_pspk00426": 470, "igboapi_pspk00427": 471, "igboapi_pspk00428": 472, "igboapi_pspk00429": 473, "igboapi_pspk00430": 474, "igboapi_pspk00431": 475, "igboapi_pspk00433": 476, "igboapi_pspk00434": 477, "igboapi_pspk00435": 478, "igboapi_pspk00436": 479, "igboapi_pspk00438": 480, "igboapi_pspk00439": 481, "igboapi_pspk00440": 482, "igboapi_pspk00441": 483, "igboapi_pspk00442": 484, "igboapi_pspk00443": 485, "igboapi_pspk00444": 486, "igboapi_pspk00445": 487, "igboapi_pspk00446": 488, "igboapi_pspk00447": 489, "igboapi_pspk00448": 490, "igboapi_pspk00449": 491, "igboapi_pspk00450": 492, "igboapi_pspk00451": 493, "igboapi_pspk00452": 494, "igboapi_pspk00453": 495, "igboapi_pspk00454": 496, "igboapi_pspk00455": 497, "igboapi_pspk00456": 498, "igboapi_pspk00457": 499, "igboapi_pspk00458": 500, "igboapi_pspk00459": 501, "igboapi_pspk00460": 502, "igboapi_pspk00461": 503, "igboapi_pspk00462": 504, "igboapi_pspk00463": 505, "igboapi_pspk00464": 506, "igboapi_pspk00465": 507, "igboapi_pspk00466": 508, "igboapi_pspk00467": 509, "igboapi_pspk00468": 510, "igboapi_pspk00469": 511, "igboapi_pspk00470": 512, "igboapi_pspk00471": 513, "igboapi_pspk00472": 514, "igboapi_pspk00473": 515, "igboapi_pspk00474": 516, "igboapi_pspk00475": 517, "igboapi_pspk00476": 518, "igboapi_pspk00477": 519, "igboapi_pspk00478": 520, "igboapi_pspk00479": 521, "igboapi_pspk00480": 522, "igboapi_pspk00481": 523, "igboapi_pspk00482": 524, "igboapi_pspk00483": 525, "igboapi_pspk00484": 526, "igboapi_pspk00485": 527, "igboapi_pspk00486": 528, "igboapi_pspk00487": 529, "igboapi_pspk00488": 530, "igboapi_pspk00489": 531, "igboapi_pspk00490": 532, "igboapi_pspk00491": 533, "igboapi_pspk00492": 534, "igboapi_pspk00493": 535, "igboapi_pspk00494": 536, "igboapi_pspk00495": 537, "igboapi_pspk00496": 538, "igboapi_pspk00497": 539, "igboapi_pspk00498": 540, "igboapi_pspk00499": 541, "igboapi_pspk00500": 542, "igboapi_pspk00501": 543, "igboapi_pspk00502": 544, "igboapi_pspk00503": 545, "igboapi_pspk00504": 546, "igboapi_pspk00505": 547, "igboapi_pspk00506": 548, "igboapi_pspk00507": 549, "igboapi_pspk00508": 550, "igboapi_pspk00509": 551, "igboapi_pspk00510": 552, "igboapi_pspk00511": 553, "igboapi_pspk00512": 554, "igboapi_pspk00513": 555, "igboapi_pspk00516": 556, "igboapi_pspk00517": 557, "igboapi_pspk00518": 558, "igboapi_pspk00519": 559, "igboapi_pspk00520": 560, "igboapi_pspk00521": 561, "igboapi_pspk00522": 562, "igboapi_pspk00523": 563, "igboapi_pspk00524": 564, "igboapi_pspk00525": 565, "igboapi_pspk00526": 566, "igboapi_pspk00527": 567, "igboapi_pspk00528": 568, "igboapi_pspk00529": 569, "igboapi_pspk00530": 570, "igboapi_pspk00531": 571, "igboapi_pspk00532": 572, "igboapi_pspk00533": 573, "igboapi_pspk00534": 574, "igboapi_pspk00535": 575, "igboapi_pspk00536": 576, "igboapi_pspk00537": 577, "igboapi_pspk00538": 578, "igboapi_pspk00539": 579, "igboapi_pspk00540": 580, "igboapi_pspk00541": 581, "igboapi_pspk00542": 582, "igboapi_pspk00543": 583, "igboapi_pspk00544": 584, "igboapi_pspk00545": 585, "igboapi_pspk00546": 586, "igboapi_pspk00547": 587, "igboapi_pspk00548": 588, "igboapi_pspk00549": 589, "igboapi_pspk00550": 590, "igboapi_pspk00551": 591, "igboapi_pspk00552": 592, "igboapi_pspk00553": 593, "igboapi_pspk00554": 594, "igboapi_pspk00555": 595, "igboapi_pspk00556": 596, "igboapi_pspk00557": 597, "igboapi_pspk00558": 598, "igboapi_pspk00559": 599, "igboapi_pspk00560": 600, "igboapi_pspk00561": 601, "igboapi_pspk00562": 602, "igboapi_pspk00563": 603, "igboapi_pspk00564": 604, "igboapi_pspk00565": 605, "igboapi_pspk00566": 606, "igboapi_pspk00567": 607, "igboapi_pspk00568": 608, "igboapi_pspk00569": 609, "igboapi_pspk00570": 610, "igboapi_pspk00571": 611, "igboapi_pspk00572": 612, "igboapi_pspk00573": 613, "igboapi_pspk00574": 614, "igboapi_pspk00575": 615, "igboapi_pspk00576": 616, "igboapi_pspk00577": 617, "igboapi_pspk00578": 618, "igboapi_pspk00579": 619, "igboapi_pspk00580": 620, "igboapi_pspk00581": 621, "igboapi_pspk00582": 622, "igboapi_pspk00583": 623, "igboapi_pspk00584": 624, "igboapi_pspk00585": 625, "igboapi_pspk00586": 626, "igboapi_pspk00587": 627, "igboapi_pspk00588": 628, "igboapi_pspk00589": 629, "igboapi_pspk00590": 630, "igboapi_pspk00591": 631, "igboapi_pspk00592": 632, "igboapi_pspk00593": 633, "igboapi_pspk00594": 634, "igboapi_pspk00595": 635, "igboapi_pspk00596": 636, "igboapi_pspk00597": 637, "igboapi_pspk00598": 638, "igboapi_pspk00599": 639, "igboapi_pspk00600": 640, "igboapi_pspk00601": 641, "igboapi_pspk00602": 642, "igboapi_pspk00603": 643, "igboapi_pspk00604": 644, "igboapi_pspk00605": 645, "igboapi_pspk00606": 646, "igboapi_pspk00607": 647, "igboapi_pspk00608": 648, "igboapi_pspk00609": 649, "igboapi_pspk00610": 650, "igboapi_pspk00611": 651, "igboapi_pspk00612": 652, "igboapi_pspk00613": 653, "igboapi_pspk00614": 654, "igboapi_pspk00615": 655, "igboapi_pspk00616": 656, "igboapi_pspk00617": 657, "igboapi_pspk00618": 658, "igboapi_pspk00619": 659, "igboapi_pspk00620": 660, "igboapi_pspk00621": 661, "igboapi_pspk00622": 662, "igboapi_pspk00623": 663, "igboapi_pspk00624": 664, "igboapi_pspk00625": 665, "igboapi_pspk00626": 666, "igboapi_pspk00627": 667, "igboapi_pspk00628": 668, "igboapi_pspk00629": 669, "igboapi_pspk00630": 670, "igboapi_pspk00631": 671, "igboapi_pspk00632": 672, "igboapi_pspk00633": 673, "igboapi_pspk00634": 674, "igboapi_pspk00635": 675, "igboapi_pspk00636": 676, "igboapi_pspk00637": 677, "igboapi_pspk00638": 678, "igboapi_pspk00639": 679, "igboapi_pspk00640": 680, "igboapi_pspk00641": 681, "igboapi_pspk00642": 682, "igboapi_pspk00643": 683, "igboapi_pspk00644": 684, "igboapi_pspk00645": 685, "igboapi_pspk00646": 686, "igboapi_pspk00647": 687, "igboapi_pspk00648": 688, "igboapi_pspk00649": 689, "igboapi_pspk00650": 690, "igboapi_pspk00651": 691, "igboapi_pspk00652": 692, "igboapi_pspk00653": 693, "igboapi_pspk00654": 694, "igboapi_pspk00655": 695, "igboapi_pspk00656": 696, "igboapi_pspk00657": 697, "igboapi_pspk00658": 698, "igboapi_pspk00659": 699, "igboapi_pspk00660": 700, "igboapi_pspk00661": 701, "igboapi_pspk00662": 702, "igboapi_pspk00663": 703, "igboapi_pspk00664": 704, "igboapi_pspk00665": 705, "igboapi_pspk00666": 706, "igboapi_pspk00667": 707, "igboapi_pspk00668": 708, "igboapi_pspk00669": 709, "igboapi_pspk00670": 710, "igboapi_pspk00671": 711, "igboapi_pspk00672": 712, "igboapi_pspk00673": 713, "igboapi_pspk00674": 714, "igboapi_pspk00675": 715, "igboapi_pspk00676": 716, "igboapi_pspk00677": 717, "igboapi_pspk00678": 718, "igboapi_pspk00679": 719, "igboapi_pspk00680": 720, "igboapi_pspk00681": 721, "igboapi_pspk00682": 722, "igboapi_pspk00683": 723, "igboapi_pspk00684": 724, "igboapi_pspk00685": 725, "igboapi_pspk00686": 726, "igboapi_pspk00687": 727, "igboapi_pspk00688": 728, "igboapi_pspk00689": 729, "igboapi_pspk00690": 730, "igboapi_pspk00691": 731, "igboapi_pspk00692": 732, "igboapi_pspk00693": 733, "igboapi_pspk00694": 734, "igboapi_pspk00696": 735, "igboapi_pspk00697": 736, "igboapi_pspk00698": 737, "igboapi_pspk00699": 738, "igboapi_pspk00700": 739, "igboapi_pspk00701": 740, "igboapi_pspk00702": 741, "igboapi_pspk00703": 742, "igboapi_pspk00704": 743, "igboapi_pspk00705": 744, "igboapi_pspk00706": 745, "igboapi_pspk00707": 746, "igboapi_pspk00708": 747, "igboapi_pspk00709": 748, "igboapi_pspk00710": 749, "igboapi_pspk00711": 750, "igboapi_pspk00712": 751, "igboapi_pspk00713": 752, "igboapi_pspk00714": 753, "igboapi_pspk00715": 754, "igboapi_pspk00716": 755, "igboapi_pspk00717": 756, "igboapi_pspk00718": 757, "igboapi_pspk00719": 758, "igboapi_pspk00720": 759, "igboapi_pspk00721": 760, "igboapi_pspk00722": 761, "igboapi_pspk00723": 762, "igboapi_pspk00724": 763, "igboapi_pspk00725": 764, "igboapi_pspk00726": 765, "igboapi_pspk00727": 766, "igboapi_pspk00728": 767, "igboapi_pspk00729": 768, "igboapi_pspk00730": 769, "igboapi_pspk00731": 770, "igboapi_pspk00732": 771, "igboapi_pspk00733": 772, "igboapi_pspk00734": 773, "igboapi_pspk00735": 774, "igboapi_pspk00736": 775, "igboapi_pspk00737": 776, "igboapi_pspk00738": 777, "igboapi_pspk00739": 778, "igboapi_pspk00740": 779, "igboapi_pspk00741": 780, "igboapi_pspk00742": 781, "igboapi_pspk00743": 782, "igboapi_pspk00744": 783, "igboapi_pspk00745": 784, "igboapi_pspk00746": 785, "igboapi_pspk00747": 786, "igboapi_pspk00748": 787, "igboapi_pspk00749": 788, "igboapi_pspk00750": 789, "igboapi_pspk00751": 790, "igboapi_pspk00752": 791, "igboapi_pspk00754": 792, "igboapi_pspk00755": 793, "igboapi_pspk00756": 794, "igboapi_pspk00757": 795, "igboapi_pspk00758": 796, "igboapi_pspk00759": 797, "igboapi_pspk00760": 798, "igboapi_pspk00761": 799, "igboapi_pspk00762": 800, "igboapi_pspk00763": 801, "igboapi_pspk00764": 802, "igboapi_pspk00765": 803, "igboapi_pspk00766": 804, "igboapi_pspk00767": 805, "igboapi_pspk00768": 806, "igboapi_pspk00769": 807, "igboapi_pspk00770": 808, "igboapi_pspk00771": 809, "igboapi_pspk00772": 810, "igboapi_pspk00773": 811, "igboapi_pspk00774": 812, "igboapi_pspk00775": 813, "igboapi_pspk00776": 814, "igboapi_pspk00777": 815, "igboapi_pspk00778": 816, "igboapi_pspk00779": 817, "igboapi_pspk00780": 818, "igboapi_pspk00781": 819, "igboapi_pspk00782": 820, "igboapi_pspk00783": 821, "igboapi_pspk00784": 822, "igboapi_pspk00785": 823, "igboapi_pspk00786": 824, "igboapi_pspk00787": 825, "igboapi_pspk00788": 826, "igboapi_pspk00789": 827, "igboapi_pspk00790": 828, "igboapi_pspk00791": 829, "igboapi_pspk00792": 830, "igboapi_pspk00793": 831, "igboapi_pspk00794": 832, "igboapi_pspk00795": 833, "igboapi_pspk00796": 834, "igboapi_pspk00797": 835, "igboapi_pspk00798": 836, "igboapi_pspk00799": 837, "igboapi_pspk00800": 838, "igboapi_pspk00801": 839, "igboapi_pspk00803": 840, "igboapi_pspk00804": 841, "igboapi_pspk00805": 842, "igboapi_pspk00806": 843, "igboapi_pspk00807": 844, "igboapi_pspk00808": 845, "igboapi_pspk00809": 846, "igboapi_pspk00810": 847, "igboapi_pspk00811": 848, "igboapi_pspk00812": 849, "igboapi_pspk00813": 850, "igboapi_pspk00814": 851, "igboapi_pspk00815": 852, "igboapi_pspk00816": 853, "igboapi_pspk00817": 854, "igboapi_pspk00818": 855, "igboapi_pspk00819": 856, "igboapi_pspk00820": 857, "igboapi_pspk00821": 858, "igboapi_pspk00822": 859, "igboapi_pspk00824": 860, "igboapi_pspk00825": 861, "igboapi_pspk00826": 862, "igboapi_pspk00827": 863, "igboapi_pspk00828": 864, "igboapi_pspk00829": 865, "igboapi_pspk00831": 866, "igboapi_pspk00832": 867, "igboapi_pspk00833": 868, "igboapi_pspk00834": 869, "igboapi_pspk00835": 870, "igboapi_pspk00836": 871, "igboapi_pspk00837": 872, "igboapi_pspk00838": 873, "igboapi_pspk00839": 874, "igboapi_pspk00840": 875, "igboapi_pspk00841": 876, "igboapi_pspk00842": 877, "igboapi_pspk00843": 878, "igboapi_pspk00844": 879, "igboapi_pspk00845": 880, "igboapi_pspk00846": 881, "igboapi_pspk00847": 882, "igboapi_pspk00848": 883, "igboapi_pspk00849": 884, "igboapi_pspk00850": 885, "igboapi_pspk00851": 886, "igboapi_pspk00852": 887, "igboapi_pspk00853": 888, "igboapi_pspk00854": 889, "igboapi_pspk00855": 890, "igboapi_pspk00856": 891, "igboapi_pspk00857": 892, "igboapi_pspk00858": 893, "igboapi_pspk00859": 894, "igboapi_pspk00860": 895, "igboapi_pspk00861": 896, "igboapi_pspk00862": 897, "igboapi_pspk00863": 898, "igboapi_pspk00864": 899, "igboapi_pspk00865": 900, "igboapi_pspk00866": 901, "igboapi_pspk00867": 902, "igboapi_pspk00868": 903, "igboapi_pspk00869": 904, "igboapi_pspk00870": 905, "igboapi_pspk00871": 906, "igboapi_pspk00872": 907, "igboapi_pspk00873": 908, "igboapi_pspk00874": 909, "igboapi_pspk00875": 910, "igboapi_pspk00876": 911, "igboapi_pspk00877": 912, "igboapi_pspk00878": 913, "igboapi_pspk00879": 914, "igboapi_pspk00880": 915, "igboapi_pspk00881": 916, "igboapi_pspk00882": 917, "igboapi_pspk00883": 918, "igboapi_pspk00884": 919, "igboapi_pspk00885": 920, "igboapi_pspk00886": 921, "igboapi_pspk00887": 922, "igboapi_pspk00888": 923, "igboapi_pspk00889": 924, "igboapi_pspk00890": 925, "igboapi_pspk00891": 926, "igboapi_pspk00892": 927, "igboapi_pspk00893": 928, "igboapi_pspk00894": 929, "igboapi_pspk00895": 930, "igboapi_pspk00896": 931, "igboapi_pspk00897": 932, "igboapi_pspk00898": 933, "igboapi_pspk00899": 934, "igboapi_pspk00900": 935, "igboapi_pspk00901": 936, "igboapi_pspk00902": 937, "igboapi_pspk00903": 938, "igboapi_pspk00904": 939, "igboapi_pspk00905": 940, "igboapi_pspk00906": 941, "igboapi_pspk00907": 942, "igboapi_pspk00908": 943, "igboapi_pspk00909": 944, "igboapi_pspk00910": 945, "igboapi_pspk00911": 946, "igboapi_pspk00912": 947, "igboapi_pspk00913": 948, "igboapi_pspk00914": 949, "igboapi_pspk00915": 950, "igboapi_pspk00916": 951, "igboapi_pspk00917": 952, "igboapi_pspk00918": 953, "igboapi_pspk00919": 954, "igboapi_pspk00920": 955, "igboapi_pspk00921": 956, "igboapi_pspk00922": 957, "igboapi_pspk00923": 958, "igboapi_pspk00924": 959, "igboapi_pspk00925": 960, "igboapi_pspk00926": 961, "igboapi_pspk00927": 962, "igboapi_pspk00928": 963, "igboapi_pspk00929": 964, "igboapi_pspk00930": 965, "igboapi_pspk00931": 966, "igboapi_pspk00932": 967, "igboapi_pspk00933": 968, "igboapi_pspk00934": 969, "igboapi_pspk00935": 970, "igboapi_pspk00936": 971, "igboapi_pspk00937": 972, "igboapi_pspk00938": 973, "igboapi_pspk00939": 974, "igboapi_pspk00940": 975, "igboapi_pspk00941": 976, "igboapi_pspk00942": 977, "igboapi_pspk00943": 978, "igboapi_pspk00944": 979, "igboapi_pspk00945": 980, "igboapi_pspk00946": 981, "igboapi_pspk00947": 982, "igboapi_pspk00948": 983, "igboapi_pspk00949": 984, "igboapi_pspk00950": 985, "igboapi_pspk00951": 986, "igboapi_pspk00952": 987, "igboapi_pspk00953": 988, "igboapi_pspk00954": 989, "igboapi_pspk00955": 990, "igboapi_pspk00956": 991, "igboapi_pspk00957": 992, "igboapi_pspk00958": 993, "igboapi_pspk00959": 994, "igboapi_pspk00960": 995, "igboapi_pspk00961": 996, "igboapi_pspk00962": 997, "igboapi_pspk00963": 998, "igboapi_pspk00964": 999, "igboapi_pspk00965": 1000, "igboapi_pspk00966": 1001, "igboapi_pspk00967": 1002, "igboapi_pspk00968": 1003, "igboapi_pspk00969": 1004, "igboapi_pspk00970": 1005, "igboapi_pspk00971": 1006, "igboapi_pspk00972": 1007, "igboapi_pspk00973": 1008, "igboapi_pspk00974": 1009, "igboapi_pspk00975": 1010, "igboapi_pspk00976": 1011, "igboapi_pspk00977": 1012, "igboapi_pspk00978": 1013, "igboapi_pspk00979": 1014, "igboapi_pspk00980": 1015, "igboapi_pspk00981": 1016, "igboapi_pspk00982": 1017, "igboapi_pspk00983": 1018, "igboapi_pspk00984": 1019, "igboapi_pspk00985": 1020, "igboapi_pspk00986": 1021, "igboapi_pspk00987": 1022, "igboapi_pspk00988": 1023, "igboapi_pspk00989": 1024, "igboapi_pspk00990": 1025, "igboapi_pspk00991": 1026, "igboapi_pspk00992": 1027, "igboapi_pspk00993": 1028, "igboapi_pspk00994": 1029, "igboapi_pspk00995": 1030, "igboapi_pspk00996": 1031, "igboapi_pspk00997": 1032, "igboapi_pspk00998": 1033, "igboapi_pspk00999": 1034, "igboapi_pspk01000": 1035, "igboapi_pspk01001": 1036, "igboapi_pspk01002": 1037, "igboapi_pspk01003": 1038, "igboapi_pspk01004": 1039, "igboapi_pspk01005": 1040, "igboapi_pspk01006": 1041, "igboapi_pspk01007": 1042, "igboapi_pspk01008": 1043, "igboapi_pspk01009": 1044, "igboapi_pspk01010": 1045, "igboapi_pspk01011": 1046, "igboapi_pspk01012": 1047, "igboapi_pspk01013": 1048, "igboapi_pspk01014": 1049, "igboapi_pspk01015": 1050, "igboapi_pspk01016": 1051, "igboapi_pspk01017": 1052, "igboapi_pspk01018": 1053, "igboapi_pspk01019": 1054, "igboapi_pspk01020": 1055, "igboapi_pspk01021": 1056, "igboapi_pspk01022": 1057, "igboapi_pspk01023": 1058, "igboapi_pspk01024": 1059, "igboapi_pspk01025": 1060, "igboapi_pspk01026": 1061, "igboapi_pspk01027": 1062, "igboapi_pspk01028": 1063, "igboapi_pspk01029": 1064, "igboapi_pspk01030": 1065, "igboapi_pspk01031": 1066, "igboapi_pspk01032": 1067, "igboapi_pspk01033": 1068, "igboapi_pspk01034": 1069, "igboapi_pspk01035": 1070, "igboapi_pspk01036": 1071, "igboapi_pspk01037": 1072, "igboapi_pspk01038": 1073, "igboapi_pspk01039": 1074, "igboapi_pspk01040": 1075, "igboapi_pspk01041": 1076, "igboapi_pspk01042": 1077, "igboapi_pspk01043": 1078, "igboapi_pspk01044": 1079, "igboapi_pspk01045": 1080, "igboapi_pspk01046": 1081, "igboapi_pspk01047": 1082, "igboapi_pspk01048": 1083, "igboapi_pspk01049": 1084, "igboapi_pspk01050": 1085, "igboapi_pspk01051": 1086, "igboapi_pspk01052": 1087, "igboapi_pspk01053": 1088, "igboapi_pspk01054": 1089, "igboapi_pspk01055": 1090, "igboapi_pspk01056": 1091, "igboapi_pspk01057": 1092, "igboapi_pspk01059": 1093, "igboapi_pspk01061": 1094, "igboapi_pspk01062": 1095, "igboapi_pspk01063": 1096, "igboapi_pspk01064": 1097, "igboapi_pspk01065": 1098, "igboapi_pspk01066": 1099, "igboapi_pspk01067": 1100, "igboapi_pspk01068": 1101, "igboapi_pspk01069": 1102, "igboapi_pspk01070": 1103, "igboapi_pspk01071": 1104, "igboapi_pspk01072": 1105, "igboapi_pspk01073": 1106, "igboapi_pspk01074": 1107, "igboapi_pspk01075": 1108, "igboapi_pspk01076": 1109, "igboapi_pspk01077": 1110, "igboapi_pspk01078": 1111, "igboapi_pspk01079": 1112, "igboapi_pspk01080": 1113, "igboapi_pspk01081": 1114, "igboapi_pspk01082": 1115, "igboapi_pspk01083": 1116, "igboapi_pspk01084": 1117, "igboapi_pspk01085": 1118, "igboapi_pspk01086": 1119, "igboapi_pspk01087": 1120, "igboapi_pspk01088": 1121, "igboapi_pspk01089": 1122, "igboapi_pspk01090": 1123, "igboapi_pspk01092": 1124, "igboapi_pspk01093": 1125, "igboapi_pspk01094": 1126, "igboapi_pspk01095": 1127, "igboapi_pspk01096": 1128, "igboapi_pspk01097": 1129, "igboapi_pspk01098": 1130, "igboapi_pspk01099": 1131, "igboapi_pspk01100": 1132, "igboapi_pspk01101": 1133, "igboapi_pspk01102": 1134, "igboapi_pspk01103": 1135, "igboapi_pspk01104": 1136, "igboapi_pspk01105": 1137, "igboapi_pspk01106": 1138, "igboapi_pspk01107": 1139, "igboapi_pspk01108": 1140, "igboapi_pspk01109": 1141, "igboapi_pspk01110": 1142, "igboapi_pspk01111": 1143, "igboapi_pspk01112": 1144, "igboapi_pspk01113": 1145, "igboapi_pspk01114": 1146, "igboapi_pspk01115": 1147, "igboapi_pspk01116": 1148, "igboapi_pspk01117": 1149, "igboapi_pspk01118": 1150, "igboapi_pspk01119": 1151, "igboapi_pspk01120": 1152, "igboapi_pspk01121": 1153, "igboapi_pspk01122": 1154, "igboapi_pspk01123": 1155, "igboapi_pspk01124": 1156, "igboapi_pspk01125": 1157, "igboapi_pspk01126": 1158, "igboapi_pspk01127": 1159, "igboapi_pspk01128": 1160, "igboapi_pspk01129": 1161, "igboapi_pspk01130": 1162, "igboapi_pspk01131": 1163, "igboapi_pspk01132": 1164, "igboapi_pspk01133": 1165, "igboapi_pspk01134": 1166, "igboapi_pspk01135": 1167, "igboapi_pspk01136": 1168, "igboapi_pspk01137": 1169, "igboapi_pspk01138": 1170, "igboapi_pspk01139": 1171, "igboapi_pspk01140": 1172, "igboapi_pspk01141": 1173, "igboapi_pspk01142": 1174, "igboapi_pspk01143": 1175, "igboapi_pspk01144": 1176, "igboapi_pspk01145": 1177, "igboapi_pspk01146": 1178, "igboapi_pspk01147": 1179, "igboapi_pspk01148": 1180, "igboapi_pspk01149": 1181, "igboapi_pspk01150": 1182, "igboapi_pspk01151": 1183, "igboapi_pspk01152": 1184, "igboapi_pspk01153": 1185, "igboapi_pspk01154": 1186, "igboapi_pspk01155": 1187, "igboapi_pspk01156": 1188, "igboapi_pspk01157": 1189, "igboapi_pspk01158": 1190, "igboapi_pspk01159": 1191, "igboapi_pspk01160": 1192, "igboapi_pspk01161": 1193, "igboapi_pspk01162": 1194, "igboapi_pspk01163": 1195, "igboapi_pspk01164": 1196, "igboapi_pspk01165": 1197, "igboapi_pspk01166": 1198, "igboapi_pspk01167": 1199, "igboapi_pspk01168": 1200, "igboapi_pspk01169": 1201, "igboapi_pspk01170": 1202, "igboapi_pspk01172": 1203, "igboapi_pspk01173": 1204, "igboapi_pspk01174": 1205, "igboapi_pspk01175": 1206, "igboapi_pspk01176": 1207, "igboapi_pspk01178": 1208, "igboapi_pspk01179": 1209, "igboapi_pspk01180": 1210, "igboapi_pspk01181": 1211, "igboapi_pspk01182": 1212, "igboapi_pspk01183": 1213, "igboapi_pspk01184": 1214, "igboapi_pspk01185": 1215, "igboapi_pspk01186": 1216, "igboapi_pspk01187": 1217, "igboapi_pspk01188": 1218, "igboapi_pspk01189": 1219, "igboapi_pspk01190": 1220, "igboapi_pspk01191": 1221, "igboapi_pspk01192": 1222, "igboapi_pspk01193": 1223, "igboapi_pspk01194": 1224, "igboapi_pspk01195": 1225, "igboapi_pspk01196": 1226, "igboapi_pspk01197": 1227, "igboapi_pspk01198": 1228, "igboapi_pspk01199": 1229, "igboapi_pspk01200": 1230, "igboapi_pspk01201": 1231, "igboapi_pspk01202": 1232, "igboapi_pspk01203": 1233, "igboapi_pspk01204": 1234, "igboapi_pspk01205": 1235, "igboapi_pspk01206": 1236, "igboapi_pspk01207": 1237, "igboapi_pspk01208": 1238, "igboapi_pspk01209": 1239, "igboapi_pspk01210": 1240, "igboapi_pspk01211": 1241, "igboapi_pspk01212": 1242, "igboapi_pspk01213": 1243, "igboapi_pspk01214": 1244, "igboapi_pspk01215": 1245, "igboapi_pspk01216": 1246, "igboapi_pspk01217": 1247, "igboapi_pspk01218": 1248, "igboapi_pspk01219": 1249, "igboapi_pspk01220": 1250, "igboapi_pspk01221": 1251, "igboapi_pspk01222": 1252, "igboapi_pspk01223": 1253, "igboapi_pspk01224": 1254, "igboapi_pspk01225": 1255, "igboapi_pspk01226": 1256, "igboapi_pspk01227": 1257, "igboapi_pspk01228": 1258, "igboapi_pspk01229": 1259, "igboapi_pspk01230": 1260, "igboapi_pspk01231": 1261, "igboapi_pspk01232": 1262, "igboapi_pspk01233": 1263, "igboapi_pspk01234": 1264, "igboapi_pspk01235": 1265, "igboapi_pspk01236": 1266, "igboapi_pspk01237": 1267, "igboapi_pspk01238": 1268, "igboapi_pspk01239": 1269, "igboapi_pspk01240": 1270, "igboapi_pspk01241": 1271, "igboapi_pspk01242": 1272, "igboapi_pspk01243": 1273, "igboapi_pspk01244": 1274, "igboapi_pspk01245": 1275, "igboapi_pspk01246": 1276, "igboapi_pspk01247": 1277, "igboapi_pspk01248": 1278, "igboapi_pspk01249": 1279, "igboapi_pspk01250": 1280, "igboapi_pspk01251": 1281, "igboapi_pspk01252": 1282, "igboapi_pspk01253": 1283, "igboapi_pspk01254": 1284, "igboapi_pspk01255": 1285, "igboapi_pspk01256": 1286, "igboapi_pspk01257": 1287, "igboapi_pspk01258": 1288, "igboapi_pspk01259": 1289, "igboapi_pspk01260": 1290, "igboapi_pspk01261": 1291, "igboapi_pspk01262": 1292, "igboapi_pspk01263": 1293, "igboapi_pspk01264": 1294, "igboapi_pspk01265": 1295, "igboapi_pspk01266": 1296, "igboapi_pspk01267": 1297, "igboapi_pspk01268": 1298, "igboapi_pspk01269": 1299, "igboapi_pspk01270": 1300, "igboapi_pspk01271": 1301, "igboapi_pspk01272": 1302, "igboapi_pspk01273": 1303, "igboapi_pspk01274": 1304, "igboapi_pspk01275": 1305, "igboapi_pspk01276": 1306, "igboapi_pspk01277": 1307, "igboapi_pspk01278": 1308, "igboapi_pspk01279": 1309, "igboapi_pspk01280": 1310, "igboapi_pspk01281": 1311, "igboapi_pspk01282": 1312, "igboapi_pspk01283": 1313, "igboapi_pspk01284": 1314, "igboapi_pspk01285": 1315, "igboapi_pspk01286": 1316, "igboapi_pspk01287": 1317, "igboapi_pspk01288": 1318, "igboapi_pspk01289": 1319, "igboapi_pspk01290": 1320, "igboapi_pspk01292": 1321, "igboapi_pspk01293": 1322, "igboapi_pspk01294": 1323, "igboapi_pspk01295": 1324, "igboapi_pspk01296": 1325, "igboapi_pspk01297": 1326, "igboapi_pspk01298": 1327, "igboapi_pspk01300": 1328, "igboapi_pspk01301": 1329, "igboapi_pspk01302": 1330, "igboapi_pspk01303": 1331, "igboapi_pspk01304": 1332, "igboapi_pspk01306": 1333, "igboapi_pspk01307": 1334, "igboapi_pspk01308": 1335, "igboapi_pspk01309": 1336, "igboapi_pspk01310": 1337, "igboapi_pspk01311": 1338, "igboapi_pspk01312": 1339, "igboapi_pspk01313": 1340, "igboapi_pspk01314": 1341, "igboapi_pspk01315": 1342, "igboapi_pspk01316": 1343, "igboapi_pspk01317": 1344, "igboapi_pspk01318": 1345, "igboapi_pspk01319": 1346, "igboapi_pspk01320": 1347, "igboapi_pspk01321": 1348, "igboapi_pspk01322": 1349, "igboapi_pspk01323": 1350, "igboapi_pspk01324": 1351, "igboapi_pspk01325": 1352, "igboapi_pspk01326": 1353, "igboapi_pspk01327": 1354, "igboapi_pspk01328": 1355, "igboapi_pspk01329": 1356, "igboapi_pspk01330": 1357, "igboapi_pspk01331": 1358, "igboapi_pspk01332": 1359, "igboapi_pspk01333": 1360, "igboapi_pspk01334": 1361, "igboapi_pspk01335": 1362, "igboapi_pspk01336": 1363, "igboapi_pspk01337": 1364, "igboapi_pspk01338": 1365, "igboapi_pspk01339": 1366, "igboapi_pspk01340": 1367, "igboapi_pspk01341": 1368, "igboapi_pspk01342": 1369, "igboapi_pspk01343": 1370, "igboapi_pspk01344": 1371, "igboapi_pspk01345": 1372, "igboapi_pspk01346": 1373, "igboapi_pspk01347": 1374, "igboapi_pspk01348": 1375, "igboapi_pspk01349": 1376, "igboapi_pspk01350": 1377, "igboapi_pspk01351": 1378, "igboapi_pspk01352": 1379, "igboapi_pspk01353": 1380, "igboapi_pspk01354": 1381, "igboapi_pspk01355": 1382, "igboapi_pspk01356": 1383, "igboapi_pspk01357": 1384, "igboapi_pspk01358": 1385, "igboapi_pspk01359": 1386, "igboapi_pspk01360": 1387, "igboapi_pspk01361": 1388, "igboapi_pspk01362": 1389, "igboapi_pspk01363": 1390, "igboapi_pspk01364": 1391, "igboapi_pspk01365": 1392, "igboapi_pspk01366": 1393, "igboapi_pspk01367": 1394, "igboapi_pspk01368": 1395, "igboapi_pspk01369": 1396, "igboapi_pspk01370": 1397, "igboapi_pspk01371": 1398, "igboapi_pspk01372": 1399, "igboapi_pspk01373": 1400, "igboapi_pspk01374": 1401, "igboapi_pspk01375": 1402, "igboapi_pspk01376": 1403, "igboapi_pspk01377": 1404, "igboapi_pspk01378": 1405, "igboapi_pspk01379": 1406, "igboapi_pspk01380": 1407, "igboapi_pspk01381": 1408, "igboapi_pspk01382": 1409, "igboapi_pspk01383": 1410, "igboapi_pspk01384": 1411, "igboapi_pspk01385": 1412, "igboapi_pspk01386": 1413, "igboapi_pspk01387": 1414, "igboapi_pspk01388": 1415, "igboapi_pspk01389": 1416, "igboapi_pspk01390": 1417, "igboapi_pspk01391": 1418, "igboapi_pspk01392": 1419, "igboapi_pspk01393": 1420, "igboapi_pspk01394": 1421, "igboapi_pspk01395": 1422, "igboapi_pspk01396": 1423, "igboapi_pspk01397": 1424, "igboapi_pspk01398": 1425, "igboapi_pspk01399": 1426, "igboapi_pspk01400": 1427, "igboapi_pspk01401": 1428, "igboapi_pspk01402": 1429, "igboapi_pspk01403": 1430, "igboapi_pspk01404": 1431, "igboapi_pspk01405": 1432, "igboapi_pspk01406": 1433, "igboapi_pspk01407": 1434, "igboapi_pspk01408": 1435, "igboapi_pspk01409": 1436, "igboapi_pspk01410": 1437, "igboapi_pspk01411": 1438, "igboapi_pspk01412": 1439, "igboapi_pspk01413": 1440, "igboapi_pspk01414": 1441, "igboapi_pspk01415": 1442, "igboapi_pspk01416": 1443, "igboapi_pspk01417": 1444, "igboapi_pspk01418": 1445, "igboapi_pspk01419": 1446, "igboapi_pspk01420": 1447, "igboapi_pspk01421": 1448, "igboapi_pspk01422": 1449, "igboapi_pspk01423": 1450, "igboapi_pspk01424": 1451, "igboapi_pspk01425": 1452, "igboapi_pspk01426": 1453, "igboapi_pspk01427": 1454, "igboapi_pspk01428": 1455, "igboapi_pspk01429": 1456, "igboapi_pspk01430": 1457, "igboapi_pspk01431": 1458, "igboapi_pspk01432": 1459, "igboapi_pspk01433": 1460, "igboapi_pspk01435": 1461, "igboapi_pspk01436": 1462, "igboapi_pspk01437": 1463, "igboapi_pspk01438": 1464, "igboapi_pspk01439": 1465, "igboapi_pspk01440": 1466, "igboapi_pspk01441": 1467, "igboapi_pspk01442": 1468, "igboapi_pspk01443": 1469, "igboapi_pspk01444": 1470, "igboapi_pspk01445": 1471, "igboapi_pspk01446": 1472, "igboapi_pspk01447": 1473, "igboapi_pspk01448": 1474, "igboapi_pspk01449": 1475, "igboapi_pspk01450": 1476, "igboapi_pspk01451": 1477, "igboapi_pspk01452": 1478, "igboapi_pspk01453": 1479, "igboapi_pspk01454": 1480, "igboapi_pspk01455": 1481, "igboapi_pspk01456": 1482, "igboapi_pspk01457": 1483, "igboapi_pspk01458": 1484, "igboapi_pspk01459": 1485, "igboapi_pspk01460": 1486, "igboapi_pspk01461": 1487, "igboapi_pspk01462": 1488, "igboapi_pspk01463": 1489, "igboapi_pspk01464": 1490, "igboapi_pspk01465": 1491, "igboapi_pspk01466": 1492, "igboapi_pspk01467": 1493, "igboapi_pspk01468": 1494, "igboapi_pspk01469": 1495, "igboapi_pspk01470": 1496, "igboapi_pspk01471": 1497, "igboapi_pspk01472": 1498, "igboapi_pspk01473": 1499, "igboapi_pspk01474": 1500, "igboapi_pspk01475": 1501, "igboapi_pspk01476": 1502, "igboapi_pspk01477": 1503, "igboapi_pspk01478": 1504, "igboapi_pspk01479": 1505, "igboapi_pspk01480": 1506, "igboapi_pspk01481": 1507, "igboapi_pspk01482": 1508, "igboapi_pspk01483": 1509, "igboapi_pspk01484": 1510, "igboapi_pspk01485": 1511, "igboapi_pspk01486": 1512, "igboapi_pspk01487": 1513, "igboapi_pspk01488": 1514, "igboapi_pspk01489": 1515, "igboapi_pspk01490": 1516, "igboapi_pspk01491": 1517, "igboapi_pspk01492": 1518, "igboapi_pspk01493": 1519, "igboapi_pspk01494": 1520, "igboapi_pspk01495": 1521, "igboapi_pspk01496": 1522, "igboapi_pspk01497": 1523, "igboapi_pspk01498": 1524, "igboapi_pspk01499": 1525, "igboapi_pspk01500": 1526, "igboapi_pspk01501": 1527, "igboapi_pspk01502": 1528, "igboapi_pspk01503": 1529, "igboapi_pspk01504": 1530, "igboapi_pspk01505": 1531, "igboapi_pspk01506": 1532, "igboapi_pspk01507": 1533, "igboapi_pspk01508": 1534, "igboapi_pspk01509": 1535, "igboapi_pspk01510": 1536, "igboapi_pspk01511": 1537, "igboapi_pspk01512": 1538, "igboapi_pspk01513": 1539, "igboapi_pspk01514": 1540, "igboapi_pspk01515": 1541, "igboapi_pspk01516": 1542, "igboapi_pspk01517": 1543, "igboapi_pspk01518": 1544, "igboapi_pspk01519": 1545, "igboapi_pspk01520": 1546, "igboapi_pspk01521": 1547, "igboapi_pspk01522": 1548, "igboapi_pspk01523": 1549, "igboapi_pspk01524": 1550, "igboapi_pspk01525": 1551, "igboapi_pspk01526": 1552, "igboapi_pspk01527": 1553, "igboapi_pspk01528": 1554, "igboapi_pspk01529": 1555, "igboapi_pspk01530": 1556, "igboapi_pspk01531": 1557, "igboapi_pspk01532": 1558, "igboapi_pspk01533": 1559, "igboapi_pspk01534": 1560, "igboapi_pspk01535": 1561, "igboapi_pspk01536": 1562, "igboapi_pspk01537": 1563, "igboapi_pspk01538": 1564, "igboapi_pspk01539": 1565, "igboapi_pspk01540": 1566, "igboapi_pspk01541": 1567, "igboapi_pspk01542": 1568, "igboapi_pspk01543": 1569, "igboapi_pspk01544": 1570, "igboapi_pspk01545": 1571, "igboapi_pspk01546": 1572, "igboapi_pspk01547": 1573, "igboapi_pspk01548": 1574, "igboapi_pspk01549": 1575, "igboapi_pspk01550": 1576, "igboapi_pspk01552": 1577, "igboapi_pspk01553": 1578, "igboapi_pspk01554": 1579, "igboapi_pspk01555": 1580, "igboapi_pspk01556": 1581, "igboapi_pspk01557": 1582, "igboapi_pspk01558": 1583, "igboapi_pspk01559": 1584, "igboapi_pspk01560": 1585, "igboapi_pspk01561": 1586, "igboapi_pspk01562": 1587, "igboapi_pspk01563": 1588, "igboapi_pspk01564": 1589, "igboapi_pspk01565": 1590, "igboapi_pspk01566": 1591, "igboapi_pspk01567": 1592, "igboapi_pspk01569": 1593, "igboapi_pspk01570": 1594, "igboapi_pspk01571": 1595, "igboapi_pspk01572": 1596, "igboapi_pspk01573": 1597, "igboapi_pspk01574": 1598, "igboapi_pspk01575": 1599, "igboapi_pspk01576": 1600, "igboapi_pspk01577": 1601, "igboapi_pspk01578": 1602, "igboapi_pspk01579": 1603, "igboapi_pspk01580": 1604, "igboapi_pspk01581": 1605, "igboapi_pspk01582": 1606, "igboapi_pspk01584": 1607, "igboapi_pspk01585": 1608, "igboapi_pspk01587": 1609, "igboapi_pspk01588": 1610, "igboapi_pspk01589": 1611, "igboapi_pspk01590": 1612, "igboapi_pspk01591": 1613, "igboapi_pspk01592": 1614, "igboapi_pspk01593": 1615, "igboapi_pspk01594": 1616, "igboapi_pspk01595": 1617, "igboapi_pspk01596": 1618, "igboapi_pspk01597": 1619, "igboapi_pspk01598": 1620, "igboapi_pspk01599": 1621, "igboapi_pspk01600": 1622, "igboapi_pspk01601": 1623, "igboapi_pspk01602": 1624, "igboapi_pspk01603": 1625, "igboapi_pspk01604": 1626, "igboapi_pspk01605": 1627, "igboapi_pspk01606": 1628, "igboapi_pspk01607": 1629, "igboapi_pspk01608": 1630, "igboapi_pspk01609": 1631, "igboapi_pspk01610": 1632, "igboapi_pspk01611": 1633, "igboapi_pspk01612": 1634, "igboapi_pspk01613": 1635, "igboapi_pspk01614": 1636, "igboapi_pspk01615": 1637, "igboapi_pspk01616": 1638, "igboapi_pspk01617": 1639, "igboapi_pspk01618": 1640, "igboapi_pspk01619": 1641, "igboapi_pspk01620": 1642, "igboapi_pspk01621": 1643, "igboapi_pspk01622": 1644, "igboapi_pspk01623": 1645, "igboapi_pspk01624": 1646, "igboapi_pspk01625": 1647, "igboapi_pspk01626": 1648, "igboapi_pspk01627": 1649, "igboapi_pspk01628": 1650, "igboapi_pspk01629": 1651, "igboapi_pspk01630": 1652, "igboapi_pspk01631": 1653, "igboapi_pspk01632": 1654, "igboapi_pspk01633": 1655, "igboapi_pspk01634": 1656, "igboapi_pspk01635": 1657, "igboapi_pspk01636": 1658, "igboapi_pspk01637": 1659, "igboapi_pspk01638": 1660, "igboapi_pspk01639": 1661, "igboapi_pspk01640": 1662, "igboapi_pspk01641": 1663, "igboapi_pspk01642": 1664, "igboapi_pspk01643": 1665, "igboapi_pspk01644": 1666, "igboapi_pspk01645": 1667, "igboapi_pspk01646": 1668, "igboapi_pspk01647": 1669, "igboapi_pspk01648": 1670, "igboapi_pspk01649": 1671, "igboapi_pspk01650": 1672, "igboapi_pspk01651": 1673, "igboapi_pspk01652": 1674, "igboapi_pspk01653": 1675, "igboapi_pspk01654": 1676, "igboapi_pspk01656": 1677, "igboapi_pspk01657": 1678, "igboapi_pspk01658": 1679, "igboapi_pspk01659": 1680, "igboapi_pspk01660": 1681, "igboapi_pspk01661": 1682, "igboapi_pspk01662": 1683, "igboapi_pspk01663": 1684, "igboapi_pspk01664": 1685, "igboapi_pspk01665": 1686, "igboapi_pspk01666": 1687, "igboapi_pspk01667": 1688, "igboapi_pspk01668": 1689, "igboapi_pspk01669": 1690, "igboapi_pspk01670": 1691, "igboapi_pspk01671": 1692, "igboapi_pspk01672": 1693, "igboapi_pspk01673": 1694, "igboapi_pspk01674": 1695, "igboapi_pspk01675": 1696, "igboapi_pspk01676": 1697, "igboapi_pspk01677": 1698, "igboapi_pspk01678": 1699, "igboapi_pspk01679": 1700, "igboapi_pspk01680": 1701, "igboapi_pspk01681": 1702, "igboapi_pspk01682": 1703, "igboapi_pspk01683": 1704, "igboapi_pspk01684": 1705, "igboapi_pspk01685": 1706, "igboapi_pspk01686": 1707, "igboapi_pspk01687": 1708, "igboapi_pspk01688": 1709, "igboapi_pspk01689": 1710, "igboapi_pspk01690": 1711, "igboapi_pspk01691": 1712, "igboapi_pspk01692": 1713, "igboapi_pspk01693": 1714, "igboapi_pspk01694": 1715, "igboapi_pspk01695": 1716, "igboapi_pspk01696": 1717, "igboapi_pspk01697": 1718, "igboapi_pspk01698": 1719, "igboapi_pspk01699": 1720, "igboapi_pspk01700": 1721, "igboapi_pspk01701": 1722, "igboapi_pspk01704": 1723, "igboapi_pspk01705": 1724, "igboapi_pspk01706": 1725, "igboapi_pspk01707": 1726, "igboapi_pspk01708": 1727, "igboapi_pspk01709": 1728, "igboapi_pspk01710": 1729, "igboapi_pspk01711": 1730, "igboapi_pspk01712": 1731, "igboapi_pspk01713": 1732, "igboapi_pspk01714": 1733, "igboapi_pspk01715": 1734, "igboapi_pspk01716": 1735, "igboapi_pspk01717": 1736, "igboapi_pspk01718": 1737, "igboapi_pspk01719": 1738, "igboapi_pspk01720": 1739, "igboapi_pspk01721": 1740, "igboapi_pspk01722": 1741, "igboapi_pspk01723": 1742, "igboapi_pspk01724": 1743, "igboapi_pspk01725": 1744, "igboapi_pspk01726": 1745, "igboapi_pspk01727": 1746, "igboapi_pspk01728": 1747, "igboapi_pspk01729": 1748, "igboapi_pspk01730": 1749, "igboapi_pspk01731": 1750, "igboapi_pspk01732": 1751, "igboapi_pspk01733": 1752, "igboapi_pspk01734": 1753, "igboapi_pspk01735": 1754, "igboapi_pspk01736": 1755, "igboapi_pspk01737": 1756, "igboapi_pspk01738": 1757, "igboapi_pspk01739": 1758, "igboapi_pspk01740": 1759, "igboapi_pspk01741": 1760, "igboapi_pspk01742": 1761, "igboapi_pspk01743": 1762, "igboapi_pspk01744": 1763, "igboapi_pspk01745": 1764, "igboapi_pspk01746": 1765, "igboapi_pspk01748": 1766, "igboapi_pspk01749": 1767, "igboapi_pspk01750": 1768, "igboapi_pspk01751": 1769, "igboapi_pspk01752": 1770, "igboapi_pspk01753": 1771, "igboapi_pspk01754": 1772, "igboapi_pspk01755": 1773, "igboapi_pspk01756": 1774, "igboapi_pspk01757": 1775, "igboapi_pspk01758": 1776, "igboapi_pspk01759": 1777, "igboapi_pspk01760": 1778, "igboapi_pspk01761": 1779, "igboapi_pspk01762": 1780, "igboapi_pspk01763": 1781, "igboapi_pspk01764": 1782, "igboapi_pspk01765": 1783, "igboapi_pspk01766": 1784, "igboapi_pspk01767": 1785, "igboapi_pspk01768": 1786, "igboapi_pspk01769": 1787, "igboapi_pspk01770": 1788, "igboapi_pspk01771": 1789, "igboapi_pspk01772": 1790, "igboapi_pspk01773": 1791, "igboapi_pspk01774": 1792, "igboapi_pspk01775": 1793, "igboapi_pspk01776": 1794, "igboapi_pspk01777": 1795, "igboapi_pspk01778": 1796, "igboapi_pspk01779": 1797, "igboapi_pspk01780": 1798, "igboapi_pspk01781": 1799, "igboapi_pspk01782": 1800, "igboapi_pspk01783": 1801, "igboapi_pspk01784": 1802, "igboapi_pspk01785": 1803, "igboapi_pspk01786": 1804, "igboapi_pspk01787": 1805, "igboapi_pspk01788": 1806, "igboapi_pspk01789": 1807, "igboapi_pspk01790": 1808, "igboapi_pspk01791": 1809, "igboapi_pspk01792": 1810, "igboapi_pspk01793": 1811, "igboapi_pspk01794": 1812, "igboapi_pspk01795": 1813, "igboapi_pspk01796": 1814, "igboapi_pspk01797": 1815, "igboapi_pspk01798": 1816, "igboapi_pspk01799": 1817, "igboapi_pspk01800": 1818, "igboapi_pspk01801": 1819, "igboapi_pspk01802": 1820, "igboapi_pspk01803": 1821, "igboapi_pspk01804": 1822, "igboapi_pspk01805": 1823, "igboapi_pspk01806": 1824, "igboapi_pspk01807": 1825, "igboapi_pspk01808": 1826, "igboapi_pspk01809": 1827, "igboapi_pspk01810": 1828, "igboapi_pspk01811": 1829, "igboapi_pspk01812": 1830, "igboapi_pspk01813": 1831, "igboapi_pspk01814": 1832, "igboapi_pspk01815": 1833, "igboapi_pspk01816": 1834, "igboapi_pspk01817": 1835, "igboapi_pspk01818": 1836, "igboapi_pspk01819": 1837, "igboapi_pspk01820": 1838, "igboapi_pspk01821": 1839, "igboapi_pspk01822": 1840, "igboapi_pspk01823": 1841, "igboapi_pspk01824": 1842, "igboapi_pspk01825": 1843, "igboapi_pspk01826": 1844, "igboapi_pspk01827": 1845, "igboapi_pspk01828": 1846, "igboapi_pspk01829": 1847, "igboapi_pspk01830": 1848, "igboapi_pspk01831": 1849, "igboapi_pspk01832": 1850, "igboapi_pspk01833": 1851, "igboapi_pspk01834": 1852, "igboapi_pspk01835": 1853, "igboapi_pspk01836": 1854, "igboapi_pspk01837": 1855, "igboapi_pspk01838": 1856, "igboapi_pspk01839": 1857, "igboapi_pspk01840": 1858, "igboapi_pspk01841": 1859, "igboapi_pspk01842": 1860, "igboapi_pspk01843": 1861, "igboapi_pspk01844": 1862, "igboapi_pspk01845": 1863, "igboapi_pspk01846": 1864, "igboapi_pspk01847": 1865, "igboapi_pspk01848": 1866, "igboapi_pspk01849": 1867, "igboapi_pspk01850": 1868, "igboapi_pspk01851": 1869, "igboapi_pspk01852": 1870, "igboapi_pspk01853": 1871, "igboapi_pspk01854": 1872, "igboapi_pspk01855": 1873, "igboapi_pspk01856": 1874, "igboapi_pspk01857": 1875, "igboapi_pspk01858": 1876, "igboapi_pspk01859": 1877, "igboapi_pspk01860": 1878, "igboapi_pspk01861": 1879, "igboapi_pspk01862": 1880, "igboapi_pspk01863": 1881, "igboapi_pspk01864": 1882, "igboapi_pspk01866": 1883, "igboapi_pspk01867": 1884, "igboapi_pspk01868": 1885, "igboapi_pspk01869": 1886, "igboapi_pspk01870": 1887, "igboapi_pspk01871": 1888, "igboapi_pspk01872": 1889, "igboapi_pspk01873": 1890, "igboapi_pspk01874": 1891, "igboapi_pspk01875": 1892, "igboapi_pspk01876": 1893, "igboapi_pspk01877": 1894, "igboapi_pspk01878": 1895, "igboapi_pspk01879": 1896, "igboapi_pspk01880": 1897, "igboapi_pspk01881": 1898, "igboapi_pspk01882": 1899, "igboapi_pspk01883": 1900, "igboapi_pspk01884": 1901, "igboapi_pspk01885": 1902, "igboapi_pspk01886": 1903, "igboapi_pspk01887": 1904, "igboapi_pspk01888": 1905, "igboapi_pspk01889": 1906, "igboapi_pspk01890": 1907, "igboapi_pspk01891": 1908, "igboapi_pspk01892": 1909, "igboapi_pspk01893": 1910, "igboapi_pspk01894": 1911, "igboapi_pspk01895": 1912, "igboapi_pspk01896": 1913, "igboapi_pspk01897": 1914, "igboapi_pspk01898": 1915, "igboapi_pspk01899": 1916, "igboapi_pspk01900": 1917, "igboapi_pspk01901": 1918, "igboapi_pspk01902": 1919, "igboapi_pspk01903": 1920, "igboapi_pspk01904": 1921, "igboapi_pspk01905": 1922, "igboapi_pspk01906": 1923, "igboapi_pspk01907": 1924, "igboapi_pspk01908": 1925, "igboapi_pspk01909": 1926, "igboapi_pspk01910": 1927, "igboapi_pspk01911": 1928, "igboapi_pspk01912": 1929, "igboapi_pspk01913": 1930, "igboapi_pspk01914": 1931, "igboapi_pspk01915": 1932, "igboapi_pspk01916": 1933, "igboapi_pspk01917": 1934, "igboapi_pspk01918": 1935, "igboapi_pspk01919": 1936, "igboapi_pspk01920": 1937, "igboapi_pspk01921": 1938, "igboapi_pspk01922": 1939, "igboapi_pspk01923": 1940, "igboapi_pspk01924": 1941, "igboapi_pspk01925": 1942, "igboapi_pspk01926": 1943, "igboapi_pspk01927": 1944, "igboapi_pspk01928": 1945, "igboapi_pspk01929": 1946, "igboapi_pspk01930": 1947, "igboapi_pspk01931": 1948, "igboapi_pspk01932": 1949, "igboapi_pspk01933": 1950, "igboapi_pspk01934": 1951, "igboapi_pspk01935": 1952, "igboapi_pspk01936": 1953, "igboapi_pspk01937": 1954, "igboapi_pspk01938": 1955, "igboapi_pspk01939": 1956, "igboapi_pspk01940": 1957, "igboapi_pspk01941": 1958, "igboapi_pspk01942": 1959, "igboapi_pspk01943": 1960, "igboapi_pspk01944": 1961, "igboapi_pspk01945": 1962, "igboapi_pspk01946": 1963, "igboapi_pspk01947": 1964, "igboapi_pspk01948": 1965, "igboapi_pspk01949": 1966, "igboapi_pspk01950": 1967, "igboapi_pspk01951": 1968, "igboapi_pspk01952": 1969, "igboapi_pspk01953": 1970, "igboapi_pspk01954": 1971, "igboapi_pspk01955": 1972, "igboapi_pspk01956": 1973, "igboapi_pspk01957": 1974, "igboapi_pspk01958": 1975, "igboapi_pspk01959": 1976, "igboapi_pspk01960": 1977, "igboapi_pspk01962": 1978, "igboapi_pspk01963": 1979, "igboapi_pspk01964": 1980, "igboapi_pspk01965": 1981, "igboapi_pspk01966": 1982, "igboapi_pspk01967": 1983, "igboapi_pspk01968": 1984, "igboapi_pspk01969": 1985, "igboapi_pspk01970": 1986, "igboapi_pspk01971": 1987, "igboapi_pspk01972": 1988, "igboapi_pspk01973": 1989, "igboapi_pspk01974": 1990, "igboapi_pspk01975": 1991, "igboapi_pspk01976": 1992, "igboapi_pspk01977": 1993, "igboapi_pspk01978": 1994, "igboapi_pspk01979": 1995, "igboapi_pspk01980": 1996, "igboapi_pspk01981": 1997, "igboapi_pspk01982": 1998, "igboapi_pspk01983": 1999, "igboapi_pspk01984": 2000, "igboapi_pspk01985": 2001, "igboapi_pspk01986": 2002, "igboapi_pspk01987": 2003, "igboapi_pspk01988": 2004, "igboapi_pspk01989": 2005, "igboapi_pspk01990": 2006, "igboapi_pspk01991": 2007, "igboapi_pspk01993": 2008, "igboapi_pspk01994": 2009, "igboapi_pspk01995": 2010, "igboapi_pspk01996": 2011, "igboapi_pspk01997": 2012, "igboapi_pspk01998": 2013, "igboapi_pspk01999": 2014, "igboapi_pspk02000": 2015, "igboapi_pspk02001": 2016, "igboapi_pspk02002": 2017, "igboapi_pspk02003": 2018, "igboapi_pspk02004": 2019, "igboapi_pspk02005": 2020, "igboapi_pspk02006": 2021, "igboapi_pspk02007": 2022, "igboapi_pspk02008": 2023, "igboapi_pspk02009": 2024, "igboapi_pspk02010": 2025, "igboapi_pspk02011": 2026, "igboapi_pspk02012": 2027, "igboapi_pspk02013": 2028, "igboapi_pspk02014": 2029, "igboapi_pspk02015": 2030, "igboapi_pspk02016": 2031, "igboapi_pspk02017": 2032, "igboapi_pspk02018": 2033, "igboapi_pspk02019": 2034, "igboapi_pspk02020": 2035, "igboapi_pspk02021": 2036, "igboapi_pspk02022": 2037, "igboapi_pspk02023": 2038, "igboapi_pspk02024": 2039, "igboapi_pspk02025": 2040, "igboapi_pspk02026": 2041, "igboapi_pspk02027": 2042, "igboapi_pspk02028": 2043, "igboapi_pspk02029": 2044, "igboapi_pspk02030": 2045, "igboapi_pspk02031": 2046, "igboapi_pspk02032": 2047, "igboapi_pspk02033": 2048, "igboapi_pspk02034": 2049, "igboapi_pspk02035": 2050, "igboapi_pspk02036": 2051, "igboapi_pspk02037": 2052, "igboapi_pspk02038": 2053, "igboapi_pspk02039": 2054, "igboapi_pspk02040": 2055, "igboapi_pspk02041": 2056, "igboapi_pspk02042": 2057, "igboapi_pspk02043": 2058, "igboapi_pspk02044": 2059, "igboapi_pspk02045": 2060, "igboapi_pspk02046": 2061, "igboapi_pspk02047": 2062, "igboapi_pspk02048": 2063, "igboapi_pspk02049": 2064, "igboapi_pspk02050": 2065, "igboapi_pspk02051": 2066, "igboapi_pspk02052": 2067, "igboapi_pspk02053": 2068, "igboapi_pspk02054": 2069, "igboapi_pspk02055": 2070, "igboapi_pspk02056": 2071, "igboapi_pspk02057": 2072, "igboapi_pspk02058": 2073, "igboapi_pspk02059": 2074, "igboapi_pspk02061": 2075, "igboapi_pspk02062": 2076, "igboapi_pspk02063": 2077, "igboapi_pspk02064": 2078, "igboapi_pspk02065": 2079, "igboapi_pspk02066": 2080, "igboapi_pspk02068": 2081, "igboapi_pspk02069": 2082, "igboapi_pspk02070": 2083, "igboapi_pspk02071": 2084, "igboapi_pspk02072": 2085, "igboapi_pspk02073": 2086, "igboapi_pspk02074": 2087, "igboapi_pspk02075": 2088, "igboapi_pspk02076": 2089, "igboapi_pspk02077": 2090, "igboapi_pspk02078": 2091, "igboapi_pspk02079": 2092, "igboapi_pspk02080": 2093, "igboapi_pspk02081": 2094, "igboapi_pspk02082": 2095, "igboapi_pspk02083": 2096, "igboapi_pspk02084": 2097, "igboapi_pspk02085": 2098, "igboapi_pspk02086": 2099, "igboapi_pspk02087": 2100, "igboapi_pspk02088": 2101, "igboapi_pspk02089": 2102, "igboapi_pspk02090": 2103, "igboapi_pspk02091": 2104, "igboapi_pspk02092": 2105, "igboapi_pspk02093": 2106, "igboapi_pspk02094": 2107, "igboapi_pspk02095": 2108, "igboapi_pspk02096": 2109, "igboapi_pspk02097": 2110, "igboapi_pspk02098": 2111, "igboapi_pspk02099": 2112, "igboapi_pspk02100": 2113, "igboapi_pspk02101": 2114, "igboapi_pspk02102": 2115, "igboapi_pspk02103": 2116, "igboapi_pspk02104": 2117, "igboapi_pspk02105": 2118, "igboapi_pspk02106": 2119, "igboapi_pspk02107": 2120, "igboapi_pspk02108": 2121, "igboapi_pspk02109": 2122, "igboapi_pspk02110": 2123, "igboapi_pspk02111": 2124, "igboapi_pspk02112": 2125, "igboapi_pspk02113": 2126, "igboapi_pspk02114": 2127, "igboapi_pspk02116": 2128, "igboapi_pspk02117": 2129, "igboapi_pspk02118": 2130, "igboapi_pspk02119": 2131, "igboapi_pspk02120": 2132, "igboapi_pspk02121": 2133, "igboapi_pspk02122": 2134, "igboapi_pspk02123": 2135, "igboapi_pspk02124": 2136, "igboapi_pspk02125": 2137, "igboapi_pspk02126": 2138, "igboapi_pspk02127": 2139, "igboapi_pspk02128": 2140, "igboapi_pspk02129": 2141, "igboapi_pspk02130": 2142, "igboapi_pspk02131": 2143, "igboapi_pspk02132": 2144, "igboapi_pspk02133": 2145, "igboapi_pspk02134": 2146, "igboapi_pspk02135": 2147, "igboapi_pspk02136": 2148, "igboapi_pspk02137": 2149, "igboapi_pspk02138": 2150, "igboapi_pspk02139": 2151, "igboapi_pspk02140": 2152, "igboapi_pspk02141": 2153, "igboapi_pspk02142": 2154, "igboapi_pspk02143": 2155, "igboapi_pspk02144": 2156, "igboapi_pspk02145": 2157, "igboapi_pspk02146": 2158, "igboapi_pspk02147": 2159, "igboapi_pspk02148": 2160, "igboapi_pspk02149": 2161, "igboapi_pspk02150": 2162, "igboapi_pspk02151": 2163, "igboapi_pspk02152": 2164, "igboapi_pspk02153": 2165, "igboapi_pspk02154": 2166, "igboapi_pspk02155": 2167, "igboapi_pspk02156": 2168, "igboapi_pspk02157": 2169, "igboapi_pspk02158": 2170, "igboapi_pspk02159": 2171, "igboapi_pspk02160": 2172, "igboapi_pspk02161": 2173, "igboapi_pspk02162": 2174, "igboapi_pspk02163": 2175, "igboapi_pspk02164": 2176, "igboapi_pspk02165": 2177, "igboapi_pspk02166": 2178, "igboapi_pspk02167": 2179, "igboapi_pspk02168": 2180, "igboapi_pspk02169": 2181, "igboapi_pspk02170": 2182, "igboapi_pspk02172": 2183, "igboapi_pspk02173": 2184, "igboapi_pspk02174": 2185, "igboapi_pspk02175": 2186, "igboapi_pspk02176": 2187, "igboapi_pspk02177": 2188, "igboapi_pspk02178": 2189, "igboapi_pspk02179": 2190, "igboapi_pspk02180": 2191, "igboapi_pspk02181": 2192, "igboapi_pspk02182": 2193, "igboapi_pspk02183": 2194, "igboapi_pspk02184": 2195, "igboapi_pspk02185": 2196, "igboapi_pspk02186": 2197, "igboapi_pspk02187": 2198, "igboapi_pspk02188": 2199, "igboapi_pspk02189": 2200, "igboapi_pspk02190": 2201, "igboapi_pspk02191": 2202, "igboapi_pspk02193": 2203, "igboapi_pspk02194": 2204, "igboapi_pspk02195": 2205, "igboapi_pspk02196": 2206, "igboapi_pspk02197": 2207, "igboapi_pspk02198": 2208, "igboapi_pspk02199": 2209, "igboapi_pspk02200": 2210, "igboapi_pspk02201": 2211, "igboapi_pspk02202": 2212, "igboapi_pspk02203": 2213, "igboapi_pspk02205": 2214, "igboapi_pspk02206": 2215, "igboapi_pspk02207": 2216, "igboapi_pspk02208": 2217, "igboapi_pspk02209": 2218, "igboapi_pspk02211": 2219, "igboapi_pspk02212": 2220, "igboapi_pspk02213": 2221, "igboapi_pspk02214": 2222, "igboapi_pspk02215": 2223, "igboapi_pspk02216": 2224, "igboapi_pspk02217": 2225, "igboapi_pspk02218": 2226, "igboapi_pspk02219": 2227, "igboapi_pspk02220": 2228, "igboapi_pspk02221": 2229, "igboapi_pspk02222": 2230, "igboapi_pspk02223": 2231, "igboapi_pspk02224": 2232, "igboapi_pspk02225": 2233, "igboapi_pspk02226": 2234, "igboapi_pspk02227": 2235, "igboapi_pspk02228": 2236, "igboapi_pspk02229": 2237, "igboapi_pspk02230": 2238, "igboapi_pspk02231": 2239, "igboapi_pspk02232": 2240, "igboapi_pspk02233": 2241, "igboapi_pspk02234": 2242, "igboapi_pspk02235": 2243, "igboapi_pspk02236": 2244, "igboapi_pspk02237": 2245, "igboapi_pspk02238": 2246, "igboapi_pspk02239": 2247, "igboapi_pspk02240": 2248, "igboapi_pspk02241": 2249, "igboapi_pspk02242": 2250, "igboapi_pspk02243": 2251, "igboapi_pspk02244": 2252, "igboapi_pspk02245": 2253, "igboapi_pspk02246": 2254, "igboapi_pspk02247": 2255, "igboapi_pspk02248": 2256, "igboapi_pspk02249": 2257, "igboapi_pspk02251": 2258, "igboapi_pspk02252": 2259, "igboapi_pspk02253": 2260, "igboapi_pspk02254": 2261, "igboapi_pspk02255": 2262, "igboapi_pspk02256": 2263, "igboapi_pspk02257": 2264, "igboapi_pspk02258": 2265, "igboapi_pspk02259": 2266, "igboapi_pspk02260": 2267, "igboapi_pspk02261": 2268, "igboapi_pspk02262": 2269, "igboapi_pspk02263": 2270, "igboapi_pspk02264": 2271, "igboapi_pspk02265": 2272, "igboapi_pspk02266": 2273, "igboapi_pspk02267": 2274, "igboapi_pspk02268": 2275, "igboapi_pspk02269": 2276, "igboapi_pspk02270": 2277, "igboapi_pspk02271": 2278, "igboapi_pspk02272": 2279, "igboapi_pspk02273": 2280, "igboapi_pspk02274": 2281, "igboapi_pspk02275": 2282, "igboapi_pspk02276": 2283, "igboapi_pspk02277": 2284, "igboapi_pspk02278": 2285, "igboapi_pspk02279": 2286, "igboapi_pspk02280": 2287, "igboapi_pspk02281": 2288, "igboapi_pspk02282": 2289, "igboapi_pspk02283": 2290, "igboapi_pspk02284": 2291, "igboapi_pspk02285": 2292, "igboapi_pspk02286": 2293, "igboapi_pspk02287": 2294, "igboapi_pspk02288": 2295, "igboapi_pspk02289": 2296, "igboapi_pspk02290": 2297, "igboapi_pspk02291": 2298, "igboapi_pspk02292": 2299, "igboapi_pspk02293": 2300, "igboapi_pspk02294": 2301, "igboapi_pspk02295": 2302, "igboapi_pspk02296": 2303, "igboapi_pspk02297": 2304, "igboapi_pspk02298": 2305, "igboapi_pspk02299": 2306, "igboapi_pspk02300": 2307, "igboapi_pspk02301": 2308, "igboapi_pspk02302": 2309, "igboapi_pspk02303": 2310, "igboapi_pspk02304": 2311, "igboapi_pspk02305": 2312, "igboapi_pspk02306": 2313, "igboapi_pspk02307": 2314, "igboapi_pspk02308": 2315, "igboapi_pspk02309": 2316, "igboapi_pspk02310": 2317, "igboapi_pspk02311": 2318, "igboapi_pspk02312": 2319, "igboapi_pspk02313": 2320, "igboapi_pspk02314": 2321, "igboapi_pspk02315": 2322, "igboapi_pspk02316": 2323, "igboapi_pspk02317": 2324, "igboapi_pspk02318": 2325, "igboapi_pspk02319": 2326, "igboapi_pspk02320": 2327, "igboapi_pspk02321": 2328, "igboapi_pspk02322": 2329, "igboapi_pspk02323": 2330, "igboapi_pspk02324": 2331, "igboapi_pspk02325": 2332, "igboapi_pspk02326": 2333, "igboapi_pspk02329": 2334, "igboapi_pspk02330": 2335, "igboapi_pspk02331": 2336, "igboapi_pspk02332": 2337, "igboapi_pspk02333": 2338, "igboapi_pspk02334": 2339, "igboapi_pspk02335": 2340, "igboapi_pspk02336": 2341, "igboapi_pspk02337": 2342, "igboapi_pspk02339": 2343, "igboapi_pspk02341": 2344, "igboapi_pspk02342": 2345, "igboapi_pspk02343": 2346, "igboapi_pspk02344": 2347, "igboapi_pspk02345": 2348, "igboapi_pspk02346": 2349, "igboapi_pspk02347": 2350, "igboapi_pspk02348": 2351, "igboapi_pspk02349": 2352, "igboapi_pspk02350": 2353, "igboapi_pspk02351": 2354, "igboapi_pspk02352": 2355, "igboapi_pspk02353": 2356, "igboapi_pspk02354": 2357, "igboapi_pspk02355": 2358, "igboapi_pspk02356": 2359, "igboapi_pspk02357": 2360, "igboapi_pspk02358": 2361, "igboapi_pspk02359": 2362, "igboapi_pspk02360": 2363, "igboapi_pspk02361": 2364, "igboapi_pspk02362": 2365, "igboapi_pspk02363": 2366, "igboapi_pspk02364": 2367, "igboapi_pspk02365": 2368, "igboapi_pspk02366": 2369, "igboapi_pspk02367": 2370, "igboapi_pspk02368": 2371, "igboapi_pspk02369": 2372, "igboapi_pspk02370": 2373, "igboapi_pspk02371": 2374, "igboapi_pspk02372": 2375, "igboapi_pspk02373": 2376, "igboapi_pspk02374": 2377, "igboapi_pspk02375": 2378, "igboapi_pspk02376": 2379, "igboapi_pspk02377": 2380, "igboapi_pspk02378": 2381, "igboapi_pspk02379": 2382, "igboapi_pspk02380": 2383, "igboapi_pspk02381": 2384, "igboapi_pspk02382": 2385, "igboapi_pspk02383": 2386, "igboapi_pspk02384": 2387, "igboapi_pspk02385": 2388, "igboapi_pspk02386": 2389, "igboapi_pspk02387": 2390, "igboapi_pspk02388": 2391, "igboapi_pspk02389": 2392, "igboapi_pspk02390": 2393, "igboapi_pspk02391": 2394, "igboapi_pspk02392": 2395, "igboapi_pspk02393": 2396, "igboapi_pspk02394": 2397, "igboapi_pspk02395": 2398, "igboapi_pspk02396": 2399, "igboapi_pspk02397": 2400, "igboapi_pspk02398": 2401, "igboapi_pspk02399": 2402, "igboapi_pspk02400": 2403, "igboapi_pspk02401": 2404, "igboapi_pspk02402": 2405, "igboapi_pspk02403": 2406, "igboapi_pspk02404": 2407, "igboapi_pspk02405": 2408, "igboapi_pspk02406": 2409, "igboapi_pspk02407": 2410, "igboapi_pspk02408": 2411, "igboapi_pspk02409": 2412, "igboapi_pspk02410": 2413, "igboapi_pspk02411": 2414, "igboapi_pspk02412": 2415, "igboapi_pspk02413": 2416, "igboapi_pspk02414": 2417, "igboapi_pspk02415": 2418, "igboapi_pspk02416": 2419, "igboapi_pspk02417": 2420, "igboapi_pspk02418": 2421, "igboapi_pspk02419": 2422, "igboapi_pspk02420": 2423, "igboapi_pspk02421": 2424, "igboapi_pspk02422": 2425, "igboapi_pspk02423": 2426, "igboapi_pspk02424": 2427, "igboapi_pspk02425": 2428, "igboapi_pspk02426": 2429, "igboapi_pspk02427": 2430, "igboapi_pspk02428": 2431, "igboapi_pspk02429": 2432, "igboapi_pspk02430": 2433, "igboapi_pspk02431": 2434, "igboapi_pspk02432": 2435, "igboapi_pspk02433": 2436, "igboapi_pspk02434": 2437, "igboapi_pspk02435": 2438, "igboapi_pspk02436": 2439, "igboapi_pspk02437": 2440, "igboapi_pspk02438": 2441, "igboapi_pspk02439": 2442, "igboapi_pspk02440": 2443, "igboapi_pspk02441": 2444, "igboapi_pspk02442": 2445, "igboapi_pspk02443": 2446, "igboapi_pspk02444": 2447, "igboapi_pspk02445": 2448, "igboapi_pspk02446": 2449, "igboapi_pspk02447": 2450, "igboapi_pspk02448": 2451, "igboapi_pspk02449": 2452, "igboapi_pspk02450": 2453, "igboapi_pspk02451": 2454, "igboapi_pspk02452": 2455, "igboapi_pspk02453": 2456, "igboapi_pspk02454": 2457, "igboapi_pspk02455": 2458, "igboapi_pspk02456": 2459, "igboapi_pspk02457": 2460, "igboapi_pspk02458": 2461, "igboapi_pspk02459": 2462, "igboapi_pspk02460": 2463, "igboapi_pspk02461": 2464, "igboapi_pspk02462": 2465, "igboapi_pspk02463": 2466, "igboapi_pspk02464": 2467, "igboapi_pspk02465": 2468, "igboapi_pspk02466": 2469, "igboapi_pspk02467": 2470, "igboapi_pspk02468": 2471, "igboapi_pspk02469": 2472, "igboapi_pspk02470": 2473, "igboapi_pspk02471": 2474, "igboapi_pspk02472": 2475, "igboapi_pspk02473": 2476, "igboapi_pspk02474": 2477, "igboapi_pspk02475": 2478, "igboapi_pspk02476": 2479, "igboapi_pspk02477": 2480, "igboapi_pspk02478": 2481, "igboapi_pspk02479": 2482, "igboapi_pspk02480": 2483, "igboapi_pspk02481": 2484, "igboapi_pspk02482": 2485, "igboapi_pspk02483": 2486, "igboapi_pspk02484": 2487, "igboapi_pspk02485": 2488, "igboapi_pspk02486": 2489, "igboapi_pspk02487": 2490, "igboapi_pspk02488": 2491, "igboapi_pspk02489": 2492, "igboapi_pspk02490": 2493, "igboapi_pspk02491": 2494, "igboapi_pspk02492": 2495, "igboapi_pspk02493": 2496, "igboapi_pspk02494": 2497, "igboapi_pspk02495": 2498, "igboapi_pspk02496": 2499, "igboapi_pspk02497": 2500, "igboapi_pspk02498": 2501, "igboapi_pspk02499": 2502, "igboapi_pspk02500": 2503, "igboapi_pspk02501": 2504, "igboapi_pspk02502": 2505, "igboapi_pspk02503": 2506, "igboapi_pspk02504": 2507, "igboapi_pspk02505": 2508, "igboapi_pspk02506": 2509, "igboapi_pspk02507": 2510, "igboapi_pspk02508": 2511, "igboapi_pspk02509": 2512, "igboapi_pspk02510": 2513, "igboapi_pspk02511": 2514, "igboapi_pspk02512": 2515, "igboapi_pspk02513": 2516, "igboapi_pspk02514": 2517, "igboapi_pspk02515": 2518, "igboapi_pspk02516": 2519, "igboapi_pspk02517": 2520, "igboapi_pspk02518": 2521, "igboapi_pspk02519": 2522, "igboapi_pspk02520": 2523, "igboapi_pspk02521": 2524, "igboapi_pspk02522": 2525, "igboapi_pspk02523": 2526, "igboapi_pspk02524": 2527, "igboapi_pspk02525": 2528, "igboapi_pspk02526": 2529, "igboapi_pspk02527": 2530, "igboapi_pspk02528": 2531, "igboapi_pspk02529": 2532, "igboapi_pspk02530": 2533, "igboapi_pspk02531": 2534, "igboapi_pspk02532": 2535, "igboapi_pspk02533": 2536, "igboapi_pspk02535": 2537, "igboapi_pspk02536": 2538, "igboapi_pspk02537": 2539, "igboapi_pspk02539": 2540, "igboapi_pspk02540": 2541, "igboapi_pspk02541": 2542, "igboapi_pspk02542": 2543, "igboapi_pspk02543": 2544, "igboapi_pspk02544": 2545, "igboapi_pspk02545": 2546, "igboapi_pspk02546": 2547, "igboapi_pspk02547": 2548, "igboapi_pspk02548": 2549, "igboapi_pspk02549": 2550, "igboapi_pspk02550": 2551, "igboapi_pspk02551": 2552, "igboapi_pspk02552": 2553, "igboapi_pspk02553": 2554, "igboapi_pspk02554": 2555, "igboapi_pspk02555": 2556, "igboapi_pspk02556": 2557, "igboapi_pspk02557": 2558, "igboapi_pspk02558": 2559, "igboapi_pspk02559": 2560, "igboapi_pspk02560": 2561, "igboapi_pspk02561": 2562, "igboapi_pspk02562": 2563, "igboapi_pspk02563": 2564, "igboapi_pspk02564": 2565, "igboapi_pspk02565": 2566, "igboapi_pspk02566": 2567, "igboapi_pspk02567": 2568, "igboapi_pspk02568": 2569, "igboapi_pspk02569": 2570, "igboapi_pspk02570": 2571, "igboapi_pspk02571": 2572, "igboapi_pspk02572": 2573, "igboapi_pspk02573": 2574, "igboapi_pspk02574": 2575, "igboapi_pspk02575": 2576, "igboapi_pspk02576": 2577, "igboapi_pspk02577": 2578, "igboapi_pspk02578": 2579, "igboapi_pspk02579": 2580, "igboapi_pspk02580": 2581, "igboapi_pspk02581": 2582, "igboapi_pspk02582": 2583, "igboapi_pspk02583": 2584, "igboapi_pspk02584": 2585, "igboapi_pspk02585": 2586, "igboapi_pspk02586": 2587, "igboapi_pspk02587": 2588, "igboapi_pspk02588": 2589, "igboapi_pspk02589": 2590, "igboapi_pspk02590": 2591, "igboapi_pspk02591": 2592, "igboapi_pspk02592": 2593, "igboapi_pspk02593": 2594, "igboapi_pspk02594": 2595, "igboapi_pspk02595": 2596, "igboapi_pspk02596": 2597, "igboapi_pspk02597": 2598, "igboapi_pspk02598": 2599, "igboapi_pspk02599": 2600, "igboapi_pspk02600": 2601, "igboapi_pspk02601": 2602, "igboapi_pspk02602": 2603, "igboapi_pspk02603": 2604, "igboapi_pspk02604": 2605, "igboapi_pspk02605": 2606, "igboapi_pspk02606": 2607, "igboapi_pspk02607": 2608, "igboapi_pspk02608": 2609, "igboapi_pspk02609": 2610, "igboapi_pspk02610": 2611, "igboapi_pspk02611": 2612, "igboapi_pspk02612": 2613, "igboapi_pspk02613": 2614, "igboapi_pspk02614": 2615, "igboapi_pspk02615": 2616, "igboapi_pspk02616": 2617, "igboapi_pspk02617": 2618, "igboapi_pspk02618": 2619, "igboapi_pspk02619": 2620, "igboapi_pspk02620": 2621, "igboapi_pspk02621": 2622, "igboapi_pspk02622": 2623, "igboapi_pspk02623": 2624, "igboapi_pspk02624": 2625, "igboapi_pspk02625": 2626, "igboapi_pspk02626": 2627, "igboapi_pspk02627": 2628, "igboapi_pspk02628": 2629, "igboapi_pspk02629": 2630, "igboapi_pspk02630": 2631, "igboapi_pspk02631": 2632, "igboapi_pspk02632": 2633, "igboapi_pspk02633": 2634, "igboapi_pspk02634": 2635, "igboapi_pspk02635": 2636, "igboapi_pspk02636": 2637, "igboapi_pspk02637": 2638, "igboapi_pspk02638": 2639, "igboapi_pspk02639": 2640, "igboapi_pspk02640": 2641, "igboapi_pspk02641": 2642, "igboapi_pspk02642": 2643, "igboapi_pspk02643": 2644, "igboapi_pspk02644": 2645, "igboapi_pspk02645": 2646, "igboapi_pspk02648": 2647, "igboapi_pspk02649": 2648, "igboapi_pspk02650": 2649, "igboapi_pspk02651": 2650, "igboapi_pspk02652": 2651, "igboapi_pspk02653": 2652, "igboapi_pspk02655": 2653, "igboapi_pspk02656": 2654, "igboapi_pspk02657": 2655, "igboapi_pspk02658": 2656, "igboapi_pspk02659": 2657, "igboapi_pspk02661": 2658, "igboapi_pspk02662": 2659, "igboapi_pspk02663": 2660, "igboapi_pspk02664": 2661, "igboapi_pspk02665": 2662, "igboapi_pspk02666": 2663, "igboapi_pspk02667": 2664, "igboapi_pspk02668": 2665, "igboapi_pspk02669": 2666, "igboapi_pspk02670": 2667, "igboapi_pspk02671": 2668, "igboapi_pspk02672": 2669, "igboapi_pspk02673": 2670, "igboapi_pspk02674": 2671, "igboapi_pspk02675": 2672, "igboapi_pspk02676": 2673, "igboapi_pspk02677": 2674, "igboapi_pspk02678": 2675, "igboapi_pspk02679": 2676, "igboapi_pspk02680": 2677, "igboapi_pspk02681": 2678, "igboapi_pspk02682": 2679, "igboapi_pspk02683": 2680, "igboapi_pspk02684": 2681, "igboapi_pspk02685": 2682, "igboapi_pspk02686": 2683, "igboapi_pspk02687": 2684, "igboapi_pspk02688": 2685, "igboapi_pspk02689": 2686, "igboapi_pspk02690": 2687, "igboapi_pspk02691": 2688, "igboapi_pspk02692": 2689, "igboapi_pspk02693": 2690, "igboapi_pspk02694": 2691, "igboapi_pspk02695": 2692, "igboapi_pspk02696": 2693, "igboapi_pspk02697": 2694, "igboapi_pspk02698": 2695, "igboapi_pspk02699": 2696, "igboapi_pspk02700": 2697, "igboapi_pspk02701": 2698, "igboapi_pspk02702": 2699, "igboapi_pspk02703": 2700, "igboapi_pspk02704": 2701, "igboapi_pspk02705": 2702, "igboapi_pspk02706": 2703, "igboapi_pspk02707": 2704, "igboapi_pspk02708": 2705, "igboapi_pspk02709": 2706, "igboapi_pspk02710": 2707, "igboapi_pspk02711": 2708, "igboapi_pspk02712": 2709, "igboapi_pspk02713": 2710, "igboapi_pspk02714": 2711, "igboapi_pspk02715": 2712, "igboapi_pspk02716": 2713, "igboapi_pspk02718": 2714, "igboapi_pspk02719": 2715, "igboapi_pspk02720": 2716, "igboapi_pspk02721": 2717, "igboapi_pspk02722": 2718, "igboapi_pspk02723": 2719, "igboapi_pspk02724": 2720, "igboapi_pspk02725": 2721, "igboapi_pspk02726": 2722, "igboapi_pspk02727": 2723, "igboapi_pspk02728": 2724, "igboapi_pspk02729": 2725, "igboapi_pspk02730": 2726, "igboapi_pspk02731": 2727, "igboapi_pspk02732": 2728, "igboapi_pspk02733": 2729, "igboapi_pspk02734": 2730, "igboapi_pspk02735": 2731, "igboapi_pspk02736": 2732, "igboapi_pspk02737": 2733, "igboapi_pspk02738": 2734, "igboapi_pspk02739": 2735, "igboapi_pspk02740": 2736, "igboapi_pspk02741": 2737, "igboapi_pspk02742": 2738, "igboapi_pspk02744": 2739, "igboapi_pspk02745": 2740, "igboapi_pspk02746": 2741, "igboapi_pspk02747": 2742, "igboapi_pspk02748": 2743, "igboapi_pspk02749": 2744, "igboapi_pspk02751": 2745, "igboapi_pspk02752": 2746, "igboapi_pspk02753": 2747, "igboapi_pspk02754": 2748, "igboapi_pspk02755": 2749, "igboapi_pspk02756": 2750, "igboapi_pspk02757": 2751, "igboapi_pspk02758": 2752, "igboapi_pspk02759": 2753, "igboapi_pspk02760": 2754, "igboapi_pspk02761": 2755, "igboapi_pspk02763": 2756, "igboapi_pspk02764": 2757, "igboapi_pspk02765": 2758, "igboapi_pspk02766": 2759, "igboapi_pspk02767": 2760, "igboapi_pspk02768": 2761, "igboapi_pspk02769": 2762, "igboapi_pspk02770": 2763, "igboapi_pspk02771": 2764, "igboapi_pspk02772": 2765, "igboapi_pspk02773": 2766, "igboapi_pspk02774": 2767, "igboapi_pspk02775": 2768, "igboapi_pspk02776": 2769, "igboapi_pspk02778": 2770, "igboapi_pspk02779": 2771, "igboapi_pspk02780": 2772, "igboapi_pspk02781": 2773, "igboapi_pspk02782": 2774, "igboapi_pspk02783": 2775, "igboapi_pspk02784": 2776, "igboapi_pspk02785": 2777, "igboapi_pspk02786": 2778, "igboapi_pspk02787": 2779, "igboapi_pspk02788": 2780, "igboapi_pspk02789": 2781, "igboapi_pspk02790": 2782, "igboapi_pspk02791": 2783, "igboapi_pspk02792": 2784, "igboapi_pspk02793": 2785, "igboapi_pspk02794": 2786, "igboapi_pspk02795": 2787, "igboapi_pspk02796": 2788, "igboapi_pspk02797": 2789, "igboapi_pspk02798": 2790, "igboapi_pspk02799": 2791, "igboapi_pspk02800": 2792, "igboapi_pspk02801": 2793, "igboapi_pspk02802": 2794, "igboapi_pspk02803": 2795, "igboapi_pspk02804": 2796, "igboapi_pspk02805": 2797, "igboapi_pspk02806": 2798, "igboapi_pspk02807": 2799, "igboapi_pspk02808": 2800, "igboapi_pspk02809": 2801, "igboapi_pspk02810": 2802, "igboapi_pspk02811": 2803, "igboapi_pspk02812": 2804, "igboapi_pspk02813": 2805, "igboapi_pspk02814": 2806, "igboapi_pspk02815": 2807, "igboapi_pspk02816": 2808, "igboapi_pspk02817": 2809, "igboapi_pspk02818": 2810, "igboapi_pspk02819": 2811, "igboapi_pspk02820": 2812, "igboapi_pspk02821": 2813, "igboapi_pspk02822": 2814, "igboapi_pspk02823": 2815, "igboapi_pspk02824": 2816, "igboapi_pspk02825": 2817, "igboapi_pspk02827": 2818, "igboapi_pspk02828": 2819, "igboapi_pspk02829": 2820, "igboapi_pspk02830": 2821, "igboapi_pspk02831": 2822, "igboapi_pspk02832": 2823, "igboapi_pspk02833": 2824, "igboapi_pspk02834": 2825, "igboapi_pspk02835": 2826, "igboapi_pspk02836": 2827, "igboapi_pspk02837": 2828, "igboapi_pspk02838": 2829, "igboapi_pspk02839": 2830, "igboapi_pspk02840": 2831, "igboapi_pspk02841": 2832, "igboapi_pspk02842": 2833, "igboapi_pspk02843": 2834, "igboapi_pspk02844": 2835, "igboapi_pspk02845": 2836, "igboapi_pspk02846": 2837, "igboapi_pspk02847": 2838, "igboapi_pspk02848": 2839, "igboapi_pspk02849": 2840, "igboapi_pspk02850": 2841, "igboapi_pspk02851": 2842, "igboapi_pspk02852": 2843, "igboapi_pspk02853": 2844, "igboapi_pspk02854": 2845, "igboapi_pspk02855": 2846, "igboapi_pspk02856": 2847, "igboapi_pspk02857": 2848, "igboapi_pspk02858": 2849, "igboapi_pspk02859": 2850, "igboapi_pspk02860": 2851, "igboapi_pspk02861": 2852, "igboapi_pspk02862": 2853, "igboapi_pspk02863": 2854, "igboapi_pspk02864": 2855, "igboapi_pspk02865": 2856, "igboapi_pspk02866": 2857, "igboapi_pspk02867": 2858, "igboapi_pspk02870": 2859, "igboapi_pspk02871": 2860, "igboapi_pspk02872": 2861, "igboapi_pspk02873": 2862, "igboapi_pspk02874": 2863, "igboapi_pspk02875": 2864, "igboapi_pspk02876": 2865, "igboapi_pspk02878": 2866, "igboapi_pspk02879": 2867, "igboapi_pspk02880": 2868, "igboapi_pspk02881": 2869, "igboapi_pspk02882": 2870, "igboapi_pspk02883": 2871, "igboapi_pspk02884": 2872, "igboapi_pspk02885": 2873, "igboapi_pspk02886": 2874, "igboapi_pspk02887": 2875, "igboapi_pspk02888": 2876, "igboapi_pspk02889": 2877, "igboapi_pspk02890": 2878, "igboapi_pspk02891": 2879, "igboapi_pspk02892": 2880, "igboapi_pspk02893": 2881, "igboapi_pspk02894": 2882, "igboapi_pspk02895": 2883, "igboapi_pspk02896": 2884, "igboapi_pspk02897": 2885, "igboapi_pspk02898": 2886, "igboapi_pspk02899": 2887, "igboapi_pspk02900": 2888, "igboapi_pspk02901": 2889, "igboapi_pspk02902": 2890, "igboapi_pspk02903": 2891, "igboapi_pspk02904": 2892, "igboapi_pspk02905": 2893, "igboapi_pspk02906": 2894, "igboapi_pspk02907": 2895, "igboapi_pspk02908": 2896, "igboapi_pspk02909": 2897, "igboapi_pspk02910": 2898, "igboapi_pspk02911": 2899, "igboapi_pspk02912": 2900, "igboapi_pspk02913": 2901, "igboapi_pspk02914": 2902, "igboapi_pspk02915": 2903, "igboapi_pspk02916": 2904, "igboapi_pspk02917": 2905, "igboapi_pspk02918": 2906, "igboapi_pspk02919": 2907, "igboapi_pspk02920": 2908, "igboapi_pspk02921": 2909, "igboapi_pspk02922": 2910, "igboapi_pspk02923": 2911, "igboapi_pspk02924": 2912, "igboapi_pspk02925": 2913, "igboapi_pspk02926": 2914, "igboapi_pspk02927": 2915, "igboapi_pspk02928": 2916, "igboapi_pspk02929": 2917, "igboapi_pspk02930": 2918, "igboapi_pspk02931": 2919, "igboapi_pspk02932": 2920, "igboapi_pspk02933": 2921, "igboapi_pspk02934": 2922, "igboapi_pspk02935": 2923, "igboapi_pspk02936": 2924, "igboapi_pspk02937": 2925, "igboapi_pspk02938": 2926, "igboapi_pspk02939": 2927, "igboapi_pspk02940": 2928, "igboapi_pspk02941": 2929, "igboapi_pspk02942": 2930, "igboapi_pspk02943": 2931, "igboapi_pspk02944": 2932, "igboapi_pspk02945": 2933, "igboapi_pspk02946": 2934, "igboapi_pspk02947": 2935, "igboapi_pspk02948": 2936, "igboapi_pspk02949": 2937, "igboapi_pspk02950": 2938, "igboapi_pspk02951": 2939, "igboapi_pspk02952": 2940, "igboapi_pspk02954": 2941, "igboapi_pspk02955": 2942, "igboapi_pspk02956": 2943, "igboapi_pspk02957": 2944, "igboapi_pspk02958": 2945, "igboapi_pspk02959": 2946, "igboapi_pspk02960": 2947, "igboapi_pspk02961": 2948, "igboapi_pspk02962": 2949, "igboapi_pspk02963": 2950, "igboapi_pspk02964": 2951, "igboapi_pspk02965": 2952, "igboapi_pspk02966": 2953, "igboapi_pspk02967": 2954, "igboapi_pspk02968": 2955, "igboapi_pspk02969": 2956, "igboapi_pspk02970": 2957, "igboapi_pspk02971": 2958, "igboapi_pspk02972": 2959, "igboapi_pspk02973": 2960, "igboapi_pspk02974": 2961, "igboapi_pspk02975": 2962, "igboapi_pspk02976": 2963, "igboapi_pspk02978": 2964, "igboapi_pspk02979": 2965, "igboapi_pspk02981": 2966, "igboapi_pspk02982": 2967, "igboapi_pspk02983": 2968, "igboapi_pspk02984": 2969, "igboapi_pspk02985": 2970, "igboapi_pspk02986": 2971, "igboapi_pspk02987": 2972, "igboapi_pspk02989": 2973, "igboapi_pspk02990": 2974, "igboapi_pspk02991": 2975, "igboapi_pspk02992": 2976, "igboapi_pspk02993": 2977, "igboapi_pspk02994": 2978, "igboapi_pspk02995": 2979, "igboapi_pspk02996": 2980, "igboapi_pspk02997": 2981, "igboapi_pspk02998": 2982, "igboapi_pspk02999": 2983, "igboapi_pspk03000": 2984, "igboapi_pspk03001": 2985, "igboapi_pspk03002": 2986, "igboapi_pspk03003": 2987, "igboapi_pspk03004": 2988, "igboapi_pspk03005": 2989, "igboapi_pspk03006": 2990, "igboapi_pspk03007": 2991, "igboapi_pspk03008": 2992, "igboapi_pspk03009": 2993, "igboapi_pspk03010": 2994, "igboapi_pspk03011": 2995, "igboapi_pspk03012": 2996, "igboapi_pspk03013": 2997, "igboapi_pspk03014": 2998, "igboapi_pspk03015": 2999, "igboapi_pspk03016": 3000, "igboapi_pspk03017": 3001, "igboapi_pspk03018": 3002, "igboapi_pspk03019": 3003, "igboapi_pspk03020": 3004, "igboapi_pspk03021": 3005, "igboapi_pspk03022": 3006, "igboapi_pspk03023": 3007, "igboapi_pspk03024": 3008, "igboapi_pspk03025": 3009, "igboapi_pspk03026": 3010, "igboapi_pspk03027": 3011, "igboapi_pspk03028": 3012, "igboapi_pspk03029": 3013, "igboapi_pspk03030": 3014, "igboapi_pspk03031": 3015, "igboapi_pspk03032": 3016, "igboapi_pspk03033": 3017, "igboapi_pspk03034": 3018, "igboapi_pspk03035": 3019, "igboapi_pspk03036": 3020, "igboapi_pspk03037": 3021, "igboapi_pspk03038": 3022, "igboapi_pspk03039": 3023, "igboapi_pspk03040": 3024, "igboapi_pspk03041": 3025, "igboapi_pspk03042": 3026, "igboapi_pspk03043": 3027, "igboapi_pspk03044": 3028, "igboapi_pspk03045": 3029, "igboapi_pspk03046": 3030, "igboapi_pspk03047": 3031, "igboapi_pspk03048": 3032, "igboapi_pspk03049": 3033, "igboapi_pspk03050": 3034, "igboapi_pspk03051": 3035, "igboapi_pspk03052": 3036, "igboapi_pspk03053": 3037, "igboapi_pspk03054": 3038, "igboapi_pspk03055": 3039, "igboapi_pspk03056": 3040, "igboapi_pspk03057": 3041, "igboapi_pspk03058": 3042, "igboapi_pspk03059": 3043, "igboapi_pspk03060": 3044, "igboapi_pspk03061": 3045, "igboapi_pspk03062": 3046, "igboapi_pspk03063": 3047, "igboapi_pspk03064": 3048, "igboapi_pspk03065": 3049, "igboapi_pspk03066": 3050, "igboapi_pspk03067": 3051, "igboapi_pspk03068": 3052, "igboapi_pspk03069": 3053, "igboapi_pspk03070": 3054, "igboapi_pspk03071": 3055, "igboapi_pspk03072": 3056, "igboapi_pspk03073": 3057, "igboapi_pspk03074": 3058, "igboapi_pspk03076": 3059, "igboapi_pspk03077": 3060, "igboapi_pspk03078": 3061, "igboapi_pspk03079": 3062, "igboapi_pspk03080": 3063, "igboapi_pspk03081": 3064, "igboapi_pspk03082": 3065, "igboapi_pspk03083": 3066, "igboapi_pspk03084": 3067, "igboapi_pspk03085": 3068, "igboapi_pspk03086": 3069, "igboapi_pspk03087": 3070, "igboapi_pspk03088": 3071, "igboapi_pspk03089": 3072, "igboapi_pspk03090": 3073, "igboapi_pspk03091": 3074, "igboapi_pspk03092": 3075, "igboapi_pspk03093": 3076, "igboapi_pspk03094": 3077, "igboapi_pspk03095": 3078, "igboapi_pspk03096": 3079, "igboapi_pspk03097": 3080, "igboapi_pspk03098": 3081, "igboapi_pspk03099": 3082, "igboapi_pspk03100": 3083, "igboapi_pspk03101": 3084, "igboapi_pspk03102": 3085, "igboapi_pspk03103": 3086, "igboapi_pspk03104": 3087, "igboapi_pspk03105": 3088, "igboapi_pspk03106": 3089, "igboapi_pspk03107": 3090, "igboapi_pspk03108": 3091, "igboapi_pspk03109": 3092, "igboapi_pspk03110": 3093, "igboapi_pspk03111": 3094, "igboapi_pspk03112": 3095, "igboapi_pspk03113": 3096, "igboapi_pspk03114": 3097, "igboapi_pspk03115": 3098, "igboapi_pspk03116": 3099, "igboapi_pspk03117": 3100, "igboapi_pspk03118": 3101, "igboapi_pspk03119": 3102, "igboapi_pspk03120": 3103, "igboapi_pspk03121": 3104, "igboapi_pspk03122": 3105, "igboapi_pspk03123": 3106, "igboapi_pspk03124": 3107, "igboapi_pspk03125": 3108, "igboapi_pspk03126": 3109, "igboapi_pspk03127": 3110, "igboapi_pspk03128": 3111, "igboapi_pspk03129": 3112, "igboapi_pspk03130": 3113, "igboapi_pspk03131": 3114, "igboapi_pspk03132": 3115, "igboapi_pspk03133": 3116, "igboapi_pspk03135": 3117, "igboapi_pspk03136": 3118, "igboapi_pspk03137": 3119, "igboapi_pspk03138": 3120, "igboapi_pspk03139": 3121, "igboapi_pspk03141": 3122, "igboapi_pspk03142": 3123, "igboapi_pspk03143": 3124, "igboapi_pspk03144": 3125, "igboapi_pspk03146": 3126, "igboapi_pspk03148": 3127, "igboapi_pspk03149": 3128, "igboapi_pspk03150": 3129, "igboapi_pspk03152": 3130, "igboapi_pspk03153": 3131, "igboapi_pspk03154": 3132, "igboapi_pspk03155": 3133, "igboapi_pspk03156": 3134, "igboapi_pspk03157": 3135, "igboapi_pspk03158": 3136, "igboapi_pspk03159": 3137, "igboapi_pspk03160": 3138, "igboapi_pspk03161": 3139, "igboapi_pspk03162": 3140, "igboapi_pspk03163": 3141, "igboapi_pspk03164": 3142, "igboapi_pspk03165": 3143, "igboapi_pspk03166": 3144, "igboapi_pspk03167": 3145, "igboapi_pspk03168": 3146, "igboapi_pspk03169": 3147, "igboapi_pspk03170": 3148, "igboapi_pspk03171": 3149, "igboapi_pspk03172": 3150, "igboapi_pspk03173": 3151, "igboapi_pspk03174": 3152, "igboapi_pspk03175": 3153, "igboapi_pspk03176": 3154, "igboapi_pspk03177": 3155, "igboapi_pspk03178": 3156, "igboapi_pspk03179": 3157, "igboapi_pspk03180": 3158, "igboapi_pspk03181": 3159, "igboapi_pspk03182": 3160, "igboapi_pspk03183": 3161, "igboapi_pspk03184": 3162, "igboapi_pspk03185": 3163, "igboapi_pspk03186": 3164, "igboapi_pspk03187": 3165, "igboapi_pspk03188": 3166, "igboapi_pspk03189": 3167, "igboapi_pspk03190": 3168, "igboapi_pspk03191": 3169, "igboapi_pspk03192": 3170, "igboapi_pspk03193": 3171, "igboapi_pspk03194": 3172, "igboapi_pspk03195": 3173, "igboapi_pspk03196": 3174, "igboapi_pspk03197": 3175, "igboapi_pspk03198": 3176, "igboapi_pspk03199": 3177, "igboapi_pspk03201": 3178, "igboapi_pspk03202": 3179, "igboapi_pspk03203": 3180, "igboapi_pspk03204": 3181, "igboapi_pspk03205": 3182, "igboapi_pspk03206": 3183, "igboapi_pspk03207": 3184, "igboapi_pspk03208": 3185, "igboapi_pspk03209": 3186, "igboapi_pspk03210": 3187, "igboapi_pspk03211": 3188, "igboapi_pspk03212": 3189, "igboapi_pspk03213": 3190, "igboapi_pspk03214": 3191, "igboapi_pspk03215": 3192, "igboapi_pspk03216": 3193, "igboapi_pspk03217": 3194, "igboapi_pspk03218": 3195, "igboapi_pspk03219": 3196, "igboapi_pspk03220": 3197, "igboapi_pspk03221": 3198, "igboapi_pspk03222": 3199, "igboapi_pspk03223": 3200, "igboapi_pspk03224": 3201, "igboapi_pspk03225": 3202, "igboapi_pspk03226": 3203, "igboapi_pspk03227": 3204, "igboapi_pspk03228": 3205, "igboapi_pspk03229": 3206, "igboapi_pspk03230": 3207, "igboapi_pspk03231": 3208, "igboapi_pspk03232": 3209, "igboapi_pspk03233": 3210, "igboapi_pspk03234": 3211, "igboapi_pspk03235": 3212, "igboapi_pspk03236": 3213, "igboapi_pspk03237": 3214, "igboapi_pspk03238": 3215, "igboapi_pspk03239": 3216, "igboapi_pspk03240": 3217, "igboapi_pspk03241": 3218, "igboapi_pspk03242": 3219, "igboapi_pspk03243": 3220, "igboapi_pspk03246": 3221, "igboapi_pspk03247": 3222, "igboapi_pspk03248": 3223, "igboapi_pspk03249": 3224, "igboapi_pspk03252": 3225, "igboapi_pspk03253": 3226, "igboapi_pspk03254": 3227, "igboapi_pspk03255": 3228, "igboapi_pspk03256": 3229, "igboapi_pspk03257": 3230, "igboapi_pspk03258": 3231, "igboapi_pspk03259": 3232, "igboapi_pspk03260": 3233, "igboapi_pspk03261": 3234, "igboapi_pspk03262": 3235, "igboapi_pspk03264": 3236, "igboapi_pspk03265": 3237, "igboapi_pspk03266": 3238, "igboapi_pspk03267": 3239, "igboapi_pspk03268": 3240, "igboapi_pspk03269": 3241, "igboapi_pspk03270": 3242, "igboapi_pspk03271": 3243, "igboapi_pspk03272": 3244, "igboapi_pspk03273": 3245, "igboapi_pspk03274": 3246, "igboapi_pspk03275": 3247, "igboapi_pspk03276": 3248, "igboapi_pspk03277": 3249, "igboapi_pspk03278": 3250, "igboapi_pspk03279": 3251, "igboapi_pspk03280": 3252, "igboapi_pspk03281": 3253, "igboapi_pspk03282": 3254, "igboapi_pspk03283": 3255, "igboapi_pspk03284": 3256, "igboapi_pspk03285": 3257, "igboapi_pspk03286": 3258, "igboapi_pspk03287": 3259, "igboapi_pspk03288": 3260, "igboapi_pspk03289": 3261, "igboapi_pspk03290": 3262, "igboapi_pspk03291": 3263, "igboapi_pspk03292": 3264, "igboapi_pspk03293": 3265, "igboapi_pspk03294": 3266, "igboapi_pspk03295": 3267, "igboapi_pspk03296": 3268, "igboapi_pspk03297": 3269, "igboapi_pspk03298": 3270, "igboapi_pspk03299": 3271, "igboapi_pspk03300": 3272, "igboapi_pspk03301": 3273, "igboapi_pspk03302": 3274, "igboapi_pspk03303": 3275, "igboapi_pspk03304": 3276, "igboapi_pspk03305": 3277, "igboapi_pspk03306": 3278, "igboapi_pspk03307": 3279, "igboapi_pspk03308": 3280, "igboapi_pspk03309": 3281, "igboapi_pspk03310": 3282, "igboapi_pspk03311": 3283, "igboapi_pspk03312": 3284, "igboapi_pspk03313": 3285, "igboapi_pspk03314": 3286, "igboapi_pspk03315": 3287, "igboapi_pspk03316": 3288, "igboapi_pspk03317": 3289, "igboapi_pspk03318": 3290, "igboapi_pspk03319": 3291, "igboapi_pspk03320": 3292, "igboapi_pspk03321": 3293, "igboapi_pspk03322": 3294, "igboapi_pspk03324": 3295, "igboapi_pspk03325": 3296, "igboapi_pspk03326": 3297, "igboapi_pspk03327": 3298, "igboapi_pspk03328": 3299, "igboapi_pspk03329": 3300, "igboapi_pspk03330": 3301, "igboapi_pspk03331": 3302, "igboapi_pspk03332": 3303, "igboapi_pspk03333": 3304, "igboapi_pspk03334": 3305, "igboapi_pspk03335": 3306, "igboapi_pspk03336": 3307, "igboapi_pspk03337": 3308, "igboapi_pspk03338": 3309, "igboapi_pspk03339": 3310, "igboapi_pspk03340": 3311, "igboapi_pspk03341": 3312, "igboapi_pspk03342": 3313, "igboapi_pspk03343": 3314, "igboapi_pspk03344": 3315, "igboapi_pspk03345": 3316, "igboapi_pspk03346": 3317, "igboapi_pspk03347": 3318, "igboapi_pspk03348": 3319, "igboapi_pspk03349": 3320, "igboapi_pspk03350": 3321, "igboapi_pspk03352": 3322, "igboapi_pspk03355": 3323, "igboapi_pspk03356": 3324, "igboapi_pspk03357": 3325, "igboapi_pspk03358": 3326, "igboapi_pspk03359": 3327, "igboapi_pspk03360": 3328, "igboapi_pspk03361": 3329, "igboapi_pspk03362": 3330, "igboapi_pspk03364": 3331, "igboapi_pspk03365": 3332, "igboapi_pspk03366": 3333, "igboapi_pspk03367": 3334, "igboapi_pspk03368": 3335, "igboapi_pspk03369": 3336, "igboapi_pspk03370": 3337, "igboapi_pspk03371": 3338, "igboapi_pspk03372": 3339, "igboapi_pspk03373": 3340, "igboapi_pspk03374": 3341, "igboapi_pspk03376": 3342, "igboapi_pspk03377": 3343, "igboapi_pspk03378": 3344, "igboapi_pspk03379": 3345, "igboapi_pspk03380": 3346, "igboapi_pspk03381": 3347, "igboapi_pspk03382": 3348, "igboapi_pspk03383": 3349, "igboapi_pspk03384": 3350, "igboapi_pspk03385": 3351, "igboapi_pspk03386": 3352, "igboapi_pspk03387": 3353, "igboapi_pspk03388": 3354, "igboapi_pspk03389": 3355, "igboapi_pspk03390": 3356, "igboapi_pspk03391": 3357, "igboapi_pspk03392": 3358, "igboapi_pspk03393": 3359, "igboapi_pspk03394": 3360, "igboapi_pspk03395": 3361, "igboapi_pspk03396": 3362, "igboapi_pspk03397": 3363, "igboapi_pspk03398": 3364, "igboapi_pspk03399": 3365, "igboapi_pspk03400": 3366, "igboapi_pspk03401": 3367, "igboapi_pspk03402": 3368, "igboapi_pspk03403": 3369, "igboapi_pspk03404": 3370, "igboapi_pspk03405": 3371, "igboapi_pspk03406": 3372, "igboapi_pspk03407": 3373, "igboapi_pspk03408": 3374, "igboapi_pspk03409": 3375, "igboapi_pspk03410": 3376, "igboapi_pspk03411": 3377, "igboapi_pspk03412": 3378, "igboapi_pspk03413": 3379, "igboapi_pspk03414": 3380, "igboapi_pspk03415": 3381, "igboapi_pspk03416": 3382, "igboapi_pspk03417": 3383, "igboapi_pspk03418": 3384, "igboapi_pspk03419": 3385, "igboapi_pspk03420": 3386, "igboapi_pspk03421": 3387, "igboapi_pspk03422": 3388, "igboapi_pspk03423": 3389, "igboapi_pspk03424": 3390, "igboapi_pspk03425": 3391, "igboapi_pspk03426": 3392, "igboapi_pspk03427": 3393, "igboapi_pspk03428": 3394, "igboapi_pspk03429": 3395, "igboapi_pspk03430": 3396, "igboapi_pspk03431": 3397, "igboapi_pspk03432": 3398, "igboapi_pspk03433": 3399, "igboapi_pspk03434": 3400, "igboapi_pspk03435": 3401, "igboapi_pspk03436": 3402, "igboapi_pspk03437": 3403, "igboapi_pspk03438": 3404, "igboapi_pspk03439": 3405, "igboapi_pspk03440": 3406, "igboapi_pspk03441": 3407, "igboapi_pspk03442": 3408, "igboapi_pspk03443": 3409, "igboapi_pspk03444": 3410, "igboapi_pspk03445": 3411, "igboapi_pspk03446": 3412, "igboapi_pspk03447": 3413, "igboapi_pspk03448": 3414, "igboapi_pspk03449": 3415, "igboapi_pspk03450": 3416, "igboapi_pspk03451": 3417, "igboapi_pspk03452": 3418, "igboapi_pspk03453": 3419, "igboapi_pspk03454": 3420, "igboapi_pspk03455": 3421, "igboapi_pspk03456": 3422, "igboapi_pspk03457": 3423, "igboapi_pspk03458": 3424, "igboapi_pspk03459": 3425, "igboapi_pspk03460": 3426, "igboapi_pspk03461": 3427, "igboapi_pspk03462": 3428, "igboapi_pspk03463": 3429, "igboapi_pspk03464": 3430, "igboapi_pspk03465": 3431, "igboapi_pspk03466": 3432, "igboapi_pspk03467": 3433, "igboapi_pspk03468": 3434, "igboapi_pspk03469": 3435, "igboapi_pspk03470": 3436, "igboapi_pspk03471": 3437, "igboapi_pspk03472": 3438, "igboapi_pspk03473": 3439, "igboapi_pspk03474": 3440, "igboapi_pspk03475": 3441, "igboapi_pspk03476": 3442, "igboapi_pspk03477": 3443, "igboapi_pspk03478": 3444, "igboapi_pspk03479": 3445, "igboapi_pspk03480": 3446, "igboapi_pspk03481": 3447, "igboapi_pspk03482": 3448, "igboapi_pspk03483": 3449, "igboapi_pspk03484": 3450, "igboapi_pspk03486": 3451, "igboapi_pspk03487": 3452, "igboapi_pspk03488": 3453, "igboapi_pspk03489": 3454, "igboapi_pspk03490": 3455, "igboapi_pspk03491": 3456, "igboapi_pspk03492": 3457, "igboapi_pspk03493": 3458, "igboapi_pspk03494": 3459, "igboapi_pspk03495": 3460, "igboapi_pspk03496": 3461, "igboapi_pspk03497": 3462, "igboapi_pspk03498": 3463, "igboapi_pspk03499": 3464, "igboapi_pspk03501": 3465, "igboapi_pspk03503": 3466, "igboapi_pspk03504": 3467, "igboapi_pspk03505": 3468, "igboapi_pspk03507": 3469, "igboapi_pspk03508": 3470, "igboapi_pspk03509": 3471, "igboapi_pspk03510": 3472, "igboapi_pspk03511": 3473, "igboapi_pspk03512": 3474, "igboapi_pspk03513": 3475, "igboapi_pspk03514": 3476, "igboapi_pspk03516": 3477, "igboapi_pspk03518": 3478, "igboapi_pspk03519": 3479, "igboapi_pspk03520": 3480, "igboapi_pspk03521": 3481, "igboapi_pspk03522": 3482, "igboapi_pspk03523": 3483, "igboapi_pspk03524": 3484, "igboapi_pspk03525": 3485, "igboapi_pspk03526": 3486, "igboapi_pspk03527": 3487, "igboapi_pspk03528": 3488, "igboapi_pspk03529": 3489, "igboapi_pspk03530": 3490, "igboapi_pspk03531": 3491, "igboapi_pspk03532": 3492, "igboapi_pspk03533": 3493, "igboapi_pspk03534": 3494, "igboapi_pspk03535": 3495, "igboapi_pspk03536": 3496, "igboapi_pspk03537": 3497, "igboapi_pspk03538": 3498, "igboapi_pspk03539": 3499, "igboapi_pspk03540": 3500, "igboapi_pspk03541": 3501, "igboapi_pspk03542": 3502, "igboapi_pspk03543": 3503, "igboapi_pspk03544": 3504, "igboapi_pspk03545": 3505, "igboapi_pspk03546": 3506, "igboapi_pspk03547": 3507, "igboapi_pspk03548": 3508, "igboapi_pspk03549": 3509, "igboapi_pspk03550": 3510, "igboapi_pspk03551": 3511, "igboapi_pspk03552": 3512, "igboapi_pspk03553": 3513, "igboapi_pspk03554": 3514, "igboapi_pspk03555": 3515, "igboapi_pspk03556": 3516, "igboapi_pspk03557": 3517, "igboapi_pspk03558": 3518, "igboapi_pspk03559": 3519, "igboapi_pspk03560": 3520, "igboapi_pspk03561": 3521, "igboapi_pspk03562": 3522, "igboapi_pspk03563": 3523, "igboapi_pspk03564": 3524, "igboapi_pspk03565": 3525, "igboapi_pspk03566": 3526, "igboapi_pspk03567": 3527, "igboapi_pspk03568": 3528, "igboapi_pspk03570": 3529, "igboapi_pspk03571": 3530, "igboapi_pspk03572": 3531, "igboapi_pspk03573": 3532, "igboapi_pspk03574": 3533, "igboapi_pspk03575": 3534, "igboapi_pspk03576": 3535, "igboapi_pspk03577": 3536, "igboapi_pspk03578": 3537, "igboapi_pspk03579": 3538, "igboapi_pspk03580": 3539, "igboapi_pspk03581": 3540, "igboapi_pspk03582": 3541, "igboapi_pspk03583": 3542, "igboapi_pspk03584": 3543, "igboapi_pspk03585": 3544, "igboapi_pspk03586": 3545, "igboapi_pspk03587": 3546, "igboapi_pspk03588": 3547, "igboapi_pspk03589": 3548, "igboapi_pspk03590": 3549, "igboapi_pspk03591": 3550, "igboapi_pspk03592": 3551, "igboapi_pspk03593": 3552, "igboapi_pspk03594": 3553, "igboapi_pspk03595": 3554, "igboapi_pspk03596": 3555, "igboapi_pspk03597": 3556, "igboapi_pspk03598": 3557, "igboapi_pspk03599": 3558, "igboapi_pspk03600": 3559, "igboapi_pspk03601": 3560, "igboapi_pspk03602": 3561, "igboapi_pspk03603": 3562, "igboapi_pspk03604": 3563, "igboapi_pspk03605": 3564, "igboapi_pspk03607": 3565, "igboapi_pspk03608": 3566, "igboapi_pspk03609": 3567, "igboapi_pspk03610": 3568, "igboapi_pspk03611": 3569, "igboapi_pspk03612": 3570, "igboapi_pspk03613": 3571, "igboapi_pspk03614": 3572, "igboapi_pspk03615": 3573, "igboapi_pspk03616": 3574, "igboapi_pspk03617": 3575, "igboapi_pspk03618": 3576, "igboapi_pspk03619": 3577, "igboapi_pspk03620": 3578, "igboapi_pspk03621": 3579, "igboapi_pspk03622": 3580, "igboapi_pspk03623": 3581, "igboapi_pspk03624": 3582, "igboapi_pspk03625": 3583, "igboapi_pspk03626": 3584, "igboapi_pspk03627": 3585, "igboapi_pspk03628": 3586, "igboapi_pspk03629": 3587, "igboapi_pspk03630": 3588, "igboapi_pspk03631": 3589, "igboapi_pspk03632": 3590, "igboapi_pspk03633": 3591, "igboapi_pspk03634": 3592, "igboapi_pspk03635": 3593, "igboapi_pspk03636": 3594, "igboapi_pspk03637": 3595, "igboapi_pspk03638": 3596, "igboapi_pspk03639": 3597, "igboapi_pspk03640": 3598, "igboapi_pspk03641": 3599, "igboapi_pspk03642": 3600, "igboapi_pspk03643": 3601, "igboapi_pspk03644": 3602, "igboapi_pspk03645": 3603, "igboapi_pspk03646": 3604, "igboapi_pspk03647": 3605, "igboapi_pspk03648": 3606, "igboapi_pspk03649": 3607, "igboapi_pspk03650": 3608, "igboapi_pspk03651": 3609, "igboapi_pspk03652": 3610, "igboapi_pspk03653": 3611, "igboapi_pspk03654": 3612, "igboapi_pspk03655": 3613, "igboapi_pspk03656": 3614, "igboapi_pspk03657": 3615, "igboapi_pspk03658": 3616, "igboapi_pspk03659": 3617, "igboapi_pspk03660": 3618, "igboapi_pspk03661": 3619, "igboapi_pspk03662": 3620, "igboapi_pspk03663": 3621, "igboapi_pspk03664": 3622, "igboapi_pspk03665": 3623, "igboapi_pspk03667": 3624, "igboapi_pspk03669": 3625, "igboapi_pspk03670": 3626, "igboapi_pspk03671": 3627, "igboapi_pspk03672": 3628, "igboapi_pspk03673": 3629, "igboapi_pspk03674": 3630, "igboapi_pspk03675": 3631, "igboapi_pspk03676": 3632, "igboapi_pspk03677": 3633, "igboapi_pspk03678": 3634, "igboapi_pspk03679": 3635, "igboapi_pspk03680": 3636, "igboapi_pspk03681": 3637, "igboapi_pspk03682": 3638, "igboapi_pspk03683": 3639, "igboapi_pspk03684": 3640, "igboapi_pspk03685": 3641, "igboapi_pspk03686": 3642, "igboapi_pspk03687": 3643, "igboapi_pspk03688": 3644, "igboapi_pspk03689": 3645, "igboapi_pspk03690": 3646, "igboapi_pspk03691": 3647, "igboapi_pspk03692": 3648, "igboapi_pspk03693": 3649, "igboapi_pspk03694": 3650, "igboapi_pspk03695": 3651, "igboapi_pspk03696": 3652, "igboapi_pspk03697": 3653, "igboapi_pspk03698": 3654, "igboapi_pspk03699": 3655, "igboapi_pspk03700": 3656, "igboapi_pspk03701": 3657, "igboapi_pspk03702": 3658, "igboapi_pspk03703": 3659, "igboapi_pspk03704": 3660, "igboapi_pspk03706": 3661, "igboapi_pspk03707": 3662, "igboapi_pspk03708": 3663, "igboapi_pspk03709": 3664, "igboapi_pspk03710": 3665, "igboapi_pspk03711": 3666, "igboapi_pspk03713": 3667, "igboapi_pspk03714": 3668, "igboapi_pspk03715": 3669, "igboapi_pspk03716": 3670, "igboapi_pspk03717": 3671, "igboapi_pspk03718": 3672, "igboapi_pspk03719": 3673, "igboapi_pspk03720": 3674, "igboapi_pspk03721": 3675, "igboapi_pspk03722": 3676, "igboapi_pspk03723": 3677, "igboapi_pspk03724": 3678, "igboapi_pspk03725": 3679, "igboapi_pspk03726": 3680, "igboapi_pspk03727": 3681, "igboapi_pspk03728": 3682, "igboapi_pspk03729": 3683, "igboapi_pspk03730": 3684, "igboapi_pspk03732": 3685, "igboapi_pspk03733": 3686, "igboapi_pspk03734": 3687, "igboapi_pspk03735": 3688, "igboapi_pspk03736": 3689, "igboapi_pspk03737": 3690, "igboapi_pspk03738": 3691, "igboapi_pspk03739": 3692, "igboapi_pspk03740": 3693, "igboapi_pspk03741": 3694, "igboapi_pspk03742": 3695, "igboapi_pspk03743": 3696, "igboapi_pspk03744": 3697, "igboapi_pspk03745": 3698, "igboapi_pspk03747": 3699, "igboapi_pspk03748": 3700, "igboapi_pspk03749": 3701, "igboapi_pspk03750": 3702, "igboapi_pspk03751": 3703, "igboapi_pspk03752": 3704, "igboapi_pspk03753": 3705, "igboapi_pspk03754": 3706, "igboapi_pspk03755": 3707, "igboapi_pspk03756": 3708, "igboapi_pspk03757": 3709, "igboapi_pspk03758": 3710, "igboapi_pspk03759": 3711, "igboapi_pspk03760": 3712, "igboapi_pspk03761": 3713, "igboapi_pspk03762": 3714, "igboapi_pspk03763": 3715, "igboapi_pspk03764": 3716, "igboapi_pspk03765": 3717, "igboapi_pspk03766": 3718, "igboapi_pspk03767": 3719, "igboapi_pspk03768": 3720, "igboapi_pspk03769": 3721, "igboapi_pspk03770": 3722, "igboapi_pspk03771": 3723, "igboapi_pspk03772": 3724, "igboapi_pspk03773": 3725, "igboapi_pspk03774": 3726, "igboapi_pspk03775": 3727, "igboapi_pspk03776": 3728, "igboapi_pspk03777": 3729, "igboapi_pspk03778": 3730, "igboapi_pspk03779": 3731, "igboapi_pspk03780": 3732, "igboapi_pspk03781": 3733, "igboapi_pspk03782": 3734, "igboapi_pspk03783": 3735, "igboapi_pspk03784": 3736, "igboapi_pspk03785": 3737, "igboapi_pspk03786": 3738, "igboapi_pspk03788": 3739, "igboapi_pspk03789": 3740, "igboapi_pspk03791": 3741, "igboapi_pspk03793": 3742, "igboapi_pspk03794": 3743, "igboapi_pspk03795": 3744, "igboapi_pspk03797": 3745, "igboapi_pspk03798": 3746, "igboapi_pspk03799": 3747, "igboapi_pspk03800": 3748, "igboapi_pspk03801": 3749, "igboapi_pspk03802": 3750, "igboapi_pspk03803": 3751, "igboapi_pspk03804": 3752, "igboapi_pspk03806": 3753, "igboapi_pspk03807": 3754, "igboapi_pspk03808": 3755, "igboapi_pspk03809": 3756, "igboapi_pspk03810": 3757, "igboapi_pspk03811": 3758, "igboapi_pspk03812": 3759, "igboapi_pspk03813": 3760, "igboapi_pspk03814": 3761, "igboapi_pspk03815": 3762, "igboapi_pspk03816": 3763, "igboapi_pspk03817": 3764, "igboapi_pspk03818": 3765, "igboapi_pspk03819": 3766, "igboapi_pspk03820": 3767, "igboapi_pspk03821": 3768, "igboapi_pspk03822": 3769, "igboapi_pspk03823": 3770, "igboapi_pspk03824": 3771, "igboapi_pspk03825": 3772, "igboapi_pspk03826": 3773, "igboapi_pspk03827": 3774, "igboapi_pspk03828": 3775, "igboapi_pspk03829": 3776, "igboapi_pspk03830": 3777, "igboapi_pspk03831": 3778, "igboapi_pspk03832": 3779, "igboapi_pspk03833": 3780, "igboapi_pspk03835": 3781, "igboapi_pspk03836": 3782, "igboapi_pspk03837": 3783, "igboapi_pspk03838": 3784, "igboapi_pspk03839": 3785, "igboapi_pspk03840": 3786, "igboapi_pspk03841": 3787, "igboapi_pspk03842": 3788, "igboapi_pspk03843": 3789, "igboapi_pspk03844": 3790, "igboapi_pspk03845": 3791, "igboapi_pspk03846": 3792, "igboapi_pspk03847": 3793, "igboapi_pspk03848": 3794, "igboapi_pspk03849": 3795, "igboapi_pspk03851": 3796, "igboapi_pspk03852": 3797, "igboapi_pspk03853": 3798, "igboapi_pspk03854": 3799, "igboapi_pspk03855": 3800, "igboapi_pspk03856": 3801, "igboapi_pspk03857": 3802, "igboapi_pspk03858": 3803, "igboapi_pspk03859": 3804, "igboapi_pspk03860": 3805, "igboapi_pspk03861": 3806, "igboapi_pspk03862": 3807, "igboapi_pspk03863": 3808, "igboapi_pspk03864": 3809, "igboapi_pspk03865": 3810, "igboapi_pspk03866": 3811, "igboapi_pspk03867": 3812, "igboapi_pspk03868": 3813, "igboapi_pspk03869": 3814, "igboapi_pspk03870": 3815, "igboapi_pspk03871": 3816, "igboapi_pspk03872": 3817, "igboapi_pspk03873": 3818, "igboapi_pspk03874": 3819, "igboapi_pspk03875": 3820, "igboapi_pspk03876": 3821, "igboapi_pspk03877": 3822, "igboapi_pspk03878": 3823, "igboapi_pspk03879": 3824, "igboapi_pspk03880": 3825, "igboapi_pspk03881": 3826, "igboapi_pspk03882": 3827, "igboapi_pspk03883": 3828, "igboapi_pspk03884": 3829, "igboapi_pspk03885": 3830, "igboapi_pspk03886": 3831, "igboapi_pspk03887": 3832, "igboapi_pspk03888": 3833, "igboapi_pspk03889": 3834, "igboapi_pspk03890": 3835, "igboapi_pspk03891": 3836, "igboapi_pspk03892": 3837, "igboapi_pspk03893": 3838, "igboapi_pspk03894": 3839, "igboapi_pspk03895": 3840, "igboapi_pspk03896": 3841, "igboapi_pspk03897": 3842, "igboapi_pspk03898": 3843, "igboapi_pspk03899": 3844, "igboapi_pspk03900": 3845, "igboapi_pspk03901": 3846, "igboapi_pspk03902": 3847, "igboapi_pspk03903": 3848, "igboapi_pspk03904": 3849, "igboapi_pspk03905": 3850, "igboapi_pspk03906": 3851, "igboapi_pspk03907": 3852, "igboapi_pspk03908": 3853, "igboapi_pspk03909": 3854, "igboapi_pspk03912": 3855, "igboapi_pspk03913": 3856, "igboapi_pspk03916": 3857, "igboapi_pspk03917": 3858, "igboapi_pspk03918": 3859, "igboapi_pspk03919": 3860, "igboapi_pspk03920": 3861, "igboapi_pspk03922": 3862, "igboapi_pspk03923": 3863, "igboapi_pspk03924": 3864, "igboapi_pspk03925": 3865, "igboapi_pspk03926": 3866, "igboapi_pspk03927": 3867, "igboapi_pspk03928": 3868, "igboapi_pspk03929": 3869, "igboapi_pspk03930": 3870, "igboapi_pspk03931": 3871, "igboapi_pspk03932": 3872, "igboapi_pspk03933": 3873, "igboapi_pspk03934": 3874, "igboapi_pspk03935": 3875, "igboapi_pspk03936": 3876, "igboapi_pspk03937": 3877, "igboapi_pspk03938": 3878, "igboapi_pspk03939": 3879, "igboapi_pspk03940": 3880, "igboapi_pspk03941": 3881, "igboapi_pspk03942": 3882, "igboapi_pspk03943": 3883, "igboapi_pspk03944": 3884, "igboapi_pspk03945": 3885, "igboapi_pspk03946": 3886, "igboapi_pspk03947": 3887, "igboapi_pspk03948": 3888, "igboapi_pspk03949": 3889, "igboapi_pspk03950": 3890, "igboapi_pspk03951": 3891, "igboapi_pspk03952": 3892, "igboapi_pspk03953": 3893, "igboapi_pspk03954": 3894, "igboapi_pspk03955": 3895, "igboapi_pspk03956": 3896, "igboapi_pspk03957": 3897, "igboapi_pspk03958": 3898, "igboapi_pspk03959": 3899, "igboapi_pspk03960": 3900, "igboapi_pspk03961": 3901, "igboapi_pspk03963": 3902, "igboapi_pspk03964": 3903, "igboapi_pspk03966": 3904, "igboapi_pspk03968": 3905, "igboapi_pspk03969": 3906, "igboapi_pspk03970": 3907, "igboapi_pspk03971": 3908, "igboapi_pspk03972": 3909, "igboapi_pspk03973": 3910, "igboapi_pspk03974": 3911, "igboapi_pspk03975": 3912, "igboapi_pspk03976": 3913, "igboapi_pspk03977": 3914, "igboapi_pspk03978": 3915, "igboapi_pspk03979": 3916, "igboapi_pspk03980": 3917, "igboapi_pspk03981": 3918, "igboapi_pspk03982": 3919, "igboapi_pspk03983": 3920, "igboapi_pspk03984": 3921, "igboapi_pspk03985": 3922, "igboapi_pspk03986": 3923, "igboapi_pspk03987": 3924, "igboapi_pspk03988": 3925, "igboapi_pspk03989": 3926, "igboapi_pspk03990": 3927, "igboapi_pspk03991": 3928, "igboapi_pspk03992": 3929, "igboapi_pspk03994": 3930, "igboapi_pspk03995": 3931, "igboapi_pspk03996": 3932, "igboapi_pspk03997": 3933, "igboapi_pspk03998": 3934, "igboapi_pspk03999": 3935, "igboapi_pspk04000": 3936, "igboapi_pspk04001": 3937, "igboapi_pspk04002": 3938, "igboapi_pspk04003": 3939, "igboapi_pspk04004": 3940, "igboapi_pspk04005": 3941, "igboapi_pspk04006": 3942, "igboapi_pspk04007": 3943, "igboapi_pspk04008": 3944, "igboapi_pspk04009": 3945, "igboapi_pspk04010": 3946, "igboapi_pspk04011": 3947, "igboapi_pspk04013": 3948, "igboapi_pspk04015": 3949, "igboapi_pspk04016": 3950, "igboapi_pspk04017": 3951, "igboapi_pspk04018": 3952, "igboapi_pspk04019": 3953, "igboapi_pspk04020": 3954, "igboapi_pspk04021": 3955, "igboapi_pspk04022": 3956, "igboapi_pspk04023": 3957, "igboapi_pspk04024": 3958, "igboapi_pspk04025": 3959, "igboapi_pspk04026": 3960, "igboapi_pspk04027": 3961, "igboapi_pspk04028": 3962, "igboapi_pspk04029": 3963, "igboapi_pspk04030": 3964, "igboapi_pspk04031": 3965, "igboapi_pspk04032": 3966, "igboapi_pspk04033": 3967, "igboapi_pspk04034": 3968, "igboapi_pspk04035": 3969, "igboapi_pspk04036": 3970, "igboapi_pspk04037": 3971, "igboapi_pspk04038": 3972, "igboapi_pspk04039": 3973, "igboapi_pspk04040": 3974, "igboapi_pspk04041": 3975, "igboapi_pspk04042": 3976, "igboapi_pspk04043": 3977, "igboapi_pspk04044": 3978, "igboapi_pspk04045": 3979, "igboapi_pspk04046": 3980, "igboapi_pspk04047": 3981, "igboapi_pspk04048": 3982, "igboapi_pspk04049": 3983, "igboapi_pspk04050": 3984, "igboapi_pspk04051": 3985, "igboapi_pspk04052": 3986, "igboapi_pspk04053": 3987, "igboapi_pspk04054": 3988, "igboapi_pspk04055": 3989, "igboapi_pspk04056": 3990, "igboapi_pspk04057": 3991, "igboapi_pspk04058": 3992, "igboapi_pspk04059": 3993, "igboapi_pspk04060": 3994, "igboapi_pspk04061": 3995, "igboapi_pspk04062": 3996, "igboapi_pspk04063": 3997, "igboapi_pspk04064": 3998, "igboapi_pspk04066": 3999, "igboapi_pspk04067": 4000, "igboapi_pspk04068": 4001, "igboapi_pspk04070": 4002, "igboapi_pspk04071": 4003, "igboapi_pspk04073": 4004, "igboapi_pspk04075": 4005, "igboapi_pspk04076": 4006, "igboapi_pspk04078": 4007, "igboapi_pspk04079": 4008, "igboapi_pspk04080": 4009, "igboapi_pspk04081": 4010, "igboapi_pspk04082": 4011, "igboapi_pspk04083": 4012, "igboapi_pspk04084": 4013, "igboapi_pspk04085": 4014, "igboapi_pspk04086": 4015, "igboapi_pspk04087": 4016, "igboapi_pspk04088": 4017, "igboapi_pspk04089": 4018, "igboapi_pspk04090": 4019, "igboapi_pspk04091": 4020, "igboapi_pspk04092": 4021, "igboapi_pspk04093": 4022, "igboapi_pspk04094": 4023, "igboapi_pspk04095": 4024, "igboapi_pspk04096": 4025, "igboapi_pspk04097": 4026, "igboapi_pspk04098": 4027, "igboapi_pspk04099": 4028, "igboapi_pspk04100": 4029, "igboapi_pspk04101": 4030, "igboapi_pspk04102": 4031, "igboapi_pspk04103": 4032, "igboapi_pspk04104": 4033, "igboapi_pspk04105": 4034, "igboapi_pspk04106": 4035, "igboapi_pspk04107": 4036, "igboapi_pspk04108": 4037, "igboapi_pspk04109": 4038, "igboapi_pspk04110": 4039, "igboapi_pspk04111": 4040, "igboapi_pspk04112": 4041, "igboapi_pspk04113": 4042, "igboapi_pspk04114": 4043, "igboapi_pspk04115": 4044, "igboapi_pspk04116": 4045, "igboapi_pspk04117": 4046, "igboapi_pspk04118": 4047, "igboapi_pspk04119": 4048, "igboapi_pspk04121": 4049, "igboapi_pspk04122": 4050, "igboapi_pspk04123": 4051, "igboapi_pspk04124": 4052, "igboapi_pspk04125": 4053, "igboapi_pspk04126": 4054, "igboapi_pspk04127": 4055, "igboapi_pspk04128": 4056, "igboapi_pspk04129": 4057, "igboapi_pspk04130": 4058, "igboapi_pspk04131": 4059, "igboapi_pspk04132": 4060, "igboapi_pspk04133": 4061, "igboapi_pspk04134": 4062, "igboapi_pspk04135": 4063, "igboapi_pspk04136": 4064, "igboapi_pspk04137": 4065, "igboapi_pspk04138": 4066, "igboapi_pspk04139": 4067, "igboapi_pspk04141": 4068, "igboapi_pspk04142": 4069, "igboapi_pspk04143": 4070, "igboapi_pspk04144": 4071, "igboapi_pspk04145": 4072, "igboapi_pspk04146": 4073, "igboapi_pspk04148": 4074, "igboapi_pspk04149": 4075, "igboapi_pspk04150": 4076, "igboapi_pspk04151": 4077, "igboapi_pspk04152": 4078, "igboapi_pspk04153": 4079, "igboapi_pspk04154": 4080, "igboapi_pspk04157": 4081, "igboapi_pspk04158": 4082, "igboapi_pspk04160": 4083, "igboapi_pspk04161": 4084, "igboapi_pspk04162": 4085, "igboapi_pspk04163": 4086, "igboapi_pspk04164": 4087, "igboapi_pspk04165": 4088, "igboapi_pspk04166": 4089, "igboapi_pspk04167": 4090, "igboapi_pspk04168": 4091, "igboapi_pspk04169": 4092, "igboapi_pspk04170": 4093, "igboapi_pspk04171": 4094, "igboapi_pspk04172": 4095, "igboapi_pspk04173": 4096, "igboapi_pspk04174": 4097, "igboapi_pspk04175": 4098, "igboapi_pspk04176": 4099, "igboapi_pspk04177": 4100, "igboapi_pspk04178": 4101, "igboapi_pspk04179": 4102, "igboapi_pspk04180": 4103, "igboapi_pspk04181": 4104, "igboapi_pspk04182": 4105, "igboapi_pspk04183": 4106, "igboapi_pspk04184": 4107, "igboapi_pspk04185": 4108, "igboapi_pspk04186": 4109, "igboapi_pspk04187": 4110, "igboapi_pspk04188": 4111, "igboapi_pspk04189": 4112, "igboapi_pspk04190": 4113, "igboapi_pspk04192": 4114, "igboapi_pspk04193": 4115, "igboapi_pspk04194": 4116, "igboapi_pspk04196": 4117, "igboapi_pspk04199": 4118, "igboapi_pspk04201": 4119, "igboapi_pspk04202": 4120, "igboapi_pspk04203": 4121, "igboapi_pspk04204": 4122, "igboapi_pspk04205": 4123, "igboapi_pspk04206": 4124, "igboapi_pspk04207": 4125, "igboapi_pspk04208": 4126, "igboapi_pspk04209": 4127, "igboapi_pspk04210": 4128, "igboapi_pspk04211": 4129, "igboapi_pspk04212": 4130, "igboapi_pspk04213": 4131, "igboapi_pspk04214": 4132, "igboapi_pspk04215": 4133, "igboapi_pspk04216": 4134, "igboapi_pspk04217": 4135, "igboapi_pspk04218": 4136, "igboapi_pspk04219": 4137, "igboapi_pspk04220": 4138, "igboapi_pspk04221": 4139, "igboapi_pspk04222": 4140, "igboapi_pspk04223": 4141, "igboapi_pspk04224": 4142, "igboapi_pspk04225": 4143, "igboapi_pspk04226": 4144, "igboapi_pspk04227": 4145, "igboapi_pspk04228": 4146, "igboapi_pspk04229": 4147, "igboapi_pspk04230": 4148, "igboapi_pspk04231": 4149, "igboapi_pspk04232": 4150, "igboapi_pspk04233": 4151, "igboapi_pspk04234": 4152, "igboapi_pspk04235": 4153, "igboapi_pspk04236": 4154, "igboapi_pspk04237": 4155, "igboapi_pspk04238": 4156, "igboapi_pspk04239": 4157, "igboapi_pspk04240": 4158, "igboapi_pspk04241": 4159, "igboapi_pspk04242": 4160, "igboapi_pspk04243": 4161, "igboapi_pspk04244": 4162, "igboapi_pspk04245": 4163, "igboapi_pspk04246": 4164, "igboapi_pspk04247": 4165, "igboapi_pspk04248": 4166, "igboapi_pspk04249": 4167, "igboapi_pspk04250": 4168, "igboapi_pspk04251": 4169, "igboapi_pspk04252": 4170, "igboapi_pspk04253": 4171, "igboapi_pspk04254": 4172, "igboapi_pspk04255": 4173, "igboapi_pspk04256": 4174, "igboapi_pspk04257": 4175, "igboapi_pspk04258": 4176, "igboapi_pspk04260": 4177, "igboapi_pspk04261": 4178, "igboapi_pspk04263": 4179, "igboapi_pspk04265": 4180, "igboapi_pspk04266": 4181, "igboapi_pspk04267": 4182, "igboapi_pspk04268": 4183, "igboapi_pspk04270": 4184, "igboapi_pspk04272": 4185, "igboapi_pspk04273": 4186, "igboapi_pspk04274": 4187, "igboapi_pspk04275": 4188, "igboapi_pspk04277": 4189, "igboapi_pspk04278": 4190, "igboapi_pspk04279": 4191, "igboapi_pspk04280": 4192, "igboapi_pspk04281": 4193, "igboapi_pspk04282": 4194, "igboapi_pspk04283": 4195, "igboapi_pspk04284": 4196, "igboapi_pspk04285": 4197, "igboapi_pspk04286": 4198, "igboapi_pspk04287": 4199, "igboapi_pspk04288": 4200, "igboapi_pspk04289": 4201, "igboapi_pspk04290": 4202, "igboapi_pspk04291": 4203, "igboapi_pspk04292": 4204, "igboapi_pspk04293": 4205, "igboapi_pspk04294": 4206, "igboapi_pspk04295": 4207, "igboapi_pspk04296": 4208, "igboapi_pspk04297": 4209, "igboapi_pspk04298": 4210, "igboapi_pspk04299": 4211, "igboapi_pspk04302": 4212, "igboapi_pspk04303": 4213, "igboapi_pspk04304": 4214, "igboapi_pspk04307": 4215, "igboapi_pspk04308": 4216, "igboapi_pspk04309": 4217, "igboapi_pspk04311": 4218, "igboapi_pspk04312": 4219, "igboapi_pspk04313": 4220, "igboapi_pspk04318": 4221, "igboapi_pspk04319": 4222, "igboapi_pspk04320": 4223, "igboapi_pspk04321": 4224, "igboapi_pspk04322": 4225, "igboapi_pspk04323": 4226, "igboapi_pspk04324": 4227, "igboapi_pspk04325": 4228, "igboapi_pspk04326": 4229, "igboapi_pspk04327": 4230, "igboapi_pspk04328": 4231, "igboapi_pspk04329": 4232, "igboapi_pspk04330": 4233, "igboapi_pspk04331": 4234, "igboapi_pspk04332": 4235, "igboapi_pspk04333": 4236, "igboapi_pspk04334": 4237, "igboapi_pspk04335": 4238, "igboapi_pspk04336": 4239, "igboapi_pspk04337": 4240, "igboapi_pspk04338": 4241, "igboapi_pspk04339": 4242, "igboapi_pspk04340": 4243, "igboapi_pspk04341": 4244, "igboapi_pspk04342": 4245, "igboapi_pspk04343": 4246, "igboapi_pspk04344": 4247, "igboapi_pspk04345": 4248, "igboapi_pspk04346": 4249, "igboapi_pspk04347": 4250, "igboapi_pspk04348": 4251, "igboapi_pspk04349": 4252, "igboapi_pspk04351": 4253, "igboapi_pspk04352": 4254, "igboapi_pspk04353": 4255, "igboapi_pspk04354": 4256, "igboapi_pspk04355": 4257, "igboapi_pspk04356": 4258, "igboapi_pspk04357": 4259, "igboapi_pspk04358": 4260, "igboapi_pspk04359": 4261, "igboapi_pspk04360": 4262, "igboapi_pspk04361": 4263, "igboapi_pspk04362": 4264, "igboapi_pspk04363": 4265, "igboapi_pspk04364": 4266, "igboapi_pspk04365": 4267, "igboapi_pspk04366": 4268, "igboapi_pspk04367": 4269, "igboapi_pspk04368": 4270, "igboapi_pspk04369": 4271, "igboapi_pspk04370": 4272, "igboapi_pspk04371": 4273, "igboapi_pspk04372": 4274, "igboapi_pspk04373": 4275, "igboapi_pspk04374": 4276, "igboapi_pspk04375": 4277, "igboapi_pspk04376": 4278, "igboapi_pspk04377": 4279, "igboapi_pspk04378": 4280, "igboapi_pspk04379": 4281, "igboapi_pspk04380": 4282, "igboapi_pspk04381": 4283, "igboapi_pspk04382": 4284, "igboapi_pspk04383": 4285, "igboapi_pspk04384": 4286, "igboapi_pspk04385": 4287, "igboapi_pspk04386": 4288, "igboapi_pspk04387": 4289, "igboapi_pspk04388": 4290, "igboapi_pspk04389": 4291, "igboapi_pspk04390": 4292, "igboapi_pspk04391": 4293, "igboapi_pspk04392": 4294, "igboapi_pspk04393": 4295, "igboapi_pspk04394": 4296, "igboapi_pspk04395": 4297, "igboapi_pspk04396": 4298, "igboapi_pspk04397": 4299, "igboapi_pspk04398": 4300, "igboapi_pspk04399": 4301, "igboapi_pspk04400": 4302, "igboapi_pspk04401": 4303, "igboapi_pspk04402": 4304, "igboapi_pspk04403": 4305, "igboapi_pspk04405": 4306, "igboapi_pspk04406": 4307, "igboapi_pspk04407": 4308, "igboapi_pspk04408": 4309, "igboapi_pspk04409": 4310, "igboapi_pspk04410": 4311, "igboapi_pspk04411": 4312, "igboapi_pspk04412": 4313, "igboapi_pspk04413": 4314, "igboapi_pspk04414": 4315, "igboapi_pspk04415": 4316, "igboapi_pspk04416": 4317, "igboapi_pspk04417": 4318, "igboapi_pspk04418": 4319, "igboapi_pspk04419": 4320, "igboapi_pspk04420": 4321, "igboapi_pspk04421": 4322, "igboapi_pspk04422": 4323, "igboapi_pspk04423": 4324, "igboapi_pspk04424": 4325, "igboapi_pspk04426": 4326, "igboapi_pspk04427": 4327, "igboapi_pspk04428": 4328, "igboapi_pspk04429": 4329, "igboapi_pspk04431": 4330, "igboapi_pspk04432": 4331, "igboapi_pspk04433": 4332, "igboapi_pspk04434": 4333, "igboapi_pspk04435": 4334, "igboapi_pspk04436": 4335, "igboapi_pspk04437": 4336, "igboapi_pspk04438": 4337, "igboapi_pspk04439": 4338, "igboapi_pspk04440": 4339, "igboapi_pspk04441": 4340, "igboapi_pspk04442": 4341, "igboapi_pspk04443": 4342, "igboapi_pspk04444": 4343, "igboapi_pspk04445": 4344, "igboapi_pspk04446": 4345, "igboapi_pspk04447": 4346, "igboapi_pspk04448": 4347, "igboapi_pspk04450": 4348, "igboapi_pspk04451": 4349, "igboapi_pspk04454": 4350, "igboapi_pspk04456": 4351, "igboapi_pspk04457": 4352, "igboapi_pspk04458": 4353, "igboapi_pspk04459": 4354, "igboapi_pspk04460": 4355, "igboapi_pspk04461": 4356, "igboapi_pspk04462": 4357, "igboapi_pspk04463": 4358, "igboapi_pspk04464": 4359, "igboapi_pspk04465": 4360, "igboapi_pspk04466": 4361, "igboapi_pspk04467": 4362, "igboapi_pspk04468": 4363, "igboapi_pspk04469": 4364, "igboapi_pspk04470": 4365, "igboapi_pspk04473": 4366, "igboapi_pspk04474": 4367, "igboapi_pspk04475": 4368, "igboapi_pspk04476": 4369, "igboapi_pspk04477": 4370, "igboapi_pspk04478": 4371, "igboapi_pspk04479": 4372, "igboapi_pspk04480": 4373, "igboapi_pspk04481": 4374, "igboapi_pspk04482": 4375, "igboapi_pspk04483": 4376, "igboapi_pspk04484": 4377, "igboapi_pspk04485": 4378, "igboapi_pspk04486": 4379, "igboapi_pspk04487": 4380, "igboapi_pspk04488": 4381, "igboapi_pspk04489": 4382, "igboapi_pspk04491": 4383, "igboapi_pspk04492": 4384, "igboapi_pspk04493": 4385, "igboapi_pspk04494": 4386, "igboapi_pspk04495": 4387, "igboapi_pspk04496": 4388, "igboapi_pspk04497": 4389, "igboapi_pspk04498": 4390, "igboapi_pspk04500": 4391, "igboapi_pspk04501": 4392, "igboapi_pspk04502": 4393, "igboapi_pspk04503": 4394, "igboapi_pspk04504": 4395, "igboapi_pspk04505": 4396, "igboapi_pspk04506": 4397, "igboapi_pspk04507": 4398, "igboapi_pspk04508": 4399, "igboapi_pspk04509": 4400, "igboapi_pspk04510": 4401, "igboapi_pspk04511": 4402, "igboapi_pspk04512": 4403, "igboapi_pspk04513": 4404, "igboapi_pspk04514": 4405, "igboapi_pspk04515": 4406, "igboapi_pspk04516": 4407, "igboapi_pspk04517": 4408, "igboapi_pspk04521": 4409, "igboapi_pspk04522": 4410, "igboapi_pspk04523": 4411, "igboapi_pspk04525": 4412, "igboapi_pspk04526": 4413, "igboapi_pspk04527": 4414, "igboapi_pspk04528": 4415, "igboapi_pspk04529": 4416, "igboapi_pspk04530": 4417, "igboapi_pspk04531": 4418, "igboapi_pspk04532": 4419, "igboapi_pspk04533": 4420, "igboapi_pspk04534": 4421, "igboapi_pspk04535": 4422, "igboapi_pspk04536": 4423, "igboapi_pspk04537": 4424, "igboapi_pspk04538": 4425, "igboapi_pspk04539": 4426, "igboapi_pspk04540": 4427, "igboapi_pspk04543": 4428, "igboapi_pspk04544": 4429, "igboapi_pspk04545": 4430, "igboapi_pspk04546": 4431, "igboapi_pspk04547": 4432, "igboapi_pspk04548": 4433, "igboapi_pspk04549": 4434, "igboapi_pspk04550": 4435, "igboapi_pspk04551": 4436, "igboapi_pspk04552": 4437, "igboapi_pspk04553": 4438, "igboapi_pspk04554": 4439, "igboapi_pspk04555": 4440, "igboapi_pspk04556": 4441, "igboapi_pspk04557": 4442, "igboapi_pspk04558": 4443, "igboapi_pspk04559": 4444, "igboapi_pspk04560": 4445, "igboapi_pspk04561": 4446, "igboapi_pspk04562": 4447, "igboapi_pspk04564": 4448, "igboapi_pspk04565": 4449, "igboapi_pspk04566": 4450, "igboapi_pspk04567": 4451, "igboapi_pspk04570": 4452, "igboapi_pspk04571": 4453, "igboapi_pspk04572": 4454, "igboapi_pspk04573": 4455, "igboapi_pspk04574": 4456, "igboapi_pspk04575": 4457, "igboapi_pspk04576": 4458, "igboapi_pspk04577": 4459, "igboapi_pspk04578": 4460, "igboapi_pspk04579": 4461, "igboapi_pspk04580": 4462, "igboapi_pspk04583": 4463, "igboapi_pspk04584": 4464, "igboapi_pspk04585": 4465, "igboapi_pspk04586": 4466, "igboapi_pspk04587": 4467, "igboapi_pspk04588": 4468, "igboapi_pspk04589": 4469, "igboapi_pspk04590": 4470, "igboapi_pspk04591": 4471, "igboapi_pspk04592": 4472, "igboapi_pspk04594": 4473, "igboapi_pspk04595": 4474, "igboapi_pspk04596": 4475, "igboapi_pspk04597": 4476, "igboapi_pspk04598": 4477, "igboapi_pspk04600": 4478, "igboapi_pspk04601": 4479, "igboapi_pspk04602": 4480, "igboapi_pspk04603": 4481, "igboapi_pspk04604": 4482, "igboapi_pspk04605": 4483, "igboapi_pspk04606": 4484, "igboapi_pspk04607": 4485, "igboapi_pspk04608": 4486, "igboapi_pspk04609": 4487, "igboapi_pspk04611": 4488, "igboapi_pspk04614": 4489, "igboapi_pspk04616": 4490, "igboapi_pspk04617": 4491, "igboapi_pspk04618": 4492, "igboapi_pspk04619": 4493, "igboapi_pspk04620": 4494, "igboapi_pspk04622": 4495, "igboapi_pspk04623": 4496, "igboapi_pspk04624": 4497, "igboapi_pspk04626": 4498, "igboapi_pspk04627": 4499, "igboapi_pspk04628": 4500, "igboapi_pspk04629": 4501, "igboapi_pspk04631": 4502, "igboapi_pspk04633": 4503, "igboapi_pspk04634": 4504, "igboapi_pspk04636": 4505, "igboapi_pspk04637": 4506, "igboapi_pspk04638": 4507, "igboapi_pspk04639": 4508, "igboapi_pspk04640": 4509, "igboapi_pspk04641": 4510, "igboapi_pspk04642": 4511, "igboapi_pspk04644": 4512, "igboapi_pspk04645": 4513, "igboapi_pspk04646": 4514, "igboapi_pspk04647": 4515, "igboapi_pspk04650": 4516, "igboapi_pspk04651": 4517, "igboapi_pspk04653": 4518, "igboapi_pspk04654": 4519, "igboapi_pspk04655": 4520, "igboapi_pspk04656": 4521, "igboapi_pspk04657": 4522, "igboapi_pspk04658": 4523, "igboapi_pspk04659": 4524, "igboapi_pspk04660": 4525, "igboapi_pspk04662": 4526, "igboapi_pspk04663": 4527, "igboapi_pspk04664": 4528, "igboapi_pspk04665": 4529, "igboapi_pspk04666": 4530, "igboapi_pspk04667": 4531, "igboapi_pspk04668": 4532, "igboapi_pspk04670": 4533, "igboapi_pspk04671": 4534, "igboapi_pspk04672": 4535, "igboapi_pspk04674": 4536, "igboapi_pspk04675": 4537, "igboapi_pspk04676": 4538, "igboapi_pspk04677": 4539, "igboapi_pspk04680": 4540, "igboapi_pspk04681": 4541, "igboapi_pspk04682": 4542, "igboapi_pspk04683": 4543, "igboapi_pspk04686": 4544, "igboapi_pspk04687": 4545, "igbosyn_Abia_0001": 4546, "igbosyn_Abia_0003": 4547, "igbosyn_Abia_0006": 4548, "igbosyn_Abia_0007": 4549, "igbosyn_Abia_0009": 4550, "igbosyn_Abia_0011": 4551, "igbosyn_Abia_0012": 4552, "igbosyn_Abia_0013": 4553, "igbosyn_Abia_0014": 4554, "igbosyn_Anambra_0004": 4555, "igbosyn_Anambra_0005": 4556, "igbosyn_Anambra_0006": 4557, "igbosyn_Anambra_0007": 4558, "igbosyn_Anambra_0009": 4559, "igbosyn_Anambra_0012": 4560, "igbosyn_Anambra_0013": 4561, "igbosyn_Anambra_0014": 4562, "igbosyn_Anambra_0015": 4563, "igbosyn_Ebonyi_0001": 4564, "igbosyn_Ebonyi_0002": 4565, "igbosyn_Ebonyi_0003": 4566, "igbosyn_Ebonyi_0004": 4567, "igbosyn_Ebonyi_0005": 4568, "igbosyn_Ebonyi_0008": 4569, "igbosyn_Ebonyi_0009": 4570, "igbosyn_Ebonyi_0010": 4571, "igbosyn_Ebonyi_0012": 4572, "igbosyn_Ebonyi_0015": 4573, "igbosyn_Ebonyi_0019": 4574, "igbosyn_Ebonyi_0020": 4575, "igbosyn_Ebonyi_0021": 4576, "igbosyn_Ebonyi_0024": 4577, "igbosyn_Ebonyi_0025": 4578, "igbosyn_Ebonyi_0026": 4579, "igbosyn_Enugu_0001": 4580, "igbosyn_Enugu_0002": 4581, "igbosyn_Enugu_0003": 4582, "igbosyn_Enugu_0008": 4583, "igbosyn_Enugu_0009": 4584, "igbosyn_Enugu_0010": 4585, "igbosyn_Enugu_0011": 4586, "igbosyn_Enugu_0013": 4587, "igbosyn_Enugu_0015": 4588, "igbosyn_Enugu_0016": 4589, "igbosyn_Enugu_0019": 4590, "igbosyn_Enugu_0020": 4591, "igbosyn_Enugu_0023": 4592, "igbosyn_Enugu_0024": 4593, "igbosyn_Enugu_0026": 4594, "igbosyn_Igbo Radio_0003": 4595, "igbosyn_Igbo Radio_0005": 4596, "igbosyn_Igbo Radio_0006": 4597, "igbosyn_Imo_0001": 4598, "igbosyn_Imo_0002": 4599, "igbosyn_Imo_0003": 4600, "igbosyn_Imo_0004": 4601, "igbosyn_Imo_0006": 4602, "igbosyn_Imo_0007": 4603, "igbosyn_Imo_0008": 4604, "igbosyn_Imo_0009": 4605, "igbosyn_Imo_0010": 4606, "igbosyn_Imo_0012": 4607, "igbosyn_Imo_0013": 4608, "igbosyn_Imo_0015": 4609, "igbosyn_Imo_0016": 4610, "igbosyn_Imo_0018": 4611, "igbosyn_Imo_0019": 4612, "igbosyn_Imo_0021": 4613, "igbosyn_Imo_0022": 4614, "igbosyn_Imo_0023": 4615, "igbosyn_Imo_0024": 4616, "igbosyn_Imo_0025": 4617, "igbosyn_Imo_0026": 4618, "igbosyn_Imo_0027": 4619, "naija_I001": 4620, "naija_I002": 4621, "naija_I003": 4622, "naija_I004": 4623, "naija_I006": 4624, "naija_I007": 4625, "naija_I009": 4626, "naija_I010": 4627, "naija_I011": 4628, "naija_I012": 4629, "naija_I013": 4630, "naija_I014": 4631, "naija_I015": 4632, "naija_I016": 4633, "naija_I017": 4634, "naija_I018": 4635, "naija_I019": 4636, "naija_I020": 4637, "naija_I021": 4638, "naija_I022": 4639, "naija_I026": 4640, "naija_I027": 4641, "naija_I029": 4642, "naija_I030": 4643, "naija_I031": 4644, "naija_I033": 4645, "naija_I034": 4646, "naija_I035": 4647, "naija_I036": 4648, "naija_I037": 4649, "naija_I039": 4650, "naija_I040": 4651, "naija_I042": 4652, "naija_I044": 4653, "naija_I045": 4654, "naija_I046": 4655, "naija_I047": 4656, "naija_I048": 4657, "naija_I049": 4658, "naija_I050": 4659, "naija_I051": 4660, "naija_I052": 4661, "naija_I054": 4662, "naija_I055": 4663, "naija_I057": 4664, "naija_I058": 4665, "naija_I059": 4666, "naija_I060": 4667, "naija_I061": 4668, "naija_I062": 4669, "naija_I064": 4670, "naija_I066": 4671, "naija_I067": 4672, "naija_I068": 4673, "naija_I069": 4674, "naija_I070": 4675, "obig_SPEAKER_01": 4676, "obig_SPEAKER_02": 4677, "obig_SPEAKER_03": 4678, "obig_SPEAKER_04": 4679, "obig_SPEAKER_05": 4680, "slr70_ngf_00295": 4681, "slr70_ngf_00610": 4682, "slr70_ngf_01208": 4683, "slr70_ngf_01523": 4684, "slr70_ngf_02121": 4685, "slr70_ngf_02436": 4686, "slr70_ngf_02484": 4687, "slr70_ngf_03349": 4688, "slr70_ngf_03397": 4689, "slr70_ngf_04310": 4690, "slr70_ngf_05223": 4691, "slr70_ngf_06136": 4692, "slr70_ngf_07049": 4693, "slr70_ngf_07508": 4694, "slr70_ngf_08421": 4695, "slr70_ngf_09334": 4696, "slr70_ngf_12484": 4697, "slr70_ngf_13397": 4698, "slr70_ngf_15223": 4699, "slr70_ngm_01523": 4700, "slr70_ngm_02436": 4701, "slr70_ngm_02484": 4702, "slr70_ngm_03349": 4703, "slr70_ngm_03397": 4704, "slr70_ngm_07049": 4705, "slr70_ngm_07508": 4706, "slr70_ngm_08784": 4707, "slr70_ngm_09334": 4708, "slr70_ngm_09697": 4709, "slr70_ngm_12484": 4710, "slr70_ngm_14310": 4711, "slr86_yof_00295": 4712, "slr86_yof_00610": 4713, "slr86_yof_01208": 4714, "slr86_yof_02121": 4715, "slr86_yof_02436": 4716, "slr86_yof_02484": 4717, "slr86_yof_03034": 4718, "slr86_yof_03349": 4719, "slr86_yof_03397": 4720, "slr86_yof_04310": 4721, "slr86_yof_05223": 4722, "slr86_yof_06136": 4723, "slr86_yof_07049": 4724, "slr86_yof_07505": 4725, "slr86_yof_07508": 4726, "slr86_yof_08421": 4727, "slr86_yof_08784": 4728, "slr86_yof_09334": 4729, "slr86_yof_09697": 4730, "slr86_yom_00295": 4731, "slr86_yom_00610": 4732, "slr86_yom_01208": 4733, "slr86_yom_01523": 4734, "slr86_yom_02121": 4735, "slr86_yom_02436": 4736, "slr86_yom_02484": 4737, "slr86_yom_03034": 4738, "slr86_yom_03397": 4739, "slr86_yom_04310": 4740, "slr86_yom_06136": 4741, "slr86_yom_07049": 4742, "slr86_yom_07505": 4743, "slr86_yom_07508": 4744, "slr86_yom_08421": 4745, "slr86_yom_08784": 4746, "slr86_yom_09334": 4747, "syncorp_Abia_0002": 4748, "syncorp_Abia_0004": 4749, "syncorp_Abia_0005": 4750, "syncorp_Abia_0010": 4751, "syncorp_Anambra_0002": 4752, "syncorp_Anambra_0010": 4753, "syncorp_Anambra_0011": 4754, "syncorp_Ebonyi_0018": 4755, "syncorp_Ebonyi_11": 4756, "syncorp_Enugu_0014": 4757, "syncorp_Enugu_0025": 4758, "syncorp_Imo_0005": 4759, "syncorp_Imo_0011": 4760, "waxal_hau_1": 4761, "waxal_hau_2": 4762, "waxal_hau_3": 4763, "waxal_hau_4": 4764, "waxal_hau_5": 4765, "waxal_hau_6": 4766, "waxal_hau_7": 4767, "waxal_hau_8": 4768, "waxal_ibo_1": 4769, "waxal_ibo_2": 4770, "waxal_ibo_3": 4771, "waxal_ibo_4": 4772, "waxal_ibo_5": 4773, "waxal_ibo_6": 4774, "waxal_ibo_7": 4775, "waxal_ibo_8": 4776, "waxal_pcm_F1": 4777, "waxal_pcm_F2": 4778, "waxal_pcm_F3": 4779, "waxal_pcm_F4": 4780, "waxal_pcm_M1": 4781, "waxal_pcm_M2": 4782, "waxal_pcm_M3": 4783, "waxal_pcm_M4": 4784, "waxal_yor_1": 4785, "waxal_yor_2": 4786, "waxal_yor_3": 4787, "waxal_yor_4": 4788, "waxal_yor_5": 4789, "waxal_yor_6": 4790, "waxal_yor_7": 4791, "waxal_yor_8": 4792} \ No newline at end of file diff --git a/checkpoints/pro/flash_instruct/args.json b/checkpoints/pro/flash_instruct/args.json new file mode 100644 index 0000000000000000000000000000000000000000..c08adeec4a55a2ee20a78426443a408c876530e9 --- /dev/null +++ b/checkpoints/pro/flash_instruct/args.json @@ -0,0 +1 @@ +{"cmd": "train", "lm": "/home/dev/fi/run/best.pt", "out": "/home/dev/fi/run_dpo", "shard": "0/1", "k": 4, "lr": 1e-06, "beta": 0.1, "sft": 0.1, "bs": 8, "epochs": 2} diff --git a/checkpoints/pro/flash_instruct/best.pt b/checkpoints/pro/flash_instruct/best.pt new file mode 100644 index 0000000000000000000000000000000000000000..fe261d8763407e07b7cebe9cca47e6d01bd1611d --- /dev/null +++ b/checkpoints/pro/flash_instruct/best.pt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:87c3a7e9a806e89cf1b77cff78989ebe63938016f5d8066cf673d350c4cab3db +size 2047789708 diff --git a/checkpoints/pro/flash_instruct/config.json b/checkpoints/pro/flash_instruct/config.json new file mode 100644 index 0000000000000000000000000000000000000000..2ac2478f5189b0a81888faef14d9a1dfab884304 --- /dev/null +++ b/checkpoints/pro/flash_instruct/config.json @@ -0,0 +1,14 @@ +{ + "engine": "cosyvoice3", + "base": "FunAudioLLM/Fun-CosyVoice3-0.5B-2512", + "rate": "normal", + "sampler": "silexcap", + "one_pass": true, + "instruct": true, + "cfg": 2.0, + "text_pauses": true, + "sha256_best_pt": "87c3a7e9a806e89cf1b77cff78989ebe63938016f5d8066cf673d350c4cab3db", + "autoresearch_artifact": "art-dbr28 (private)", + "training": "Flash-DPO LM -> Flash-Instruct SFT (3,000 steps, ~100.9k licensed clips with audio-derived pace/pause/energy labels, natural-language instruction in the CosyVoice3 system-prompt slot, 18% default-prompt items, pause/energy control embeddings) -> DPO polish (700 prompts x 4 samples, 564 preference pairs, reward = CER + pace and pause match to native + clipped-ending penalty + small UTMOS guard; 134 steps, beta 0.1, lr 1e-6, SFT weight 0.1; final step kept, val pref-acc 0.61 (0.75 after epoch 1, 28-pair val set))", + "data": "licensed only (no African Voices / WaZoBiaSpeech, no NaijaVoices)" +} diff --git a/checkpoints/pro/flash_instruct/log.jsonl b/checkpoints/pro/flash_instruct/log.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..ad55ee0bca3c7788728597fdaccc6cc55c220d02 --- /dev/null +++ b/checkpoints/pro/flash_instruct/log.jsonl @@ -0,0 +1,16 @@ +{"step": 0, "val": {"acc": 0.0, "margin": 0.0}} +{"step": 10, "of": 134, "loss": 0.9194, "acc": 0.625, "margin": 0.023, "gn": 64.85, "min": 0.1} +{"step": 20, "of": 134, "loss": 0.9342, "acc": 0.875, "margin": 0.019, "gn": 75.36, "min": 0.1} +{"step": 30, "of": 134, "loss": 0.8986, "acc": 0.5, "margin": 0.06, "gn": 64.13, "min": 0.2} +{"step": 40, "of": 134, "loss": 0.9182, "acc": 0.625, "margin": 0.03, "gn": 83.13, "min": 0.2} +{"step": 50, "of": 134, "loss": 0.9765, "acc": 0.375, "margin": -0.053, "gn": 77.84, "min": 0.3} +{"step": 60, "of": 134, "loss": 0.8475, "acc": 0.875, "margin": 0.203, "gn": 61.06, "min": 0.3} +{"epoch": 1, "step": 67, "val": {"acc": 0.75, "margin": 0.12087846860023481}} +{"step": 70, "of": 134, "loss": 0.5091, "acc": 1.0, "margin": 1.357, "gn": 38.83, "min": 0.4} +{"step": 80, "of": 134, "loss": 0.4715, "acc": 1.0, "margin": 1.605, "gn": 27.66, "min": 0.5} +{"step": 90, "of": 134, "loss": 0.5161, "acc": 1.0, "margin": 1.419, "gn": 33.8, "min": 0.5} +{"step": 100, "of": 134, "loss": 0.4414, "acc": 1.0, "margin": 1.709, "gn": 27.54, "min": 0.5} +{"step": 110, "of": 134, "loss": 0.5218, "acc": 1.0, "margin": 1.279, "gn": 32.5, "min": 0.6} +{"step": 120, "of": 134, "loss": 0.4836, "acc": 1.0, "margin": 1.426, "gn": 34.52, "min": 0.6} +{"step": 130, "of": 134, "loss": 0.5242, "acc": 1.0, "margin": 1.501, "gn": 33.22, "min": 0.7} +{"epoch": 2, "step": 134, "val": {"acc": 0.6071428571428571, "margin": 0.09966237439740715}} diff --git a/checkpoints/pro/flash_instruct/spk2id.json b/checkpoints/pro/flash_instruct/spk2id.json new file mode 100644 index 0000000000000000000000000000000000000000..99c495c8ea3d04d296083d029d8d329659f42ea0 --- /dev/null +++ b/checkpoints/pro/flash_instruct/spk2id.json @@ -0,0 +1 @@ +{"bible_hau": 0, "bible_yor": 1, "fleurs_eng_FEMALE_c000": 2, "fleurs_eng_FEMALE_c001": 3, "fleurs_eng_FEMALE_c002": 4, "fleurs_eng_FEMALE_c003": 5, "fleurs_eng_FEMALE_c004": 6, "fleurs_eng_FEMALE_c005": 7, "fleurs_eng_FEMALE_c006": 8, "fleurs_eng_FEMALE_c007": 9, "fleurs_eng_FEMALE_c008": 10, "fleurs_eng_FEMALE_c009": 11, "fleurs_eng_FEMALE_c010": 12, "fleurs_eng_FEMALE_c012": 13, "fleurs_eng_MALE_c000": 14, "fleurs_eng_MALE_c001": 15, "fleurs_eng_MALE_c002": 16, "fleurs_eng_MALE_c003": 17, "fleurs_eng_MALE_c004": 18, "fleurs_eng_MALE_c005": 19, "fleurs_eng_MALE_c006": 20, "fleurs_eng_MALE_c007": 21, "fleurs_hau_FEMALE_c000": 22, "fleurs_hau_FEMALE_c001": 23, "fleurs_hau_FEMALE_c003": 24, "fleurs_hau_FEMALE_misc": 25, "fleurs_hau_MALE_c000": 26, "fleurs_hau_MALE_c002": 27, "fleurs_hau_MALE_c003": 28, "fleurs_hau_MALE_c004": 29, "fleurs_hau_MALE_misc": 30, "fleurs_ibo_FEMALE_c000": 31, "fleurs_ibo_FEMALE_c001": 32, "fleurs_ibo_FEMALE_c003": 33, "fleurs_ibo_FEMALE_c004": 34, "fleurs_ibo_FEMALE_misc": 35, "fleurs_ibo_MALE_c000": 36, "fleurs_ibo_MALE_c001": 37, "fleurs_ibo_MALE_c002": 38, "fleurs_ibo_MALE_c004": 39, "fleurs_ibo_MALE_c005": 40, "fleurs_ibo_MALE_misc": 41, "fleurs_yor_FEMALE_c000": 42, "fleurs_yor_FEMALE_c001": 43, "fleurs_yor_FEMALE_c002": 44, "fleurs_yor_MALE_c000": 45, "fleurs_yor_MALE_c001": 46, "fleurs_yor_MALE_misc": 47, "igboapi_pspk00000": 48, "igboapi_pspk00001": 49, "igboapi_pspk00002": 50, "igboapi_pspk00003": 51, "igboapi_pspk00004": 52, "igboapi_pspk00005": 53, "igboapi_pspk00006": 54, "igboapi_pspk00007": 55, "igboapi_pspk00008": 56, "igboapi_pspk00009": 57, "igboapi_pspk00010": 58, "igboapi_pspk00011": 59, "igboapi_pspk00012": 60, "igboapi_pspk00013": 61, "igboapi_pspk00014": 62, "igboapi_pspk00015": 63, "igboapi_pspk00016": 64, "igboapi_pspk00017": 65, "igboapi_pspk00018": 66, "igboapi_pspk00019": 67, "igboapi_pspk00020": 68, "igboapi_pspk00022": 69, "igboapi_pspk00023": 70, "igboapi_pspk00024": 71, "igboapi_pspk00025": 72, "igboapi_pspk00026": 73, "igboapi_pspk00027": 74, "igboapi_pspk00028": 75, "igboapi_pspk00029": 76, "igboapi_pspk00030": 77, "igboapi_pspk00031": 78, "igboapi_pspk00032": 79, "igboapi_pspk00033": 80, "igboapi_pspk00034": 81, "igboapi_pspk00035": 82, "igboapi_pspk00036": 83, "igboapi_pspk00037": 84, "igboapi_pspk00038": 85, "igboapi_pspk00039": 86, "igboapi_pspk00040": 87, "igboapi_pspk00041": 88, "igboapi_pspk00042": 89, "igboapi_pspk00043": 90, "igboapi_pspk00044": 91, "igboapi_pspk00045": 92, "igboapi_pspk00046": 93, "igboapi_pspk00047": 94, "igboapi_pspk00048": 95, "igboapi_pspk00049": 96, "igboapi_pspk00050": 97, "igboapi_pspk00051": 98, "igboapi_pspk00052": 99, "igboapi_pspk00053": 100, "igboapi_pspk00054": 101, "igboapi_pspk00055": 102, "igboapi_pspk00056": 103, "igboapi_pspk00057": 104, "igboapi_pspk00058": 105, "igboapi_pspk00059": 106, "igboapi_pspk00060": 107, "igboapi_pspk00061": 108, "igboapi_pspk00062": 109, "igboapi_pspk00063": 110, "igboapi_pspk00064": 111, "igboapi_pspk00065": 112, "igboapi_pspk00066": 113, "igboapi_pspk00067": 114, "igboapi_pspk00068": 115, "igboapi_pspk00069": 116, "igboapi_pspk00070": 117, "igboapi_pspk00071": 118, "igboapi_pspk00072": 119, "igboapi_pspk00073": 120, "igboapi_pspk00074": 121, "igboapi_pspk00075": 122, "igboapi_pspk00076": 123, "igboapi_pspk00077": 124, "igboapi_pspk00078": 125, "igboapi_pspk00079": 126, "igboapi_pspk00080": 127, "igboapi_pspk00081": 128, "igboapi_pspk00082": 129, "igboapi_pspk00083": 130, "igboapi_pspk00084": 131, "igboapi_pspk00085": 132, "igboapi_pspk00086": 133, "igboapi_pspk00087": 134, "igboapi_pspk00088": 135, "igboapi_pspk00090": 136, "igboapi_pspk00091": 137, "igboapi_pspk00092": 138, "igboapi_pspk00093": 139, "igboapi_pspk00094": 140, "igboapi_pspk00095": 141, "igboapi_pspk00096": 142, "igboapi_pspk00097": 143, "igboapi_pspk00098": 144, "igboapi_pspk00099": 145, "igboapi_pspk00100": 146, "igboapi_pspk00101": 147, "igboapi_pspk00102": 148, "igboapi_pspk00103": 149, "igboapi_pspk00104": 150, "igboapi_pspk00105": 151, "igboapi_pspk00106": 152, "igboapi_pspk00107": 153, "igboapi_pspk00108": 154, "igboapi_pspk00109": 155, "igboapi_pspk00110": 156, "igboapi_pspk00111": 157, "igboapi_pspk00112": 158, "igboapi_pspk00113": 159, "igboapi_pspk00114": 160, "igboapi_pspk00115": 161, "igboapi_pspk00116": 162, "igboapi_pspk00117": 163, "igboapi_pspk00118": 164, "igboapi_pspk00119": 165, "igboapi_pspk00120": 166, "igboapi_pspk00121": 167, "igboapi_pspk00122": 168, "igboapi_pspk00123": 169, "igboapi_pspk00124": 170, "igboapi_pspk00125": 171, "igboapi_pspk00126": 172, "igboapi_pspk00127": 173, "igboapi_pspk00128": 174, "igboapi_pspk00130": 175, "igboapi_pspk00131": 176, "igboapi_pspk00132": 177, "igboapi_pspk00133": 178, "igboapi_pspk00134": 179, "igboapi_pspk00135": 180, "igboapi_pspk00136": 181, "igboapi_pspk00137": 182, "igboapi_pspk00138": 183, "igboapi_pspk00139": 184, "igboapi_pspk00140": 185, "igboapi_pspk00141": 186, "igboapi_pspk00142": 187, "igboapi_pspk00143": 188, "igboapi_pspk00144": 189, "igboapi_pspk00145": 190, "igboapi_pspk00146": 191, "igboapi_pspk00147": 192, "igboapi_pspk00148": 193, "igboapi_pspk00149": 194, "igboapi_pspk00150": 195, "igboapi_pspk00151": 196, "igboapi_pspk00152": 197, "igboapi_pspk00153": 198, "igboapi_pspk00154": 199, "igboapi_pspk00155": 200, "igboapi_pspk00156": 201, "igboapi_pspk00157": 202, "igboapi_pspk00158": 203, "igboapi_pspk00159": 204, "igboapi_pspk00160": 205, "igboapi_pspk00161": 206, "igboapi_pspk00162": 207, "igboapi_pspk00163": 208, "igboapi_pspk00164": 209, "igboapi_pspk00165": 210, "igboapi_pspk00166": 211, "igboapi_pspk00167": 212, "igboapi_pspk00168": 213, "igboapi_pspk00169": 214, "igboapi_pspk00170": 215, "igboapi_pspk00171": 216, "igboapi_pspk00172": 217, "igboapi_pspk00173": 218, "igboapi_pspk00174": 219, "igboapi_pspk00175": 220, "igboapi_pspk00176": 221, "igboapi_pspk00177": 222, "igboapi_pspk00178": 223, "igboapi_pspk00179": 224, "igboapi_pspk00180": 225, "igboapi_pspk00181": 226, "igboapi_pspk00182": 227, "igboapi_pspk00183": 228, "igboapi_pspk00184": 229, "igboapi_pspk00185": 230, "igboapi_pspk00186": 231, "igboapi_pspk00187": 232, "igboapi_pspk00188": 233, "igboapi_pspk00189": 234, "igboapi_pspk00190": 235, "igboapi_pspk00191": 236, "igboapi_pspk00192": 237, "igboapi_pspk00193": 238, "igboapi_pspk00194": 239, "igboapi_pspk00195": 240, "igboapi_pspk00196": 241, "igboapi_pspk00197": 242, "igboapi_pspk00198": 243, "igboapi_pspk00199": 244, "igboapi_pspk00200": 245, "igboapi_pspk00201": 246, "igboapi_pspk00202": 247, "igboapi_pspk00203": 248, "igboapi_pspk00204": 249, "igboapi_pspk00205": 250, "igboapi_pspk00206": 251, "igboapi_pspk00207": 252, "igboapi_pspk00208": 253, "igboapi_pspk00209": 254, "igboapi_pspk00210": 255, "igboapi_pspk00211": 256, "igboapi_pspk00212": 257, "igboapi_pspk00213": 258, "igboapi_pspk00214": 259, "igboapi_pspk00215": 260, "igboapi_pspk00216": 261, "igboapi_pspk00217": 262, "igboapi_pspk00218": 263, "igboapi_pspk00219": 264, "igboapi_pspk00220": 265, "igboapi_pspk00221": 266, "igboapi_pspk00222": 267, "igboapi_pspk00223": 268, "igboapi_pspk00224": 269, "igboapi_pspk00225": 270, "igboapi_pspk00226": 271, "igboapi_pspk00227": 272, "igboapi_pspk00228": 273, "igboapi_pspk00229": 274, "igboapi_pspk00230": 275, "igboapi_pspk00231": 276, "igboapi_pspk00232": 277, "igboapi_pspk00233": 278, "igboapi_pspk00234": 279, "igboapi_pspk00235": 280, "igboapi_pspk00236": 281, "igboapi_pspk00237": 282, "igboapi_pspk00238": 283, "igboapi_pspk00239": 284, "igboapi_pspk00240": 285, "igboapi_pspk00241": 286, "igboapi_pspk00242": 287, "igboapi_pspk00243": 288, "igboapi_pspk00244": 289, "igboapi_pspk00245": 290, "igboapi_pspk00246": 291, "igboapi_pspk00247": 292, "igboapi_pspk00248": 293, "igboapi_pspk00249": 294, "igboapi_pspk00250": 295, "igboapi_pspk00251": 296, "igboapi_pspk00252": 297, "igboapi_pspk00253": 298, "igboapi_pspk00254": 299, "igboapi_pspk00255": 300, "igboapi_pspk00256": 301, "igboapi_pspk00257": 302, "igboapi_pspk00258": 303, "igboapi_pspk00259": 304, "igboapi_pspk00260": 305, "igboapi_pspk00261": 306, "igboapi_pspk00262": 307, "igboapi_pspk00263": 308, "igboapi_pspk00264": 309, "igboapi_pspk00265": 310, "igboapi_pspk00266": 311, "igboapi_pspk00267": 312, "igboapi_pspk00268": 313, "igboapi_pspk00269": 314, "igboapi_pspk00270": 315, "igboapi_pspk00271": 316, "igboapi_pspk00272": 317, "igboapi_pspk00273": 318, "igboapi_pspk00274": 319, "igboapi_pspk00275": 320, "igboapi_pspk00276": 321, "igboapi_pspk00277": 322, "igboapi_pspk00278": 323, "igboapi_pspk00279": 324, "igboapi_pspk00280": 325, "igboapi_pspk00281": 326, "igboapi_pspk00282": 327, "igboapi_pspk00283": 328, "igboapi_pspk00284": 329, "igboapi_pspk00285": 330, "igboapi_pspk00286": 331, "igboapi_pspk00287": 332, "igboapi_pspk00288": 333, "igboapi_pspk00289": 334, "igboapi_pspk00290": 335, "igboapi_pspk00291": 336, "igboapi_pspk00292": 337, "igboapi_pspk00293": 338, "igboapi_pspk00294": 339, "igboapi_pspk00295": 340, "igboapi_pspk00296": 341, "igboapi_pspk00297": 342, "igboapi_pspk00298": 343, "igboapi_pspk00299": 344, "igboapi_pspk00300": 345, "igboapi_pspk00301": 346, "igboapi_pspk00302": 347, "igboapi_pspk00303": 348, "igboapi_pspk00304": 349, "igboapi_pspk00305": 350, "igboapi_pspk00306": 351, "igboapi_pspk00307": 352, "igboapi_pspk00308": 353, "igboapi_pspk00309": 354, "igboapi_pspk00310": 355, "igboapi_pspk00311": 356, "igboapi_pspk00312": 357, "igboapi_pspk00313": 358, "igboapi_pspk00314": 359, "igboapi_pspk00315": 360, "igboapi_pspk00316": 361, "igboapi_pspk00317": 362, "igboapi_pspk00318": 363, "igboapi_pspk00319": 364, "igboapi_pspk00320": 365, "igboapi_pspk00321": 366, "igboapi_pspk00322": 367, "igboapi_pspk00323": 368, "igboapi_pspk00324": 369, "igboapi_pspk00325": 370, "igboapi_pspk00326": 371, "igboapi_pspk00327": 372, "igboapi_pspk00328": 373, "igboapi_pspk00329": 374, "igboapi_pspk00330": 375, "igboapi_pspk00331": 376, "igboapi_pspk00332": 377, "igboapi_pspk00333": 378, "igboapi_pspk00335": 379, "igboapi_pspk00336": 380, "igboapi_pspk00337": 381, "igboapi_pspk00338": 382, "igboapi_pspk00339": 383, "igboapi_pspk00340": 384, "igboapi_pspk00341": 385, "igboapi_pspk00342": 386, "igboapi_pspk00343": 387, "igboapi_pspk00344": 388, "igboapi_pspk00345": 389, "igboapi_pspk00346": 390, "igboapi_pspk00347": 391, "igboapi_pspk00348": 392, "igboapi_pspk00349": 393, "igboapi_pspk00350": 394, "igboapi_pspk00351": 395, "igboapi_pspk00352": 396, "igboapi_pspk00353": 397, "igboapi_pspk00354": 398, "igboapi_pspk00355": 399, "igboapi_pspk00356": 400, "igboapi_pspk00357": 401, "igboapi_pspk00358": 402, "igboapi_pspk00359": 403, "igboapi_pspk00360": 404, "igboapi_pspk00361": 405, "igboapi_pspk00362": 406, "igboapi_pspk00363": 407, "igboapi_pspk00364": 408, "igboapi_pspk00365": 409, "igboapi_pspk00366": 410, "igboapi_pspk00367": 411, "igboapi_pspk00368": 412, "igboapi_pspk00369": 413, "igboapi_pspk00370": 414, "igboapi_pspk00371": 415, "igboapi_pspk00372": 416, "igboapi_pspk00373": 417, "igboapi_pspk00374": 418, "igboapi_pspk00375": 419, "igboapi_pspk00376": 420, "igboapi_pspk00377": 421, "igboapi_pspk00378": 422, "igboapi_pspk00379": 423, "igboapi_pspk00380": 424, "igboapi_pspk00381": 425, "igboapi_pspk00382": 426, "igboapi_pspk00383": 427, "igboapi_pspk00384": 428, "igboapi_pspk00385": 429, "igboapi_pspk00386": 430, "igboapi_pspk00387": 431, "igboapi_pspk00388": 432, "igboapi_pspk00389": 433, "igboapi_pspk00390": 434, "igboapi_pspk00391": 435, "igboapi_pspk00392": 436, "igboapi_pspk00393": 437, "igboapi_pspk00394": 438, "igboapi_pspk00395": 439, "igboapi_pspk00396": 440, "igboapi_pspk00397": 441, "igboapi_pspk00398": 442, "igboapi_pspk00399": 443, "igboapi_pspk00400": 444, "igboapi_pspk00401": 445, "igboapi_pspk00402": 446, "igboapi_pspk00403": 447, "igboapi_pspk00404": 448, "igboapi_pspk00405": 449, "igboapi_pspk00406": 450, "igboapi_pspk00407": 451, "igboapi_pspk00408": 452, "igboapi_pspk00409": 453, "igboapi_pspk00410": 454, "igboapi_pspk00411": 455, "igboapi_pspk00412": 456, "igboapi_pspk00413": 457, "igboapi_pspk00414": 458, "igboapi_pspk00415": 459, "igboapi_pspk00416": 460, "igboapi_pspk00417": 461, "igboapi_pspk00418": 462, "igboapi_pspk00419": 463, "igboapi_pspk00420": 464, "igboapi_pspk00421": 465, "igboapi_pspk00422": 466, "igboapi_pspk00423": 467, "igboapi_pspk00424": 468, "igboapi_pspk00425": 469, "igboapi_pspk00426": 470, "igboapi_pspk00427": 471, "igboapi_pspk00428": 472, "igboapi_pspk00429": 473, "igboapi_pspk00430": 474, "igboapi_pspk00431": 475, "igboapi_pspk00433": 476, "igboapi_pspk00434": 477, "igboapi_pspk00435": 478, "igboapi_pspk00436": 479, "igboapi_pspk00438": 480, "igboapi_pspk00439": 481, "igboapi_pspk00440": 482, "igboapi_pspk00441": 483, "igboapi_pspk00442": 484, "igboapi_pspk00443": 485, "igboapi_pspk00444": 486, "igboapi_pspk00445": 487, "igboapi_pspk00446": 488, "igboapi_pspk00447": 489, "igboapi_pspk00448": 490, "igboapi_pspk00449": 491, "igboapi_pspk00450": 492, "igboapi_pspk00451": 493, "igboapi_pspk00452": 494, "igboapi_pspk00453": 495, "igboapi_pspk00454": 496, "igboapi_pspk00455": 497, "igboapi_pspk00456": 498, "igboapi_pspk00457": 499, "igboapi_pspk00458": 500, "igboapi_pspk00459": 501, "igboapi_pspk00460": 502, "igboapi_pspk00461": 503, "igboapi_pspk00462": 504, "igboapi_pspk00463": 505, "igboapi_pspk00464": 506, "igboapi_pspk00465": 507, "igboapi_pspk00466": 508, "igboapi_pspk00467": 509, "igboapi_pspk00468": 510, "igboapi_pspk00469": 511, "igboapi_pspk00470": 512, "igboapi_pspk00471": 513, "igboapi_pspk00472": 514, "igboapi_pspk00473": 515, "igboapi_pspk00474": 516, "igboapi_pspk00475": 517, "igboapi_pspk00476": 518, "igboapi_pspk00477": 519, "igboapi_pspk00478": 520, "igboapi_pspk00479": 521, "igboapi_pspk00480": 522, "igboapi_pspk00481": 523, "igboapi_pspk00482": 524, "igboapi_pspk00483": 525, "igboapi_pspk00484": 526, "igboapi_pspk00485": 527, "igboapi_pspk00486": 528, "igboapi_pspk00487": 529, "igboapi_pspk00488": 530, "igboapi_pspk00489": 531, "igboapi_pspk00490": 532, "igboapi_pspk00491": 533, "igboapi_pspk00492": 534, "igboapi_pspk00493": 535, "igboapi_pspk00494": 536, "igboapi_pspk00495": 537, "igboapi_pspk00496": 538, "igboapi_pspk00497": 539, "igboapi_pspk00498": 540, "igboapi_pspk00499": 541, "igboapi_pspk00500": 542, "igboapi_pspk00501": 543, "igboapi_pspk00502": 544, "igboapi_pspk00503": 545, "igboapi_pspk00504": 546, "igboapi_pspk00505": 547, "igboapi_pspk00506": 548, "igboapi_pspk00507": 549, "igboapi_pspk00508": 550, "igboapi_pspk00509": 551, "igboapi_pspk00510": 552, "igboapi_pspk00511": 553, "igboapi_pspk00512": 554, "igboapi_pspk00513": 555, "igboapi_pspk00516": 556, "igboapi_pspk00517": 557, "igboapi_pspk00518": 558, "igboapi_pspk00519": 559, "igboapi_pspk00520": 560, "igboapi_pspk00521": 561, "igboapi_pspk00522": 562, "igboapi_pspk00523": 563, "igboapi_pspk00524": 564, "igboapi_pspk00525": 565, "igboapi_pspk00526": 566, "igboapi_pspk00527": 567, "igboapi_pspk00528": 568, "igboapi_pspk00529": 569, "igboapi_pspk00530": 570, "igboapi_pspk00531": 571, "igboapi_pspk00532": 572, "igboapi_pspk00533": 573, "igboapi_pspk00534": 574, "igboapi_pspk00535": 575, "igboapi_pspk00536": 576, "igboapi_pspk00537": 577, "igboapi_pspk00538": 578, "igboapi_pspk00539": 579, "igboapi_pspk00540": 580, "igboapi_pspk00541": 581, "igboapi_pspk00542": 582, "igboapi_pspk00543": 583, "igboapi_pspk00544": 584, "igboapi_pspk00545": 585, "igboapi_pspk00546": 586, "igboapi_pspk00547": 587, "igboapi_pspk00548": 588, "igboapi_pspk00549": 589, "igboapi_pspk00550": 590, "igboapi_pspk00551": 591, "igboapi_pspk00552": 592, "igboapi_pspk00553": 593, "igboapi_pspk00554": 594, "igboapi_pspk00555": 595, "igboapi_pspk00556": 596, "igboapi_pspk00557": 597, "igboapi_pspk00558": 598, "igboapi_pspk00559": 599, "igboapi_pspk00560": 600, "igboapi_pspk00561": 601, "igboapi_pspk00562": 602, "igboapi_pspk00563": 603, "igboapi_pspk00564": 604, "igboapi_pspk00565": 605, "igboapi_pspk00566": 606, "igboapi_pspk00567": 607, "igboapi_pspk00568": 608, "igboapi_pspk00569": 609, "igboapi_pspk00570": 610, "igboapi_pspk00571": 611, "igboapi_pspk00572": 612, "igboapi_pspk00573": 613, "igboapi_pspk00574": 614, "igboapi_pspk00575": 615, "igboapi_pspk00576": 616, "igboapi_pspk00577": 617, "igboapi_pspk00578": 618, "igboapi_pspk00579": 619, "igboapi_pspk00580": 620, "igboapi_pspk00581": 621, "igboapi_pspk00582": 622, "igboapi_pspk00583": 623, "igboapi_pspk00584": 624, "igboapi_pspk00585": 625, "igboapi_pspk00586": 626, "igboapi_pspk00587": 627, "igboapi_pspk00588": 628, "igboapi_pspk00589": 629, "igboapi_pspk00590": 630, "igboapi_pspk00591": 631, "igboapi_pspk00592": 632, "igboapi_pspk00593": 633, "igboapi_pspk00594": 634, "igboapi_pspk00595": 635, "igboapi_pspk00596": 636, "igboapi_pspk00597": 637, "igboapi_pspk00598": 638, "igboapi_pspk00599": 639, "igboapi_pspk00600": 640, "igboapi_pspk00601": 641, "igboapi_pspk00602": 642, "igboapi_pspk00603": 643, "igboapi_pspk00604": 644, "igboapi_pspk00605": 645, "igboapi_pspk00606": 646, "igboapi_pspk00607": 647, "igboapi_pspk00608": 648, "igboapi_pspk00609": 649, "igboapi_pspk00610": 650, "igboapi_pspk00611": 651, "igboapi_pspk00612": 652, "igboapi_pspk00613": 653, "igboapi_pspk00614": 654, "igboapi_pspk00615": 655, "igboapi_pspk00616": 656, "igboapi_pspk00617": 657, "igboapi_pspk00618": 658, "igboapi_pspk00619": 659, "igboapi_pspk00620": 660, "igboapi_pspk00621": 661, "igboapi_pspk00622": 662, "igboapi_pspk00623": 663, "igboapi_pspk00624": 664, "igboapi_pspk00625": 665, "igboapi_pspk00626": 666, "igboapi_pspk00627": 667, "igboapi_pspk00628": 668, "igboapi_pspk00629": 669, "igboapi_pspk00630": 670, "igboapi_pspk00631": 671, "igboapi_pspk00632": 672, "igboapi_pspk00633": 673, "igboapi_pspk00634": 674, "igboapi_pspk00635": 675, "igboapi_pspk00636": 676, "igboapi_pspk00637": 677, "igboapi_pspk00638": 678, "igboapi_pspk00639": 679, "igboapi_pspk00640": 680, "igboapi_pspk00641": 681, "igboapi_pspk00642": 682, "igboapi_pspk00643": 683, "igboapi_pspk00644": 684, "igboapi_pspk00645": 685, "igboapi_pspk00646": 686, "igboapi_pspk00647": 687, "igboapi_pspk00648": 688, "igboapi_pspk00649": 689, "igboapi_pspk00650": 690, "igboapi_pspk00651": 691, "igboapi_pspk00652": 692, "igboapi_pspk00653": 693, "igboapi_pspk00654": 694, "igboapi_pspk00655": 695, "igboapi_pspk00656": 696, "igboapi_pspk00657": 697, "igboapi_pspk00658": 698, "igboapi_pspk00659": 699, "igboapi_pspk00660": 700, "igboapi_pspk00661": 701, "igboapi_pspk00662": 702, "igboapi_pspk00663": 703, "igboapi_pspk00664": 704, "igboapi_pspk00665": 705, "igboapi_pspk00666": 706, "igboapi_pspk00667": 707, "igboapi_pspk00668": 708, "igboapi_pspk00669": 709, "igboapi_pspk00670": 710, "igboapi_pspk00671": 711, "igboapi_pspk00672": 712, "igboapi_pspk00673": 713, "igboapi_pspk00674": 714, "igboapi_pspk00675": 715, "igboapi_pspk00676": 716, "igboapi_pspk00677": 717, "igboapi_pspk00678": 718, "igboapi_pspk00679": 719, "igboapi_pspk00680": 720, "igboapi_pspk00681": 721, "igboapi_pspk00682": 722, "igboapi_pspk00683": 723, "igboapi_pspk00684": 724, "igboapi_pspk00685": 725, "igboapi_pspk00686": 726, "igboapi_pspk00687": 727, "igboapi_pspk00688": 728, "igboapi_pspk00689": 729, "igboapi_pspk00690": 730, "igboapi_pspk00691": 731, "igboapi_pspk00692": 732, "igboapi_pspk00693": 733, "igboapi_pspk00694": 734, "igboapi_pspk00696": 735, "igboapi_pspk00697": 736, "igboapi_pspk00698": 737, "igboapi_pspk00699": 738, "igboapi_pspk00700": 739, "igboapi_pspk00701": 740, "igboapi_pspk00702": 741, "igboapi_pspk00703": 742, "igboapi_pspk00704": 743, "igboapi_pspk00705": 744, "igboapi_pspk00706": 745, "igboapi_pspk00707": 746, "igboapi_pspk00708": 747, "igboapi_pspk00709": 748, "igboapi_pspk00710": 749, "igboapi_pspk00711": 750, "igboapi_pspk00712": 751, "igboapi_pspk00713": 752, "igboapi_pspk00714": 753, "igboapi_pspk00715": 754, "igboapi_pspk00716": 755, "igboapi_pspk00717": 756, "igboapi_pspk00718": 757, "igboapi_pspk00719": 758, "igboapi_pspk00720": 759, "igboapi_pspk00721": 760, "igboapi_pspk00722": 761, "igboapi_pspk00723": 762, "igboapi_pspk00724": 763, "igboapi_pspk00725": 764, "igboapi_pspk00726": 765, "igboapi_pspk00727": 766, "igboapi_pspk00728": 767, "igboapi_pspk00729": 768, "igboapi_pspk00730": 769, "igboapi_pspk00731": 770, "igboapi_pspk00732": 771, "igboapi_pspk00733": 772, "igboapi_pspk00734": 773, "igboapi_pspk00735": 774, "igboapi_pspk00736": 775, "igboapi_pspk00737": 776, "igboapi_pspk00738": 777, "igboapi_pspk00739": 778, "igboapi_pspk00740": 779, "igboapi_pspk00741": 780, "igboapi_pspk00742": 781, "igboapi_pspk00743": 782, "igboapi_pspk00744": 783, "igboapi_pspk00745": 784, "igboapi_pspk00746": 785, "igboapi_pspk00747": 786, "igboapi_pspk00748": 787, "igboapi_pspk00749": 788, "igboapi_pspk00750": 789, "igboapi_pspk00751": 790, "igboapi_pspk00752": 791, "igboapi_pspk00754": 792, "igboapi_pspk00755": 793, "igboapi_pspk00756": 794, "igboapi_pspk00757": 795, "igboapi_pspk00758": 796, "igboapi_pspk00759": 797, "igboapi_pspk00760": 798, "igboapi_pspk00761": 799, "igboapi_pspk00762": 800, "igboapi_pspk00763": 801, "igboapi_pspk00764": 802, "igboapi_pspk00765": 803, "igboapi_pspk00766": 804, "igboapi_pspk00767": 805, "igboapi_pspk00768": 806, "igboapi_pspk00769": 807, "igboapi_pspk00770": 808, "igboapi_pspk00771": 809, "igboapi_pspk00772": 810, "igboapi_pspk00773": 811, "igboapi_pspk00774": 812, "igboapi_pspk00775": 813, "igboapi_pspk00776": 814, "igboapi_pspk00777": 815, "igboapi_pspk00778": 816, "igboapi_pspk00779": 817, "igboapi_pspk00780": 818, "igboapi_pspk00781": 819, "igboapi_pspk00782": 820, "igboapi_pspk00783": 821, "igboapi_pspk00784": 822, "igboapi_pspk00785": 823, "igboapi_pspk00786": 824, "igboapi_pspk00787": 825, "igboapi_pspk00788": 826, "igboapi_pspk00789": 827, "igboapi_pspk00790": 828, "igboapi_pspk00791": 829, "igboapi_pspk00792": 830, "igboapi_pspk00793": 831, "igboapi_pspk00794": 832, "igboapi_pspk00795": 833, "igboapi_pspk00796": 834, "igboapi_pspk00797": 835, "igboapi_pspk00798": 836, "igboapi_pspk00799": 837, "igboapi_pspk00800": 838, "igboapi_pspk00801": 839, "igboapi_pspk00803": 840, "igboapi_pspk00804": 841, "igboapi_pspk00805": 842, "igboapi_pspk00806": 843, "igboapi_pspk00807": 844, "igboapi_pspk00808": 845, "igboapi_pspk00809": 846, "igboapi_pspk00810": 847, "igboapi_pspk00811": 848, "igboapi_pspk00812": 849, "igboapi_pspk00813": 850, "igboapi_pspk00814": 851, "igboapi_pspk00815": 852, "igboapi_pspk00816": 853, "igboapi_pspk00817": 854, "igboapi_pspk00818": 855, "igboapi_pspk00819": 856, "igboapi_pspk00820": 857, "igboapi_pspk00821": 858, "igboapi_pspk00822": 859, "igboapi_pspk00824": 860, "igboapi_pspk00825": 861, "igboapi_pspk00826": 862, "igboapi_pspk00827": 863, "igboapi_pspk00828": 864, "igboapi_pspk00829": 865, "igboapi_pspk00831": 866, "igboapi_pspk00832": 867, "igboapi_pspk00833": 868, "igboapi_pspk00834": 869, "igboapi_pspk00835": 870, "igboapi_pspk00836": 871, "igboapi_pspk00837": 872, "igboapi_pspk00838": 873, "igboapi_pspk00839": 874, "igboapi_pspk00840": 875, "igboapi_pspk00841": 876, "igboapi_pspk00842": 877, "igboapi_pspk00843": 878, "igboapi_pspk00844": 879, "igboapi_pspk00845": 880, "igboapi_pspk00846": 881, "igboapi_pspk00847": 882, "igboapi_pspk00848": 883, "igboapi_pspk00849": 884, "igboapi_pspk00850": 885, "igboapi_pspk00851": 886, "igboapi_pspk00852": 887, "igboapi_pspk00853": 888, "igboapi_pspk00854": 889, "igboapi_pspk00855": 890, "igboapi_pspk00856": 891, "igboapi_pspk00857": 892, "igboapi_pspk00858": 893, "igboapi_pspk00859": 894, "igboapi_pspk00860": 895, "igboapi_pspk00861": 896, "igboapi_pspk00862": 897, "igboapi_pspk00863": 898, "igboapi_pspk00864": 899, "igboapi_pspk00865": 900, "igboapi_pspk00866": 901, "igboapi_pspk00867": 902, "igboapi_pspk00868": 903, "igboapi_pspk00869": 904, "igboapi_pspk00870": 905, "igboapi_pspk00871": 906, "igboapi_pspk00872": 907, "igboapi_pspk00873": 908, "igboapi_pspk00874": 909, "igboapi_pspk00875": 910, "igboapi_pspk00876": 911, "igboapi_pspk00877": 912, "igboapi_pspk00878": 913, "igboapi_pspk00879": 914, "igboapi_pspk00880": 915, "igboapi_pspk00881": 916, "igboapi_pspk00882": 917, "igboapi_pspk00883": 918, "igboapi_pspk00884": 919, "igboapi_pspk00885": 920, "igboapi_pspk00886": 921, "igboapi_pspk00887": 922, "igboapi_pspk00888": 923, "igboapi_pspk00889": 924, "igboapi_pspk00890": 925, "igboapi_pspk00891": 926, "igboapi_pspk00892": 927, "igboapi_pspk00893": 928, "igboapi_pspk00894": 929, "igboapi_pspk00895": 930, "igboapi_pspk00896": 931, "igboapi_pspk00897": 932, "igboapi_pspk00898": 933, "igboapi_pspk00899": 934, "igboapi_pspk00900": 935, "igboapi_pspk00901": 936, "igboapi_pspk00902": 937, "igboapi_pspk00903": 938, "igboapi_pspk00904": 939, "igboapi_pspk00905": 940, "igboapi_pspk00906": 941, "igboapi_pspk00907": 942, "igboapi_pspk00908": 943, "igboapi_pspk00909": 944, "igboapi_pspk00910": 945, "igboapi_pspk00911": 946, "igboapi_pspk00912": 947, "igboapi_pspk00913": 948, "igboapi_pspk00914": 949, "igboapi_pspk00915": 950, "igboapi_pspk00916": 951, "igboapi_pspk00917": 952, "igboapi_pspk00918": 953, "igboapi_pspk00919": 954, "igboapi_pspk00920": 955, "igboapi_pspk00921": 956, "igboapi_pspk00922": 957, "igboapi_pspk00923": 958, "igboapi_pspk00924": 959, "igboapi_pspk00925": 960, "igboapi_pspk00926": 961, "igboapi_pspk00927": 962, "igboapi_pspk00928": 963, "igboapi_pspk00929": 964, "igboapi_pspk00930": 965, "igboapi_pspk00931": 966, "igboapi_pspk00932": 967, "igboapi_pspk00933": 968, "igboapi_pspk00934": 969, "igboapi_pspk00935": 970, "igboapi_pspk00936": 971, "igboapi_pspk00937": 972, "igboapi_pspk00938": 973, "igboapi_pspk00939": 974, "igboapi_pspk00940": 975, "igboapi_pspk00941": 976, "igboapi_pspk00942": 977, "igboapi_pspk00943": 978, "igboapi_pspk00944": 979, "igboapi_pspk00945": 980, "igboapi_pspk00946": 981, "igboapi_pspk00947": 982, "igboapi_pspk00948": 983, "igboapi_pspk00949": 984, "igboapi_pspk00950": 985, "igboapi_pspk00951": 986, "igboapi_pspk00952": 987, "igboapi_pspk00953": 988, "igboapi_pspk00954": 989, "igboapi_pspk00955": 990, "igboapi_pspk00956": 991, "igboapi_pspk00957": 992, "igboapi_pspk00958": 993, "igboapi_pspk00959": 994, "igboapi_pspk00960": 995, "igboapi_pspk00961": 996, "igboapi_pspk00962": 997, "igboapi_pspk00963": 998, "igboapi_pspk00964": 999, "igboapi_pspk00965": 1000, "igboapi_pspk00966": 1001, "igboapi_pspk00967": 1002, "igboapi_pspk00968": 1003, "igboapi_pspk00969": 1004, "igboapi_pspk00970": 1005, "igboapi_pspk00971": 1006, "igboapi_pspk00972": 1007, "igboapi_pspk00973": 1008, "igboapi_pspk00974": 1009, "igboapi_pspk00975": 1010, "igboapi_pspk00976": 1011, "igboapi_pspk00977": 1012, "igboapi_pspk00978": 1013, "igboapi_pspk00979": 1014, "igboapi_pspk00980": 1015, "igboapi_pspk00981": 1016, "igboapi_pspk00982": 1017, "igboapi_pspk00983": 1018, "igboapi_pspk00984": 1019, "igboapi_pspk00985": 1020, "igboapi_pspk00986": 1021, "igboapi_pspk00987": 1022, "igboapi_pspk00988": 1023, "igboapi_pspk00989": 1024, "igboapi_pspk00990": 1025, "igboapi_pspk00991": 1026, "igboapi_pspk00992": 1027, "igboapi_pspk00993": 1028, "igboapi_pspk00994": 1029, "igboapi_pspk00995": 1030, "igboapi_pspk00996": 1031, "igboapi_pspk00997": 1032, "igboapi_pspk00998": 1033, "igboapi_pspk00999": 1034, "igboapi_pspk01000": 1035, "igboapi_pspk01001": 1036, "igboapi_pspk01002": 1037, "igboapi_pspk01003": 1038, "igboapi_pspk01004": 1039, "igboapi_pspk01005": 1040, "igboapi_pspk01006": 1041, "igboapi_pspk01007": 1042, "igboapi_pspk01008": 1043, "igboapi_pspk01009": 1044, "igboapi_pspk01010": 1045, "igboapi_pspk01011": 1046, "igboapi_pspk01012": 1047, "igboapi_pspk01013": 1048, "igboapi_pspk01014": 1049, "igboapi_pspk01015": 1050, "igboapi_pspk01016": 1051, "igboapi_pspk01017": 1052, "igboapi_pspk01018": 1053, "igboapi_pspk01019": 1054, "igboapi_pspk01020": 1055, "igboapi_pspk01021": 1056, "igboapi_pspk01022": 1057, "igboapi_pspk01023": 1058, "igboapi_pspk01024": 1059, "igboapi_pspk01025": 1060, "igboapi_pspk01026": 1061, "igboapi_pspk01027": 1062, "igboapi_pspk01028": 1063, "igboapi_pspk01029": 1064, "igboapi_pspk01030": 1065, "igboapi_pspk01031": 1066, "igboapi_pspk01032": 1067, "igboapi_pspk01033": 1068, "igboapi_pspk01034": 1069, "igboapi_pspk01035": 1070, "igboapi_pspk01036": 1071, "igboapi_pspk01037": 1072, "igboapi_pspk01038": 1073, "igboapi_pspk01039": 1074, "igboapi_pspk01040": 1075, "igboapi_pspk01041": 1076, "igboapi_pspk01042": 1077, "igboapi_pspk01043": 1078, "igboapi_pspk01044": 1079, "igboapi_pspk01045": 1080, "igboapi_pspk01046": 1081, "igboapi_pspk01047": 1082, "igboapi_pspk01048": 1083, "igboapi_pspk01049": 1084, "igboapi_pspk01050": 1085, "igboapi_pspk01051": 1086, "igboapi_pspk01052": 1087, "igboapi_pspk01053": 1088, "igboapi_pspk01054": 1089, "igboapi_pspk01055": 1090, "igboapi_pspk01056": 1091, "igboapi_pspk01057": 1092, "igboapi_pspk01059": 1093, "igboapi_pspk01061": 1094, "igboapi_pspk01062": 1095, "igboapi_pspk01063": 1096, "igboapi_pspk01064": 1097, "igboapi_pspk01065": 1098, "igboapi_pspk01066": 1099, "igboapi_pspk01067": 1100, "igboapi_pspk01068": 1101, "igboapi_pspk01069": 1102, "igboapi_pspk01070": 1103, "igboapi_pspk01071": 1104, "igboapi_pspk01072": 1105, "igboapi_pspk01073": 1106, "igboapi_pspk01074": 1107, "igboapi_pspk01075": 1108, "igboapi_pspk01076": 1109, "igboapi_pspk01077": 1110, "igboapi_pspk01078": 1111, "igboapi_pspk01079": 1112, "igboapi_pspk01080": 1113, "igboapi_pspk01081": 1114, "igboapi_pspk01082": 1115, "igboapi_pspk01083": 1116, "igboapi_pspk01084": 1117, "igboapi_pspk01085": 1118, "igboapi_pspk01086": 1119, "igboapi_pspk01087": 1120, "igboapi_pspk01088": 1121, "igboapi_pspk01089": 1122, "igboapi_pspk01090": 1123, "igboapi_pspk01092": 1124, "igboapi_pspk01093": 1125, "igboapi_pspk01094": 1126, "igboapi_pspk01095": 1127, "igboapi_pspk01096": 1128, "igboapi_pspk01097": 1129, "igboapi_pspk01098": 1130, "igboapi_pspk01099": 1131, "igboapi_pspk01100": 1132, "igboapi_pspk01101": 1133, "igboapi_pspk01102": 1134, "igboapi_pspk01103": 1135, "igboapi_pspk01104": 1136, "igboapi_pspk01105": 1137, "igboapi_pspk01106": 1138, "igboapi_pspk01107": 1139, "igboapi_pspk01108": 1140, "igboapi_pspk01109": 1141, "igboapi_pspk01110": 1142, "igboapi_pspk01111": 1143, "igboapi_pspk01112": 1144, "igboapi_pspk01113": 1145, "igboapi_pspk01114": 1146, "igboapi_pspk01115": 1147, "igboapi_pspk01116": 1148, "igboapi_pspk01117": 1149, "igboapi_pspk01118": 1150, "igboapi_pspk01119": 1151, "igboapi_pspk01120": 1152, "igboapi_pspk01121": 1153, "igboapi_pspk01122": 1154, "igboapi_pspk01123": 1155, "igboapi_pspk01124": 1156, "igboapi_pspk01125": 1157, "igboapi_pspk01126": 1158, "igboapi_pspk01127": 1159, "igboapi_pspk01128": 1160, "igboapi_pspk01129": 1161, "igboapi_pspk01130": 1162, "igboapi_pspk01131": 1163, "igboapi_pspk01132": 1164, "igboapi_pspk01133": 1165, "igboapi_pspk01134": 1166, "igboapi_pspk01135": 1167, "igboapi_pspk01136": 1168, "igboapi_pspk01137": 1169, "igboapi_pspk01138": 1170, "igboapi_pspk01139": 1171, "igboapi_pspk01140": 1172, "igboapi_pspk01141": 1173, "igboapi_pspk01142": 1174, "igboapi_pspk01143": 1175, "igboapi_pspk01144": 1176, "igboapi_pspk01145": 1177, "igboapi_pspk01146": 1178, "igboapi_pspk01147": 1179, "igboapi_pspk01148": 1180, "igboapi_pspk01149": 1181, "igboapi_pspk01150": 1182, "igboapi_pspk01151": 1183, "igboapi_pspk01152": 1184, "igboapi_pspk01153": 1185, "igboapi_pspk01154": 1186, "igboapi_pspk01155": 1187, "igboapi_pspk01156": 1188, "igboapi_pspk01157": 1189, "igboapi_pspk01158": 1190, "igboapi_pspk01159": 1191, "igboapi_pspk01160": 1192, "igboapi_pspk01161": 1193, "igboapi_pspk01162": 1194, "igboapi_pspk01163": 1195, "igboapi_pspk01164": 1196, "igboapi_pspk01165": 1197, "igboapi_pspk01166": 1198, "igboapi_pspk01167": 1199, "igboapi_pspk01168": 1200, "igboapi_pspk01169": 1201, "igboapi_pspk01170": 1202, "igboapi_pspk01172": 1203, "igboapi_pspk01173": 1204, "igboapi_pspk01174": 1205, "igboapi_pspk01175": 1206, "igboapi_pspk01176": 1207, "igboapi_pspk01178": 1208, "igboapi_pspk01179": 1209, "igboapi_pspk01180": 1210, "igboapi_pspk01181": 1211, "igboapi_pspk01182": 1212, "igboapi_pspk01183": 1213, "igboapi_pspk01184": 1214, "igboapi_pspk01185": 1215, "igboapi_pspk01186": 1216, "igboapi_pspk01187": 1217, "igboapi_pspk01188": 1218, "igboapi_pspk01189": 1219, "igboapi_pspk01190": 1220, "igboapi_pspk01191": 1221, "igboapi_pspk01192": 1222, "igboapi_pspk01193": 1223, "igboapi_pspk01194": 1224, "igboapi_pspk01195": 1225, "igboapi_pspk01196": 1226, "igboapi_pspk01197": 1227, "igboapi_pspk01198": 1228, "igboapi_pspk01199": 1229, "igboapi_pspk01200": 1230, "igboapi_pspk01201": 1231, "igboapi_pspk01202": 1232, "igboapi_pspk01203": 1233, "igboapi_pspk01204": 1234, "igboapi_pspk01205": 1235, "igboapi_pspk01206": 1236, "igboapi_pspk01207": 1237, "igboapi_pspk01208": 1238, "igboapi_pspk01209": 1239, "igboapi_pspk01210": 1240, "igboapi_pspk01211": 1241, "igboapi_pspk01212": 1242, "igboapi_pspk01213": 1243, "igboapi_pspk01214": 1244, "igboapi_pspk01215": 1245, "igboapi_pspk01216": 1246, "igboapi_pspk01217": 1247, "igboapi_pspk01218": 1248, "igboapi_pspk01219": 1249, "igboapi_pspk01220": 1250, "igboapi_pspk01221": 1251, "igboapi_pspk01222": 1252, "igboapi_pspk01223": 1253, "igboapi_pspk01224": 1254, "igboapi_pspk01225": 1255, "igboapi_pspk01226": 1256, "igboapi_pspk01227": 1257, "igboapi_pspk01228": 1258, "igboapi_pspk01229": 1259, "igboapi_pspk01230": 1260, "igboapi_pspk01231": 1261, "igboapi_pspk01232": 1262, "igboapi_pspk01233": 1263, "igboapi_pspk01234": 1264, "igboapi_pspk01235": 1265, "igboapi_pspk01236": 1266, "igboapi_pspk01237": 1267, "igboapi_pspk01238": 1268, "igboapi_pspk01239": 1269, "igboapi_pspk01240": 1270, "igboapi_pspk01241": 1271, "igboapi_pspk01242": 1272, "igboapi_pspk01243": 1273, "igboapi_pspk01244": 1274, "igboapi_pspk01245": 1275, "igboapi_pspk01246": 1276, "igboapi_pspk01247": 1277, "igboapi_pspk01248": 1278, "igboapi_pspk01249": 1279, "igboapi_pspk01250": 1280, "igboapi_pspk01251": 1281, "igboapi_pspk01252": 1282, "igboapi_pspk01253": 1283, "igboapi_pspk01254": 1284, "igboapi_pspk01255": 1285, "igboapi_pspk01256": 1286, "igboapi_pspk01257": 1287, "igboapi_pspk01258": 1288, "igboapi_pspk01259": 1289, "igboapi_pspk01260": 1290, "igboapi_pspk01261": 1291, "igboapi_pspk01262": 1292, "igboapi_pspk01263": 1293, "igboapi_pspk01264": 1294, "igboapi_pspk01265": 1295, "igboapi_pspk01266": 1296, "igboapi_pspk01267": 1297, "igboapi_pspk01268": 1298, "igboapi_pspk01269": 1299, "igboapi_pspk01270": 1300, "igboapi_pspk01271": 1301, "igboapi_pspk01272": 1302, "igboapi_pspk01273": 1303, "igboapi_pspk01274": 1304, "igboapi_pspk01275": 1305, "igboapi_pspk01276": 1306, "igboapi_pspk01277": 1307, "igboapi_pspk01278": 1308, "igboapi_pspk01279": 1309, "igboapi_pspk01280": 1310, "igboapi_pspk01281": 1311, "igboapi_pspk01282": 1312, "igboapi_pspk01283": 1313, "igboapi_pspk01284": 1314, "igboapi_pspk01285": 1315, "igboapi_pspk01286": 1316, "igboapi_pspk01287": 1317, "igboapi_pspk01288": 1318, "igboapi_pspk01289": 1319, "igboapi_pspk01290": 1320, "igboapi_pspk01292": 1321, "igboapi_pspk01293": 1322, "igboapi_pspk01294": 1323, "igboapi_pspk01295": 1324, "igboapi_pspk01296": 1325, "igboapi_pspk01297": 1326, "igboapi_pspk01298": 1327, "igboapi_pspk01300": 1328, "igboapi_pspk01301": 1329, "igboapi_pspk01302": 1330, "igboapi_pspk01303": 1331, "igboapi_pspk01304": 1332, "igboapi_pspk01306": 1333, "igboapi_pspk01307": 1334, "igboapi_pspk01308": 1335, "igboapi_pspk01309": 1336, "igboapi_pspk01310": 1337, "igboapi_pspk01311": 1338, "igboapi_pspk01312": 1339, "igboapi_pspk01313": 1340, "igboapi_pspk01314": 1341, "igboapi_pspk01315": 1342, "igboapi_pspk01316": 1343, "igboapi_pspk01317": 1344, "igboapi_pspk01318": 1345, "igboapi_pspk01319": 1346, "igboapi_pspk01320": 1347, "igboapi_pspk01321": 1348, "igboapi_pspk01322": 1349, "igboapi_pspk01323": 1350, "igboapi_pspk01324": 1351, "igboapi_pspk01325": 1352, "igboapi_pspk01326": 1353, "igboapi_pspk01327": 1354, "igboapi_pspk01328": 1355, "igboapi_pspk01329": 1356, "igboapi_pspk01330": 1357, "igboapi_pspk01331": 1358, "igboapi_pspk01332": 1359, "igboapi_pspk01333": 1360, "igboapi_pspk01334": 1361, "igboapi_pspk01335": 1362, "igboapi_pspk01336": 1363, "igboapi_pspk01337": 1364, "igboapi_pspk01338": 1365, "igboapi_pspk01339": 1366, "igboapi_pspk01340": 1367, "igboapi_pspk01341": 1368, "igboapi_pspk01342": 1369, "igboapi_pspk01343": 1370, "igboapi_pspk01344": 1371, "igboapi_pspk01345": 1372, "igboapi_pspk01346": 1373, "igboapi_pspk01347": 1374, "igboapi_pspk01348": 1375, "igboapi_pspk01349": 1376, "igboapi_pspk01350": 1377, "igboapi_pspk01351": 1378, "igboapi_pspk01352": 1379, "igboapi_pspk01353": 1380, "igboapi_pspk01354": 1381, "igboapi_pspk01355": 1382, "igboapi_pspk01356": 1383, "igboapi_pspk01357": 1384, "igboapi_pspk01358": 1385, "igboapi_pspk01359": 1386, "igboapi_pspk01360": 1387, "igboapi_pspk01361": 1388, "igboapi_pspk01362": 1389, "igboapi_pspk01363": 1390, "igboapi_pspk01364": 1391, "igboapi_pspk01365": 1392, "igboapi_pspk01366": 1393, "igboapi_pspk01367": 1394, "igboapi_pspk01368": 1395, "igboapi_pspk01369": 1396, "igboapi_pspk01370": 1397, "igboapi_pspk01371": 1398, "igboapi_pspk01372": 1399, "igboapi_pspk01373": 1400, "igboapi_pspk01374": 1401, "igboapi_pspk01375": 1402, "igboapi_pspk01376": 1403, "igboapi_pspk01377": 1404, "igboapi_pspk01378": 1405, "igboapi_pspk01379": 1406, "igboapi_pspk01380": 1407, "igboapi_pspk01381": 1408, "igboapi_pspk01382": 1409, "igboapi_pspk01383": 1410, "igboapi_pspk01384": 1411, "igboapi_pspk01385": 1412, "igboapi_pspk01386": 1413, "igboapi_pspk01387": 1414, "igboapi_pspk01388": 1415, "igboapi_pspk01389": 1416, "igboapi_pspk01390": 1417, "igboapi_pspk01391": 1418, "igboapi_pspk01392": 1419, "igboapi_pspk01393": 1420, "igboapi_pspk01394": 1421, "igboapi_pspk01395": 1422, "igboapi_pspk01396": 1423, "igboapi_pspk01397": 1424, "igboapi_pspk01398": 1425, "igboapi_pspk01399": 1426, "igboapi_pspk01400": 1427, "igboapi_pspk01401": 1428, "igboapi_pspk01402": 1429, "igboapi_pspk01403": 1430, "igboapi_pspk01404": 1431, "igboapi_pspk01405": 1432, "igboapi_pspk01406": 1433, "igboapi_pspk01407": 1434, "igboapi_pspk01408": 1435, "igboapi_pspk01409": 1436, "igboapi_pspk01410": 1437, "igboapi_pspk01411": 1438, "igboapi_pspk01412": 1439, "igboapi_pspk01413": 1440, "igboapi_pspk01414": 1441, "igboapi_pspk01415": 1442, "igboapi_pspk01416": 1443, "igboapi_pspk01417": 1444, "igboapi_pspk01418": 1445, "igboapi_pspk01419": 1446, "igboapi_pspk01420": 1447, "igboapi_pspk01421": 1448, "igboapi_pspk01422": 1449, "igboapi_pspk01423": 1450, "igboapi_pspk01424": 1451, "igboapi_pspk01425": 1452, "igboapi_pspk01426": 1453, "igboapi_pspk01427": 1454, "igboapi_pspk01428": 1455, "igboapi_pspk01429": 1456, "igboapi_pspk01430": 1457, "igboapi_pspk01431": 1458, "igboapi_pspk01432": 1459, "igboapi_pspk01433": 1460, "igboapi_pspk01435": 1461, "igboapi_pspk01436": 1462, "igboapi_pspk01437": 1463, "igboapi_pspk01438": 1464, "igboapi_pspk01439": 1465, "igboapi_pspk01440": 1466, "igboapi_pspk01441": 1467, "igboapi_pspk01442": 1468, "igboapi_pspk01443": 1469, "igboapi_pspk01444": 1470, "igboapi_pspk01445": 1471, "igboapi_pspk01446": 1472, "igboapi_pspk01447": 1473, "igboapi_pspk01448": 1474, "igboapi_pspk01449": 1475, "igboapi_pspk01450": 1476, "igboapi_pspk01451": 1477, "igboapi_pspk01452": 1478, "igboapi_pspk01453": 1479, "igboapi_pspk01454": 1480, "igboapi_pspk01455": 1481, "igboapi_pspk01456": 1482, "igboapi_pspk01457": 1483, "igboapi_pspk01458": 1484, "igboapi_pspk01459": 1485, "igboapi_pspk01460": 1486, "igboapi_pspk01461": 1487, "igboapi_pspk01462": 1488, "igboapi_pspk01463": 1489, "igboapi_pspk01464": 1490, "igboapi_pspk01465": 1491, "igboapi_pspk01466": 1492, "igboapi_pspk01467": 1493, "igboapi_pspk01468": 1494, "igboapi_pspk01469": 1495, "igboapi_pspk01470": 1496, "igboapi_pspk01471": 1497, "igboapi_pspk01472": 1498, "igboapi_pspk01473": 1499, "igboapi_pspk01474": 1500, "igboapi_pspk01475": 1501, "igboapi_pspk01476": 1502, "igboapi_pspk01477": 1503, "igboapi_pspk01478": 1504, "igboapi_pspk01479": 1505, "igboapi_pspk01480": 1506, "igboapi_pspk01481": 1507, "igboapi_pspk01482": 1508, "igboapi_pspk01483": 1509, "igboapi_pspk01484": 1510, "igboapi_pspk01485": 1511, "igboapi_pspk01486": 1512, "igboapi_pspk01487": 1513, "igboapi_pspk01488": 1514, "igboapi_pspk01489": 1515, "igboapi_pspk01490": 1516, "igboapi_pspk01491": 1517, "igboapi_pspk01492": 1518, "igboapi_pspk01493": 1519, "igboapi_pspk01494": 1520, "igboapi_pspk01495": 1521, "igboapi_pspk01496": 1522, "igboapi_pspk01497": 1523, "igboapi_pspk01498": 1524, "igboapi_pspk01499": 1525, "igboapi_pspk01500": 1526, "igboapi_pspk01501": 1527, "igboapi_pspk01502": 1528, "igboapi_pspk01503": 1529, "igboapi_pspk01504": 1530, "igboapi_pspk01505": 1531, "igboapi_pspk01506": 1532, "igboapi_pspk01507": 1533, "igboapi_pspk01508": 1534, "igboapi_pspk01509": 1535, "igboapi_pspk01510": 1536, "igboapi_pspk01511": 1537, "igboapi_pspk01512": 1538, "igboapi_pspk01513": 1539, "igboapi_pspk01514": 1540, "igboapi_pspk01515": 1541, "igboapi_pspk01516": 1542, "igboapi_pspk01517": 1543, "igboapi_pspk01518": 1544, "igboapi_pspk01519": 1545, "igboapi_pspk01520": 1546, "igboapi_pspk01521": 1547, "igboapi_pspk01522": 1548, "igboapi_pspk01523": 1549, "igboapi_pspk01524": 1550, "igboapi_pspk01525": 1551, "igboapi_pspk01526": 1552, "igboapi_pspk01527": 1553, "igboapi_pspk01528": 1554, "igboapi_pspk01529": 1555, "igboapi_pspk01530": 1556, "igboapi_pspk01531": 1557, "igboapi_pspk01532": 1558, "igboapi_pspk01533": 1559, "igboapi_pspk01534": 1560, "igboapi_pspk01535": 1561, "igboapi_pspk01536": 1562, "igboapi_pspk01537": 1563, "igboapi_pspk01538": 1564, "igboapi_pspk01539": 1565, "igboapi_pspk01540": 1566, "igboapi_pspk01541": 1567, "igboapi_pspk01542": 1568, "igboapi_pspk01543": 1569, "igboapi_pspk01544": 1570, "igboapi_pspk01545": 1571, "igboapi_pspk01546": 1572, "igboapi_pspk01547": 1573, "igboapi_pspk01548": 1574, "igboapi_pspk01549": 1575, "igboapi_pspk01550": 1576, "igboapi_pspk01552": 1577, "igboapi_pspk01553": 1578, "igboapi_pspk01554": 1579, "igboapi_pspk01555": 1580, "igboapi_pspk01556": 1581, "igboapi_pspk01557": 1582, "igboapi_pspk01558": 1583, "igboapi_pspk01559": 1584, "igboapi_pspk01560": 1585, "igboapi_pspk01561": 1586, "igboapi_pspk01562": 1587, "igboapi_pspk01563": 1588, "igboapi_pspk01564": 1589, "igboapi_pspk01565": 1590, "igboapi_pspk01566": 1591, "igboapi_pspk01567": 1592, "igboapi_pspk01569": 1593, "igboapi_pspk01570": 1594, "igboapi_pspk01571": 1595, "igboapi_pspk01572": 1596, "igboapi_pspk01573": 1597, "igboapi_pspk01574": 1598, "igboapi_pspk01575": 1599, "igboapi_pspk01576": 1600, "igboapi_pspk01577": 1601, "igboapi_pspk01578": 1602, "igboapi_pspk01579": 1603, "igboapi_pspk01580": 1604, "igboapi_pspk01581": 1605, "igboapi_pspk01582": 1606, "igboapi_pspk01584": 1607, "igboapi_pspk01585": 1608, "igboapi_pspk01587": 1609, "igboapi_pspk01588": 1610, "igboapi_pspk01589": 1611, "igboapi_pspk01590": 1612, "igboapi_pspk01591": 1613, "igboapi_pspk01592": 1614, "igboapi_pspk01593": 1615, "igboapi_pspk01594": 1616, "igboapi_pspk01595": 1617, "igboapi_pspk01596": 1618, "igboapi_pspk01597": 1619, "igboapi_pspk01598": 1620, "igboapi_pspk01599": 1621, "igboapi_pspk01600": 1622, "igboapi_pspk01601": 1623, "igboapi_pspk01602": 1624, "igboapi_pspk01603": 1625, "igboapi_pspk01604": 1626, "igboapi_pspk01605": 1627, "igboapi_pspk01606": 1628, "igboapi_pspk01607": 1629, "igboapi_pspk01608": 1630, "igboapi_pspk01609": 1631, "igboapi_pspk01610": 1632, "igboapi_pspk01611": 1633, "igboapi_pspk01612": 1634, "igboapi_pspk01613": 1635, "igboapi_pspk01614": 1636, "igboapi_pspk01615": 1637, "igboapi_pspk01616": 1638, "igboapi_pspk01617": 1639, "igboapi_pspk01618": 1640, "igboapi_pspk01619": 1641, "igboapi_pspk01620": 1642, "igboapi_pspk01621": 1643, "igboapi_pspk01622": 1644, "igboapi_pspk01623": 1645, "igboapi_pspk01624": 1646, "igboapi_pspk01625": 1647, "igboapi_pspk01626": 1648, "igboapi_pspk01627": 1649, "igboapi_pspk01628": 1650, "igboapi_pspk01629": 1651, "igboapi_pspk01630": 1652, "igboapi_pspk01631": 1653, "igboapi_pspk01632": 1654, "igboapi_pspk01633": 1655, "igboapi_pspk01634": 1656, "igboapi_pspk01635": 1657, "igboapi_pspk01636": 1658, "igboapi_pspk01637": 1659, "igboapi_pspk01638": 1660, "igboapi_pspk01639": 1661, "igboapi_pspk01640": 1662, "igboapi_pspk01641": 1663, "igboapi_pspk01642": 1664, "igboapi_pspk01643": 1665, "igboapi_pspk01644": 1666, "igboapi_pspk01645": 1667, "igboapi_pspk01646": 1668, "igboapi_pspk01647": 1669, "igboapi_pspk01648": 1670, "igboapi_pspk01649": 1671, "igboapi_pspk01650": 1672, "igboapi_pspk01651": 1673, "igboapi_pspk01652": 1674, "igboapi_pspk01653": 1675, "igboapi_pspk01654": 1676, "igboapi_pspk01656": 1677, "igboapi_pspk01657": 1678, "igboapi_pspk01658": 1679, "igboapi_pspk01659": 1680, "igboapi_pspk01660": 1681, "igboapi_pspk01661": 1682, "igboapi_pspk01662": 1683, "igboapi_pspk01663": 1684, "igboapi_pspk01664": 1685, "igboapi_pspk01665": 1686, "igboapi_pspk01666": 1687, "igboapi_pspk01667": 1688, "igboapi_pspk01668": 1689, "igboapi_pspk01669": 1690, "igboapi_pspk01670": 1691, "igboapi_pspk01671": 1692, "igboapi_pspk01672": 1693, "igboapi_pspk01673": 1694, "igboapi_pspk01674": 1695, "igboapi_pspk01675": 1696, "igboapi_pspk01676": 1697, "igboapi_pspk01677": 1698, "igboapi_pspk01678": 1699, "igboapi_pspk01679": 1700, "igboapi_pspk01680": 1701, "igboapi_pspk01681": 1702, "igboapi_pspk01682": 1703, "igboapi_pspk01683": 1704, "igboapi_pspk01684": 1705, "igboapi_pspk01685": 1706, "igboapi_pspk01686": 1707, "igboapi_pspk01687": 1708, "igboapi_pspk01688": 1709, "igboapi_pspk01689": 1710, "igboapi_pspk01690": 1711, "igboapi_pspk01691": 1712, "igboapi_pspk01692": 1713, "igboapi_pspk01693": 1714, "igboapi_pspk01694": 1715, "igboapi_pspk01695": 1716, "igboapi_pspk01696": 1717, "igboapi_pspk01697": 1718, "igboapi_pspk01698": 1719, "igboapi_pspk01699": 1720, "igboapi_pspk01700": 1721, "igboapi_pspk01701": 1722, "igboapi_pspk01704": 1723, "igboapi_pspk01705": 1724, "igboapi_pspk01706": 1725, "igboapi_pspk01707": 1726, "igboapi_pspk01708": 1727, "igboapi_pspk01709": 1728, "igboapi_pspk01710": 1729, "igboapi_pspk01711": 1730, "igboapi_pspk01712": 1731, "igboapi_pspk01713": 1732, "igboapi_pspk01714": 1733, "igboapi_pspk01715": 1734, "igboapi_pspk01716": 1735, "igboapi_pspk01717": 1736, "igboapi_pspk01718": 1737, "igboapi_pspk01719": 1738, "igboapi_pspk01720": 1739, "igboapi_pspk01721": 1740, "igboapi_pspk01722": 1741, "igboapi_pspk01723": 1742, "igboapi_pspk01724": 1743, "igboapi_pspk01725": 1744, "igboapi_pspk01726": 1745, "igboapi_pspk01727": 1746, "igboapi_pspk01728": 1747, "igboapi_pspk01729": 1748, "igboapi_pspk01730": 1749, "igboapi_pspk01731": 1750, "igboapi_pspk01732": 1751, "igboapi_pspk01733": 1752, "igboapi_pspk01734": 1753, "igboapi_pspk01735": 1754, "igboapi_pspk01736": 1755, "igboapi_pspk01737": 1756, "igboapi_pspk01738": 1757, "igboapi_pspk01739": 1758, "igboapi_pspk01740": 1759, "igboapi_pspk01741": 1760, "igboapi_pspk01742": 1761, "igboapi_pspk01743": 1762, "igboapi_pspk01744": 1763, "igboapi_pspk01745": 1764, "igboapi_pspk01746": 1765, "igboapi_pspk01748": 1766, "igboapi_pspk01749": 1767, "igboapi_pspk01750": 1768, "igboapi_pspk01751": 1769, "igboapi_pspk01752": 1770, "igboapi_pspk01753": 1771, "igboapi_pspk01754": 1772, "igboapi_pspk01755": 1773, "igboapi_pspk01756": 1774, "igboapi_pspk01757": 1775, "igboapi_pspk01758": 1776, "igboapi_pspk01759": 1777, "igboapi_pspk01760": 1778, "igboapi_pspk01761": 1779, "igboapi_pspk01762": 1780, "igboapi_pspk01763": 1781, "igboapi_pspk01764": 1782, "igboapi_pspk01765": 1783, "igboapi_pspk01766": 1784, "igboapi_pspk01767": 1785, "igboapi_pspk01768": 1786, "igboapi_pspk01769": 1787, "igboapi_pspk01770": 1788, "igboapi_pspk01771": 1789, "igboapi_pspk01772": 1790, "igboapi_pspk01773": 1791, "igboapi_pspk01774": 1792, "igboapi_pspk01775": 1793, "igboapi_pspk01776": 1794, "igboapi_pspk01777": 1795, "igboapi_pspk01778": 1796, "igboapi_pspk01779": 1797, "igboapi_pspk01780": 1798, "igboapi_pspk01781": 1799, "igboapi_pspk01782": 1800, "igboapi_pspk01783": 1801, "igboapi_pspk01784": 1802, "igboapi_pspk01785": 1803, "igboapi_pspk01786": 1804, "igboapi_pspk01787": 1805, "igboapi_pspk01788": 1806, "igboapi_pspk01789": 1807, "igboapi_pspk01790": 1808, "igboapi_pspk01791": 1809, "igboapi_pspk01792": 1810, "igboapi_pspk01793": 1811, "igboapi_pspk01794": 1812, "igboapi_pspk01795": 1813, "igboapi_pspk01796": 1814, "igboapi_pspk01797": 1815, "igboapi_pspk01798": 1816, "igboapi_pspk01799": 1817, "igboapi_pspk01800": 1818, "igboapi_pspk01801": 1819, "igboapi_pspk01802": 1820, "igboapi_pspk01803": 1821, "igboapi_pspk01804": 1822, "igboapi_pspk01805": 1823, "igboapi_pspk01806": 1824, "igboapi_pspk01807": 1825, "igboapi_pspk01808": 1826, "igboapi_pspk01809": 1827, "igboapi_pspk01810": 1828, "igboapi_pspk01811": 1829, "igboapi_pspk01812": 1830, "igboapi_pspk01813": 1831, "igboapi_pspk01814": 1832, "igboapi_pspk01815": 1833, "igboapi_pspk01816": 1834, "igboapi_pspk01817": 1835, "igboapi_pspk01818": 1836, "igboapi_pspk01819": 1837, "igboapi_pspk01820": 1838, "igboapi_pspk01821": 1839, "igboapi_pspk01822": 1840, "igboapi_pspk01823": 1841, "igboapi_pspk01824": 1842, "igboapi_pspk01825": 1843, "igboapi_pspk01826": 1844, "igboapi_pspk01827": 1845, "igboapi_pspk01828": 1846, "igboapi_pspk01829": 1847, "igboapi_pspk01830": 1848, "igboapi_pspk01831": 1849, "igboapi_pspk01832": 1850, "igboapi_pspk01833": 1851, "igboapi_pspk01834": 1852, "igboapi_pspk01835": 1853, "igboapi_pspk01836": 1854, "igboapi_pspk01837": 1855, "igboapi_pspk01838": 1856, "igboapi_pspk01839": 1857, "igboapi_pspk01840": 1858, "igboapi_pspk01841": 1859, "igboapi_pspk01842": 1860, "igboapi_pspk01843": 1861, "igboapi_pspk01844": 1862, "igboapi_pspk01845": 1863, "igboapi_pspk01846": 1864, "igboapi_pspk01847": 1865, "igboapi_pspk01848": 1866, "igboapi_pspk01849": 1867, "igboapi_pspk01850": 1868, "igboapi_pspk01851": 1869, "igboapi_pspk01852": 1870, "igboapi_pspk01853": 1871, "igboapi_pspk01854": 1872, "igboapi_pspk01855": 1873, "igboapi_pspk01856": 1874, "igboapi_pspk01857": 1875, "igboapi_pspk01858": 1876, "igboapi_pspk01859": 1877, "igboapi_pspk01860": 1878, "igboapi_pspk01861": 1879, "igboapi_pspk01862": 1880, "igboapi_pspk01863": 1881, "igboapi_pspk01864": 1882, "igboapi_pspk01866": 1883, "igboapi_pspk01867": 1884, "igboapi_pspk01868": 1885, "igboapi_pspk01869": 1886, "igboapi_pspk01870": 1887, "igboapi_pspk01871": 1888, "igboapi_pspk01872": 1889, "igboapi_pspk01873": 1890, "igboapi_pspk01874": 1891, "igboapi_pspk01875": 1892, "igboapi_pspk01876": 1893, "igboapi_pspk01877": 1894, "igboapi_pspk01878": 1895, "igboapi_pspk01879": 1896, "igboapi_pspk01880": 1897, "igboapi_pspk01881": 1898, "igboapi_pspk01882": 1899, "igboapi_pspk01883": 1900, "igboapi_pspk01884": 1901, "igboapi_pspk01885": 1902, "igboapi_pspk01886": 1903, "igboapi_pspk01887": 1904, "igboapi_pspk01888": 1905, "igboapi_pspk01889": 1906, "igboapi_pspk01890": 1907, "igboapi_pspk01891": 1908, "igboapi_pspk01892": 1909, "igboapi_pspk01893": 1910, "igboapi_pspk01894": 1911, "igboapi_pspk01895": 1912, "igboapi_pspk01896": 1913, "igboapi_pspk01897": 1914, "igboapi_pspk01898": 1915, "igboapi_pspk01899": 1916, "igboapi_pspk01900": 1917, "igboapi_pspk01901": 1918, "igboapi_pspk01902": 1919, "igboapi_pspk01903": 1920, "igboapi_pspk01904": 1921, "igboapi_pspk01905": 1922, "igboapi_pspk01906": 1923, "igboapi_pspk01907": 1924, "igboapi_pspk01908": 1925, "igboapi_pspk01909": 1926, "igboapi_pspk01910": 1927, "igboapi_pspk01911": 1928, "igboapi_pspk01912": 1929, "igboapi_pspk01913": 1930, "igboapi_pspk01914": 1931, "igboapi_pspk01915": 1932, "igboapi_pspk01916": 1933, "igboapi_pspk01917": 1934, "igboapi_pspk01918": 1935, "igboapi_pspk01919": 1936, "igboapi_pspk01920": 1937, "igboapi_pspk01921": 1938, "igboapi_pspk01922": 1939, "igboapi_pspk01923": 1940, "igboapi_pspk01924": 1941, "igboapi_pspk01925": 1942, "igboapi_pspk01926": 1943, "igboapi_pspk01927": 1944, "igboapi_pspk01928": 1945, "igboapi_pspk01929": 1946, "igboapi_pspk01930": 1947, "igboapi_pspk01931": 1948, "igboapi_pspk01932": 1949, "igboapi_pspk01933": 1950, "igboapi_pspk01934": 1951, "igboapi_pspk01935": 1952, "igboapi_pspk01936": 1953, "igboapi_pspk01937": 1954, "igboapi_pspk01938": 1955, "igboapi_pspk01939": 1956, "igboapi_pspk01940": 1957, "igboapi_pspk01941": 1958, "igboapi_pspk01942": 1959, "igboapi_pspk01943": 1960, "igboapi_pspk01944": 1961, "igboapi_pspk01945": 1962, "igboapi_pspk01946": 1963, "igboapi_pspk01947": 1964, "igboapi_pspk01948": 1965, "igboapi_pspk01949": 1966, "igboapi_pspk01950": 1967, "igboapi_pspk01951": 1968, "igboapi_pspk01952": 1969, "igboapi_pspk01953": 1970, "igboapi_pspk01954": 1971, "igboapi_pspk01955": 1972, "igboapi_pspk01956": 1973, "igboapi_pspk01957": 1974, "igboapi_pspk01958": 1975, "igboapi_pspk01959": 1976, "igboapi_pspk01960": 1977, "igboapi_pspk01962": 1978, "igboapi_pspk01963": 1979, "igboapi_pspk01964": 1980, "igboapi_pspk01965": 1981, "igboapi_pspk01966": 1982, "igboapi_pspk01967": 1983, "igboapi_pspk01968": 1984, "igboapi_pspk01969": 1985, "igboapi_pspk01970": 1986, "igboapi_pspk01971": 1987, "igboapi_pspk01972": 1988, "igboapi_pspk01973": 1989, "igboapi_pspk01974": 1990, "igboapi_pspk01975": 1991, "igboapi_pspk01976": 1992, "igboapi_pspk01977": 1993, "igboapi_pspk01978": 1994, "igboapi_pspk01979": 1995, "igboapi_pspk01980": 1996, "igboapi_pspk01981": 1997, "igboapi_pspk01982": 1998, "igboapi_pspk01983": 1999, "igboapi_pspk01984": 2000, "igboapi_pspk01985": 2001, "igboapi_pspk01986": 2002, "igboapi_pspk01987": 2003, "igboapi_pspk01988": 2004, "igboapi_pspk01989": 2005, "igboapi_pspk01990": 2006, "igboapi_pspk01991": 2007, "igboapi_pspk01993": 2008, "igboapi_pspk01994": 2009, "igboapi_pspk01995": 2010, "igboapi_pspk01996": 2011, "igboapi_pspk01997": 2012, "igboapi_pspk01998": 2013, "igboapi_pspk01999": 2014, "igboapi_pspk02000": 2015, "igboapi_pspk02001": 2016, "igboapi_pspk02002": 2017, "igboapi_pspk02003": 2018, "igboapi_pspk02004": 2019, "igboapi_pspk02005": 2020, "igboapi_pspk02006": 2021, "igboapi_pspk02007": 2022, "igboapi_pspk02008": 2023, "igboapi_pspk02009": 2024, "igboapi_pspk02010": 2025, "igboapi_pspk02011": 2026, "igboapi_pspk02012": 2027, "igboapi_pspk02013": 2028, "igboapi_pspk02014": 2029, "igboapi_pspk02015": 2030, "igboapi_pspk02016": 2031, "igboapi_pspk02017": 2032, "igboapi_pspk02018": 2033, "igboapi_pspk02019": 2034, "igboapi_pspk02020": 2035, "igboapi_pspk02021": 2036, "igboapi_pspk02022": 2037, "igboapi_pspk02023": 2038, "igboapi_pspk02024": 2039, "igboapi_pspk02025": 2040, "igboapi_pspk02026": 2041, "igboapi_pspk02027": 2042, "igboapi_pspk02028": 2043, "igboapi_pspk02029": 2044, "igboapi_pspk02030": 2045, "igboapi_pspk02031": 2046, "igboapi_pspk02032": 2047, "igboapi_pspk02033": 2048, "igboapi_pspk02034": 2049, "igboapi_pspk02035": 2050, "igboapi_pspk02036": 2051, "igboapi_pspk02037": 2052, "igboapi_pspk02038": 2053, "igboapi_pspk02039": 2054, "igboapi_pspk02040": 2055, "igboapi_pspk02041": 2056, "igboapi_pspk02042": 2057, "igboapi_pspk02043": 2058, "igboapi_pspk02044": 2059, "igboapi_pspk02045": 2060, "igboapi_pspk02046": 2061, "igboapi_pspk02047": 2062, "igboapi_pspk02048": 2063, "igboapi_pspk02049": 2064, "igboapi_pspk02050": 2065, "igboapi_pspk02051": 2066, "igboapi_pspk02052": 2067, "igboapi_pspk02053": 2068, "igboapi_pspk02054": 2069, "igboapi_pspk02055": 2070, "igboapi_pspk02056": 2071, "igboapi_pspk02057": 2072, "igboapi_pspk02058": 2073, "igboapi_pspk02059": 2074, "igboapi_pspk02061": 2075, "igboapi_pspk02062": 2076, "igboapi_pspk02063": 2077, "igboapi_pspk02064": 2078, "igboapi_pspk02065": 2079, "igboapi_pspk02066": 2080, "igboapi_pspk02068": 2081, "igboapi_pspk02069": 2082, "igboapi_pspk02070": 2083, "igboapi_pspk02071": 2084, "igboapi_pspk02072": 2085, "igboapi_pspk02073": 2086, "igboapi_pspk02074": 2087, "igboapi_pspk02075": 2088, "igboapi_pspk02076": 2089, "igboapi_pspk02077": 2090, "igboapi_pspk02078": 2091, "igboapi_pspk02079": 2092, "igboapi_pspk02080": 2093, "igboapi_pspk02081": 2094, "igboapi_pspk02082": 2095, "igboapi_pspk02083": 2096, "igboapi_pspk02084": 2097, "igboapi_pspk02085": 2098, "igboapi_pspk02086": 2099, "igboapi_pspk02087": 2100, "igboapi_pspk02088": 2101, "igboapi_pspk02089": 2102, "igboapi_pspk02090": 2103, "igboapi_pspk02091": 2104, "igboapi_pspk02092": 2105, "igboapi_pspk02093": 2106, "igboapi_pspk02094": 2107, "igboapi_pspk02095": 2108, "igboapi_pspk02096": 2109, "igboapi_pspk02097": 2110, "igboapi_pspk02098": 2111, "igboapi_pspk02099": 2112, "igboapi_pspk02100": 2113, "igboapi_pspk02101": 2114, "igboapi_pspk02102": 2115, "igboapi_pspk02103": 2116, "igboapi_pspk02104": 2117, "igboapi_pspk02105": 2118, "igboapi_pspk02106": 2119, "igboapi_pspk02107": 2120, "igboapi_pspk02108": 2121, "igboapi_pspk02109": 2122, "igboapi_pspk02110": 2123, "igboapi_pspk02111": 2124, "igboapi_pspk02112": 2125, "igboapi_pspk02113": 2126, "igboapi_pspk02114": 2127, "igboapi_pspk02116": 2128, "igboapi_pspk02117": 2129, "igboapi_pspk02118": 2130, "igboapi_pspk02119": 2131, "igboapi_pspk02120": 2132, "igboapi_pspk02121": 2133, "igboapi_pspk02122": 2134, "igboapi_pspk02123": 2135, "igboapi_pspk02124": 2136, "igboapi_pspk02125": 2137, "igboapi_pspk02126": 2138, "igboapi_pspk02127": 2139, "igboapi_pspk02128": 2140, "igboapi_pspk02129": 2141, "igboapi_pspk02130": 2142, "igboapi_pspk02131": 2143, "igboapi_pspk02132": 2144, "igboapi_pspk02133": 2145, "igboapi_pspk02134": 2146, "igboapi_pspk02135": 2147, "igboapi_pspk02136": 2148, "igboapi_pspk02137": 2149, "igboapi_pspk02138": 2150, "igboapi_pspk02139": 2151, "igboapi_pspk02140": 2152, "igboapi_pspk02141": 2153, "igboapi_pspk02142": 2154, "igboapi_pspk02143": 2155, "igboapi_pspk02144": 2156, "igboapi_pspk02145": 2157, "igboapi_pspk02146": 2158, "igboapi_pspk02147": 2159, "igboapi_pspk02148": 2160, "igboapi_pspk02149": 2161, "igboapi_pspk02150": 2162, "igboapi_pspk02151": 2163, "igboapi_pspk02152": 2164, "igboapi_pspk02153": 2165, "igboapi_pspk02154": 2166, "igboapi_pspk02155": 2167, "igboapi_pspk02156": 2168, "igboapi_pspk02157": 2169, "igboapi_pspk02158": 2170, "igboapi_pspk02159": 2171, "igboapi_pspk02160": 2172, "igboapi_pspk02161": 2173, "igboapi_pspk02162": 2174, "igboapi_pspk02163": 2175, "igboapi_pspk02164": 2176, "igboapi_pspk02165": 2177, "igboapi_pspk02166": 2178, "igboapi_pspk02167": 2179, "igboapi_pspk02168": 2180, "igboapi_pspk02169": 2181, "igboapi_pspk02170": 2182, "igboapi_pspk02172": 2183, "igboapi_pspk02173": 2184, "igboapi_pspk02174": 2185, "igboapi_pspk02175": 2186, "igboapi_pspk02176": 2187, "igboapi_pspk02177": 2188, "igboapi_pspk02178": 2189, "igboapi_pspk02179": 2190, "igboapi_pspk02180": 2191, "igboapi_pspk02181": 2192, "igboapi_pspk02182": 2193, "igboapi_pspk02183": 2194, "igboapi_pspk02184": 2195, "igboapi_pspk02185": 2196, "igboapi_pspk02186": 2197, "igboapi_pspk02187": 2198, "igboapi_pspk02188": 2199, "igboapi_pspk02189": 2200, "igboapi_pspk02190": 2201, "igboapi_pspk02191": 2202, "igboapi_pspk02193": 2203, "igboapi_pspk02194": 2204, "igboapi_pspk02195": 2205, "igboapi_pspk02196": 2206, "igboapi_pspk02197": 2207, "igboapi_pspk02198": 2208, "igboapi_pspk02199": 2209, "igboapi_pspk02200": 2210, "igboapi_pspk02201": 2211, "igboapi_pspk02202": 2212, "igboapi_pspk02203": 2213, "igboapi_pspk02205": 2214, "igboapi_pspk02206": 2215, "igboapi_pspk02207": 2216, "igboapi_pspk02208": 2217, "igboapi_pspk02209": 2218, "igboapi_pspk02211": 2219, "igboapi_pspk02212": 2220, "igboapi_pspk02213": 2221, "igboapi_pspk02214": 2222, "igboapi_pspk02215": 2223, "igboapi_pspk02216": 2224, "igboapi_pspk02217": 2225, "igboapi_pspk02218": 2226, "igboapi_pspk02219": 2227, "igboapi_pspk02220": 2228, "igboapi_pspk02221": 2229, "igboapi_pspk02222": 2230, "igboapi_pspk02223": 2231, "igboapi_pspk02224": 2232, "igboapi_pspk02225": 2233, "igboapi_pspk02226": 2234, "igboapi_pspk02227": 2235, "igboapi_pspk02228": 2236, "igboapi_pspk02229": 2237, "igboapi_pspk02230": 2238, "igboapi_pspk02231": 2239, "igboapi_pspk02232": 2240, "igboapi_pspk02233": 2241, "igboapi_pspk02234": 2242, "igboapi_pspk02235": 2243, "igboapi_pspk02236": 2244, "igboapi_pspk02237": 2245, "igboapi_pspk02238": 2246, "igboapi_pspk02239": 2247, "igboapi_pspk02240": 2248, "igboapi_pspk02241": 2249, "igboapi_pspk02242": 2250, "igboapi_pspk02243": 2251, "igboapi_pspk02244": 2252, "igboapi_pspk02245": 2253, "igboapi_pspk02246": 2254, "igboapi_pspk02247": 2255, "igboapi_pspk02248": 2256, "igboapi_pspk02249": 2257, "igboapi_pspk02251": 2258, "igboapi_pspk02252": 2259, "igboapi_pspk02253": 2260, "igboapi_pspk02254": 2261, "igboapi_pspk02255": 2262, "igboapi_pspk02256": 2263, "igboapi_pspk02257": 2264, "igboapi_pspk02258": 2265, "igboapi_pspk02259": 2266, "igboapi_pspk02260": 2267, "igboapi_pspk02261": 2268, "igboapi_pspk02262": 2269, "igboapi_pspk02263": 2270, "igboapi_pspk02264": 2271, "igboapi_pspk02265": 2272, "igboapi_pspk02266": 2273, "igboapi_pspk02267": 2274, "igboapi_pspk02268": 2275, "igboapi_pspk02269": 2276, "igboapi_pspk02270": 2277, "igboapi_pspk02271": 2278, "igboapi_pspk02272": 2279, "igboapi_pspk02273": 2280, "igboapi_pspk02274": 2281, "igboapi_pspk02275": 2282, "igboapi_pspk02276": 2283, "igboapi_pspk02277": 2284, "igboapi_pspk02278": 2285, "igboapi_pspk02279": 2286, "igboapi_pspk02280": 2287, "igboapi_pspk02281": 2288, "igboapi_pspk02282": 2289, "igboapi_pspk02283": 2290, "igboapi_pspk02284": 2291, "igboapi_pspk02285": 2292, "igboapi_pspk02286": 2293, "igboapi_pspk02287": 2294, "igboapi_pspk02288": 2295, "igboapi_pspk02289": 2296, "igboapi_pspk02290": 2297, "igboapi_pspk02291": 2298, "igboapi_pspk02292": 2299, "igboapi_pspk02293": 2300, "igboapi_pspk02294": 2301, "igboapi_pspk02295": 2302, "igboapi_pspk02296": 2303, "igboapi_pspk02297": 2304, "igboapi_pspk02298": 2305, "igboapi_pspk02299": 2306, "igboapi_pspk02300": 2307, "igboapi_pspk02301": 2308, "igboapi_pspk02302": 2309, "igboapi_pspk02303": 2310, "igboapi_pspk02304": 2311, "igboapi_pspk02305": 2312, "igboapi_pspk02306": 2313, "igboapi_pspk02307": 2314, "igboapi_pspk02308": 2315, "igboapi_pspk02309": 2316, "igboapi_pspk02310": 2317, "igboapi_pspk02311": 2318, "igboapi_pspk02312": 2319, "igboapi_pspk02313": 2320, "igboapi_pspk02314": 2321, "igboapi_pspk02315": 2322, "igboapi_pspk02316": 2323, "igboapi_pspk02317": 2324, "igboapi_pspk02318": 2325, "igboapi_pspk02319": 2326, "igboapi_pspk02320": 2327, "igboapi_pspk02321": 2328, "igboapi_pspk02322": 2329, "igboapi_pspk02323": 2330, "igboapi_pspk02324": 2331, "igboapi_pspk02325": 2332, "igboapi_pspk02326": 2333, "igboapi_pspk02329": 2334, "igboapi_pspk02330": 2335, "igboapi_pspk02331": 2336, "igboapi_pspk02332": 2337, "igboapi_pspk02333": 2338, "igboapi_pspk02334": 2339, "igboapi_pspk02335": 2340, "igboapi_pspk02336": 2341, "igboapi_pspk02337": 2342, "igboapi_pspk02339": 2343, "igboapi_pspk02341": 2344, "igboapi_pspk02342": 2345, "igboapi_pspk02343": 2346, "igboapi_pspk02344": 2347, "igboapi_pspk02345": 2348, "igboapi_pspk02346": 2349, "igboapi_pspk02347": 2350, "igboapi_pspk02348": 2351, "igboapi_pspk02349": 2352, "igboapi_pspk02350": 2353, "igboapi_pspk02351": 2354, "igboapi_pspk02352": 2355, "igboapi_pspk02353": 2356, "igboapi_pspk02354": 2357, "igboapi_pspk02355": 2358, "igboapi_pspk02356": 2359, "igboapi_pspk02357": 2360, "igboapi_pspk02358": 2361, "igboapi_pspk02359": 2362, "igboapi_pspk02360": 2363, "igboapi_pspk02361": 2364, "igboapi_pspk02362": 2365, "igboapi_pspk02363": 2366, "igboapi_pspk02364": 2367, "igboapi_pspk02365": 2368, "igboapi_pspk02366": 2369, "igboapi_pspk02367": 2370, "igboapi_pspk02368": 2371, "igboapi_pspk02369": 2372, "igboapi_pspk02370": 2373, "igboapi_pspk02371": 2374, "igboapi_pspk02372": 2375, "igboapi_pspk02373": 2376, "igboapi_pspk02374": 2377, "igboapi_pspk02375": 2378, "igboapi_pspk02376": 2379, "igboapi_pspk02377": 2380, "igboapi_pspk02378": 2381, "igboapi_pspk02379": 2382, "igboapi_pspk02380": 2383, "igboapi_pspk02381": 2384, "igboapi_pspk02382": 2385, "igboapi_pspk02383": 2386, "igboapi_pspk02384": 2387, "igboapi_pspk02385": 2388, "igboapi_pspk02386": 2389, "igboapi_pspk02387": 2390, "igboapi_pspk02388": 2391, "igboapi_pspk02389": 2392, "igboapi_pspk02390": 2393, "igboapi_pspk02391": 2394, "igboapi_pspk02392": 2395, "igboapi_pspk02393": 2396, "igboapi_pspk02394": 2397, "igboapi_pspk02395": 2398, "igboapi_pspk02396": 2399, "igboapi_pspk02397": 2400, "igboapi_pspk02398": 2401, "igboapi_pspk02399": 2402, "igboapi_pspk02400": 2403, "igboapi_pspk02401": 2404, "igboapi_pspk02402": 2405, "igboapi_pspk02403": 2406, "igboapi_pspk02404": 2407, "igboapi_pspk02405": 2408, "igboapi_pspk02406": 2409, "igboapi_pspk02407": 2410, "igboapi_pspk02408": 2411, "igboapi_pspk02409": 2412, "igboapi_pspk02410": 2413, "igboapi_pspk02411": 2414, "igboapi_pspk02412": 2415, "igboapi_pspk02413": 2416, "igboapi_pspk02414": 2417, "igboapi_pspk02415": 2418, "igboapi_pspk02416": 2419, "igboapi_pspk02417": 2420, "igboapi_pspk02418": 2421, "igboapi_pspk02419": 2422, "igboapi_pspk02420": 2423, "igboapi_pspk02421": 2424, "igboapi_pspk02422": 2425, "igboapi_pspk02423": 2426, "igboapi_pspk02424": 2427, "igboapi_pspk02425": 2428, "igboapi_pspk02426": 2429, "igboapi_pspk02427": 2430, "igboapi_pspk02428": 2431, "igboapi_pspk02429": 2432, "igboapi_pspk02430": 2433, "igboapi_pspk02431": 2434, "igboapi_pspk02432": 2435, "igboapi_pspk02433": 2436, "igboapi_pspk02434": 2437, "igboapi_pspk02435": 2438, "igboapi_pspk02436": 2439, "igboapi_pspk02437": 2440, "igboapi_pspk02438": 2441, "igboapi_pspk02439": 2442, "igboapi_pspk02440": 2443, "igboapi_pspk02441": 2444, "igboapi_pspk02442": 2445, "igboapi_pspk02443": 2446, "igboapi_pspk02444": 2447, "igboapi_pspk02445": 2448, "igboapi_pspk02446": 2449, "igboapi_pspk02447": 2450, "igboapi_pspk02448": 2451, "igboapi_pspk02449": 2452, "igboapi_pspk02450": 2453, "igboapi_pspk02451": 2454, "igboapi_pspk02452": 2455, "igboapi_pspk02453": 2456, "igboapi_pspk02454": 2457, "igboapi_pspk02455": 2458, "igboapi_pspk02456": 2459, "igboapi_pspk02457": 2460, "igboapi_pspk02458": 2461, "igboapi_pspk02459": 2462, "igboapi_pspk02460": 2463, "igboapi_pspk02461": 2464, "igboapi_pspk02462": 2465, "igboapi_pspk02463": 2466, "igboapi_pspk02464": 2467, "igboapi_pspk02465": 2468, "igboapi_pspk02466": 2469, "igboapi_pspk02467": 2470, "igboapi_pspk02468": 2471, "igboapi_pspk02469": 2472, "igboapi_pspk02470": 2473, "igboapi_pspk02471": 2474, "igboapi_pspk02472": 2475, "igboapi_pspk02473": 2476, "igboapi_pspk02474": 2477, "igboapi_pspk02475": 2478, "igboapi_pspk02476": 2479, "igboapi_pspk02477": 2480, "igboapi_pspk02478": 2481, "igboapi_pspk02479": 2482, "igboapi_pspk02480": 2483, "igboapi_pspk02481": 2484, "igboapi_pspk02482": 2485, "igboapi_pspk02483": 2486, "igboapi_pspk02484": 2487, "igboapi_pspk02485": 2488, "igboapi_pspk02486": 2489, "igboapi_pspk02487": 2490, "igboapi_pspk02488": 2491, "igboapi_pspk02489": 2492, "igboapi_pspk02490": 2493, "igboapi_pspk02491": 2494, "igboapi_pspk02492": 2495, "igboapi_pspk02493": 2496, "igboapi_pspk02494": 2497, "igboapi_pspk02495": 2498, "igboapi_pspk02496": 2499, "igboapi_pspk02497": 2500, "igboapi_pspk02498": 2501, "igboapi_pspk02499": 2502, "igboapi_pspk02500": 2503, "igboapi_pspk02501": 2504, "igboapi_pspk02502": 2505, "igboapi_pspk02503": 2506, "igboapi_pspk02504": 2507, "igboapi_pspk02505": 2508, "igboapi_pspk02506": 2509, "igboapi_pspk02507": 2510, "igboapi_pspk02508": 2511, "igboapi_pspk02509": 2512, "igboapi_pspk02510": 2513, "igboapi_pspk02511": 2514, "igboapi_pspk02512": 2515, "igboapi_pspk02513": 2516, "igboapi_pspk02514": 2517, "igboapi_pspk02515": 2518, "igboapi_pspk02516": 2519, "igboapi_pspk02517": 2520, "igboapi_pspk02518": 2521, "igboapi_pspk02519": 2522, "igboapi_pspk02520": 2523, "igboapi_pspk02521": 2524, "igboapi_pspk02522": 2525, "igboapi_pspk02523": 2526, "igboapi_pspk02524": 2527, "igboapi_pspk02525": 2528, "igboapi_pspk02526": 2529, "igboapi_pspk02527": 2530, "igboapi_pspk02528": 2531, "igboapi_pspk02529": 2532, "igboapi_pspk02530": 2533, "igboapi_pspk02531": 2534, "igboapi_pspk02532": 2535, "igboapi_pspk02533": 2536, "igboapi_pspk02535": 2537, "igboapi_pspk02536": 2538, "igboapi_pspk02537": 2539, "igboapi_pspk02539": 2540, "igboapi_pspk02540": 2541, "igboapi_pspk02541": 2542, "igboapi_pspk02542": 2543, "igboapi_pspk02543": 2544, "igboapi_pspk02544": 2545, "igboapi_pspk02545": 2546, "igboapi_pspk02546": 2547, "igboapi_pspk02547": 2548, "igboapi_pspk02548": 2549, "igboapi_pspk02549": 2550, "igboapi_pspk02550": 2551, "igboapi_pspk02551": 2552, "igboapi_pspk02552": 2553, "igboapi_pspk02553": 2554, "igboapi_pspk02554": 2555, "igboapi_pspk02555": 2556, "igboapi_pspk02556": 2557, "igboapi_pspk02557": 2558, "igboapi_pspk02558": 2559, "igboapi_pspk02559": 2560, "igboapi_pspk02560": 2561, "igboapi_pspk02561": 2562, "igboapi_pspk02562": 2563, "igboapi_pspk02563": 2564, "igboapi_pspk02564": 2565, "igboapi_pspk02565": 2566, "igboapi_pspk02566": 2567, "igboapi_pspk02567": 2568, "igboapi_pspk02568": 2569, "igboapi_pspk02569": 2570, "igboapi_pspk02570": 2571, "igboapi_pspk02571": 2572, "igboapi_pspk02572": 2573, "igboapi_pspk02573": 2574, "igboapi_pspk02574": 2575, "igboapi_pspk02575": 2576, "igboapi_pspk02576": 2577, "igboapi_pspk02577": 2578, "igboapi_pspk02578": 2579, "igboapi_pspk02579": 2580, "igboapi_pspk02580": 2581, "igboapi_pspk02581": 2582, "igboapi_pspk02582": 2583, "igboapi_pspk02583": 2584, "igboapi_pspk02584": 2585, "igboapi_pspk02585": 2586, "igboapi_pspk02586": 2587, "igboapi_pspk02587": 2588, "igboapi_pspk02588": 2589, "igboapi_pspk02589": 2590, "igboapi_pspk02590": 2591, "igboapi_pspk02591": 2592, "igboapi_pspk02592": 2593, "igboapi_pspk02593": 2594, "igboapi_pspk02594": 2595, "igboapi_pspk02595": 2596, "igboapi_pspk02596": 2597, "igboapi_pspk02597": 2598, "igboapi_pspk02598": 2599, "igboapi_pspk02599": 2600, "igboapi_pspk02600": 2601, "igboapi_pspk02601": 2602, "igboapi_pspk02602": 2603, "igboapi_pspk02603": 2604, "igboapi_pspk02604": 2605, "igboapi_pspk02605": 2606, "igboapi_pspk02606": 2607, "igboapi_pspk02607": 2608, "igboapi_pspk02608": 2609, "igboapi_pspk02609": 2610, "igboapi_pspk02610": 2611, "igboapi_pspk02611": 2612, "igboapi_pspk02612": 2613, "igboapi_pspk02613": 2614, "igboapi_pspk02614": 2615, "igboapi_pspk02615": 2616, "igboapi_pspk02616": 2617, "igboapi_pspk02617": 2618, "igboapi_pspk02618": 2619, "igboapi_pspk02619": 2620, "igboapi_pspk02620": 2621, "igboapi_pspk02621": 2622, "igboapi_pspk02622": 2623, "igboapi_pspk02623": 2624, "igboapi_pspk02624": 2625, "igboapi_pspk02625": 2626, "igboapi_pspk02626": 2627, "igboapi_pspk02627": 2628, "igboapi_pspk02628": 2629, "igboapi_pspk02629": 2630, "igboapi_pspk02630": 2631, "igboapi_pspk02631": 2632, "igboapi_pspk02632": 2633, "igboapi_pspk02633": 2634, "igboapi_pspk02634": 2635, "igboapi_pspk02635": 2636, "igboapi_pspk02636": 2637, "igboapi_pspk02637": 2638, "igboapi_pspk02638": 2639, "igboapi_pspk02639": 2640, "igboapi_pspk02640": 2641, "igboapi_pspk02641": 2642, "igboapi_pspk02642": 2643, "igboapi_pspk02643": 2644, "igboapi_pspk02644": 2645, "igboapi_pspk02645": 2646, "igboapi_pspk02648": 2647, "igboapi_pspk02649": 2648, "igboapi_pspk02650": 2649, "igboapi_pspk02651": 2650, "igboapi_pspk02652": 2651, "igboapi_pspk02653": 2652, "igboapi_pspk02655": 2653, "igboapi_pspk02656": 2654, "igboapi_pspk02657": 2655, "igboapi_pspk02658": 2656, "igboapi_pspk02659": 2657, "igboapi_pspk02661": 2658, "igboapi_pspk02662": 2659, "igboapi_pspk02663": 2660, "igboapi_pspk02664": 2661, "igboapi_pspk02665": 2662, "igboapi_pspk02666": 2663, "igboapi_pspk02667": 2664, "igboapi_pspk02668": 2665, "igboapi_pspk02669": 2666, "igboapi_pspk02670": 2667, "igboapi_pspk02671": 2668, "igboapi_pspk02672": 2669, "igboapi_pspk02673": 2670, "igboapi_pspk02674": 2671, "igboapi_pspk02675": 2672, "igboapi_pspk02676": 2673, "igboapi_pspk02677": 2674, "igboapi_pspk02678": 2675, "igboapi_pspk02679": 2676, "igboapi_pspk02680": 2677, "igboapi_pspk02681": 2678, "igboapi_pspk02682": 2679, "igboapi_pspk02683": 2680, "igboapi_pspk02684": 2681, "igboapi_pspk02685": 2682, "igboapi_pspk02686": 2683, "igboapi_pspk02687": 2684, "igboapi_pspk02688": 2685, "igboapi_pspk02689": 2686, "igboapi_pspk02690": 2687, "igboapi_pspk02691": 2688, "igboapi_pspk02692": 2689, "igboapi_pspk02693": 2690, "igboapi_pspk02694": 2691, "igboapi_pspk02695": 2692, "igboapi_pspk02696": 2693, "igboapi_pspk02697": 2694, "igboapi_pspk02698": 2695, "igboapi_pspk02699": 2696, "igboapi_pspk02700": 2697, "igboapi_pspk02701": 2698, "igboapi_pspk02702": 2699, "igboapi_pspk02703": 2700, "igboapi_pspk02704": 2701, "igboapi_pspk02705": 2702, "igboapi_pspk02706": 2703, "igboapi_pspk02707": 2704, "igboapi_pspk02708": 2705, "igboapi_pspk02709": 2706, "igboapi_pspk02710": 2707, "igboapi_pspk02711": 2708, "igboapi_pspk02712": 2709, "igboapi_pspk02713": 2710, "igboapi_pspk02714": 2711, "igboapi_pspk02715": 2712, "igboapi_pspk02716": 2713, "igboapi_pspk02718": 2714, "igboapi_pspk02719": 2715, "igboapi_pspk02720": 2716, "igboapi_pspk02721": 2717, "igboapi_pspk02722": 2718, "igboapi_pspk02723": 2719, "igboapi_pspk02724": 2720, "igboapi_pspk02725": 2721, "igboapi_pspk02726": 2722, "igboapi_pspk02727": 2723, "igboapi_pspk02728": 2724, "igboapi_pspk02729": 2725, "igboapi_pspk02730": 2726, "igboapi_pspk02731": 2727, "igboapi_pspk02732": 2728, "igboapi_pspk02733": 2729, "igboapi_pspk02734": 2730, "igboapi_pspk02735": 2731, "igboapi_pspk02736": 2732, "igboapi_pspk02737": 2733, "igboapi_pspk02738": 2734, "igboapi_pspk02739": 2735, "igboapi_pspk02740": 2736, "igboapi_pspk02741": 2737, "igboapi_pspk02742": 2738, "igboapi_pspk02744": 2739, "igboapi_pspk02745": 2740, "igboapi_pspk02746": 2741, "igboapi_pspk02747": 2742, "igboapi_pspk02748": 2743, "igboapi_pspk02749": 2744, "igboapi_pspk02751": 2745, "igboapi_pspk02752": 2746, "igboapi_pspk02753": 2747, "igboapi_pspk02754": 2748, "igboapi_pspk02755": 2749, "igboapi_pspk02756": 2750, "igboapi_pspk02757": 2751, "igboapi_pspk02758": 2752, "igboapi_pspk02759": 2753, "igboapi_pspk02760": 2754, "igboapi_pspk02761": 2755, "igboapi_pspk02763": 2756, "igboapi_pspk02764": 2757, "igboapi_pspk02765": 2758, "igboapi_pspk02766": 2759, "igboapi_pspk02767": 2760, "igboapi_pspk02768": 2761, "igboapi_pspk02769": 2762, "igboapi_pspk02770": 2763, "igboapi_pspk02771": 2764, "igboapi_pspk02772": 2765, "igboapi_pspk02773": 2766, "igboapi_pspk02774": 2767, "igboapi_pspk02775": 2768, "igboapi_pspk02776": 2769, "igboapi_pspk02778": 2770, "igboapi_pspk02779": 2771, "igboapi_pspk02780": 2772, "igboapi_pspk02781": 2773, "igboapi_pspk02782": 2774, "igboapi_pspk02783": 2775, "igboapi_pspk02784": 2776, "igboapi_pspk02785": 2777, "igboapi_pspk02786": 2778, "igboapi_pspk02787": 2779, "igboapi_pspk02788": 2780, "igboapi_pspk02789": 2781, "igboapi_pspk02790": 2782, "igboapi_pspk02791": 2783, "igboapi_pspk02792": 2784, "igboapi_pspk02793": 2785, "igboapi_pspk02794": 2786, "igboapi_pspk02795": 2787, "igboapi_pspk02796": 2788, "igboapi_pspk02797": 2789, "igboapi_pspk02798": 2790, "igboapi_pspk02799": 2791, "igboapi_pspk02800": 2792, "igboapi_pspk02801": 2793, "igboapi_pspk02802": 2794, "igboapi_pspk02803": 2795, "igboapi_pspk02804": 2796, "igboapi_pspk02805": 2797, "igboapi_pspk02806": 2798, "igboapi_pspk02807": 2799, "igboapi_pspk02808": 2800, "igboapi_pspk02809": 2801, "igboapi_pspk02810": 2802, "igboapi_pspk02811": 2803, "igboapi_pspk02812": 2804, "igboapi_pspk02813": 2805, "igboapi_pspk02814": 2806, "igboapi_pspk02815": 2807, "igboapi_pspk02816": 2808, "igboapi_pspk02817": 2809, "igboapi_pspk02818": 2810, "igboapi_pspk02819": 2811, "igboapi_pspk02820": 2812, "igboapi_pspk02821": 2813, "igboapi_pspk02822": 2814, "igboapi_pspk02823": 2815, "igboapi_pspk02824": 2816, "igboapi_pspk02825": 2817, "igboapi_pspk02827": 2818, "igboapi_pspk02828": 2819, "igboapi_pspk02829": 2820, "igboapi_pspk02830": 2821, "igboapi_pspk02831": 2822, "igboapi_pspk02832": 2823, "igboapi_pspk02833": 2824, "igboapi_pspk02834": 2825, "igboapi_pspk02835": 2826, "igboapi_pspk02836": 2827, "igboapi_pspk02837": 2828, "igboapi_pspk02838": 2829, "igboapi_pspk02839": 2830, "igboapi_pspk02840": 2831, "igboapi_pspk02841": 2832, "igboapi_pspk02842": 2833, "igboapi_pspk02843": 2834, "igboapi_pspk02844": 2835, "igboapi_pspk02845": 2836, "igboapi_pspk02846": 2837, "igboapi_pspk02847": 2838, "igboapi_pspk02848": 2839, "igboapi_pspk02849": 2840, "igboapi_pspk02850": 2841, "igboapi_pspk02851": 2842, "igboapi_pspk02852": 2843, "igboapi_pspk02853": 2844, "igboapi_pspk02854": 2845, "igboapi_pspk02855": 2846, "igboapi_pspk02856": 2847, "igboapi_pspk02857": 2848, "igboapi_pspk02858": 2849, "igboapi_pspk02859": 2850, "igboapi_pspk02860": 2851, "igboapi_pspk02861": 2852, "igboapi_pspk02862": 2853, "igboapi_pspk02863": 2854, "igboapi_pspk02864": 2855, "igboapi_pspk02865": 2856, "igboapi_pspk02866": 2857, "igboapi_pspk02867": 2858, "igboapi_pspk02870": 2859, "igboapi_pspk02871": 2860, "igboapi_pspk02872": 2861, "igboapi_pspk02873": 2862, "igboapi_pspk02874": 2863, "igboapi_pspk02875": 2864, "igboapi_pspk02876": 2865, "igboapi_pspk02878": 2866, "igboapi_pspk02879": 2867, "igboapi_pspk02880": 2868, "igboapi_pspk02881": 2869, "igboapi_pspk02882": 2870, "igboapi_pspk02883": 2871, "igboapi_pspk02884": 2872, "igboapi_pspk02885": 2873, "igboapi_pspk02886": 2874, "igboapi_pspk02887": 2875, "igboapi_pspk02888": 2876, "igboapi_pspk02889": 2877, "igboapi_pspk02890": 2878, "igboapi_pspk02891": 2879, "igboapi_pspk02892": 2880, "igboapi_pspk02893": 2881, "igboapi_pspk02894": 2882, "igboapi_pspk02895": 2883, "igboapi_pspk02896": 2884, "igboapi_pspk02897": 2885, "igboapi_pspk02898": 2886, "igboapi_pspk02899": 2887, "igboapi_pspk02900": 2888, "igboapi_pspk02901": 2889, "igboapi_pspk02902": 2890, "igboapi_pspk02903": 2891, "igboapi_pspk02904": 2892, "igboapi_pspk02905": 2893, "igboapi_pspk02906": 2894, "igboapi_pspk02907": 2895, "igboapi_pspk02908": 2896, "igboapi_pspk02909": 2897, "igboapi_pspk02910": 2898, "igboapi_pspk02911": 2899, "igboapi_pspk02912": 2900, "igboapi_pspk02913": 2901, "igboapi_pspk02914": 2902, "igboapi_pspk02915": 2903, "igboapi_pspk02916": 2904, "igboapi_pspk02917": 2905, "igboapi_pspk02918": 2906, "igboapi_pspk02919": 2907, "igboapi_pspk02920": 2908, "igboapi_pspk02921": 2909, "igboapi_pspk02922": 2910, "igboapi_pspk02923": 2911, "igboapi_pspk02924": 2912, "igboapi_pspk02925": 2913, "igboapi_pspk02926": 2914, "igboapi_pspk02927": 2915, "igboapi_pspk02928": 2916, "igboapi_pspk02929": 2917, "igboapi_pspk02930": 2918, "igboapi_pspk02931": 2919, "igboapi_pspk02932": 2920, "igboapi_pspk02933": 2921, "igboapi_pspk02934": 2922, "igboapi_pspk02935": 2923, "igboapi_pspk02936": 2924, "igboapi_pspk02937": 2925, "igboapi_pspk02938": 2926, "igboapi_pspk02939": 2927, "igboapi_pspk02940": 2928, "igboapi_pspk02941": 2929, "igboapi_pspk02942": 2930, "igboapi_pspk02943": 2931, "igboapi_pspk02944": 2932, "igboapi_pspk02945": 2933, "igboapi_pspk02946": 2934, "igboapi_pspk02947": 2935, "igboapi_pspk02948": 2936, "igboapi_pspk02949": 2937, "igboapi_pspk02950": 2938, "igboapi_pspk02951": 2939, "igboapi_pspk02952": 2940, "igboapi_pspk02954": 2941, "igboapi_pspk02955": 2942, "igboapi_pspk02956": 2943, "igboapi_pspk02957": 2944, "igboapi_pspk02958": 2945, "igboapi_pspk02959": 2946, "igboapi_pspk02960": 2947, "igboapi_pspk02961": 2948, "igboapi_pspk02962": 2949, "igboapi_pspk02963": 2950, "igboapi_pspk02964": 2951, "igboapi_pspk02965": 2952, "igboapi_pspk02966": 2953, "igboapi_pspk02967": 2954, "igboapi_pspk02968": 2955, "igboapi_pspk02969": 2956, "igboapi_pspk02970": 2957, "igboapi_pspk02971": 2958, "igboapi_pspk02972": 2959, "igboapi_pspk02973": 2960, "igboapi_pspk02974": 2961, "igboapi_pspk02975": 2962, "igboapi_pspk02976": 2963, "igboapi_pspk02978": 2964, "igboapi_pspk02979": 2965, "igboapi_pspk02981": 2966, "igboapi_pspk02982": 2967, "igboapi_pspk02983": 2968, "igboapi_pspk02984": 2969, "igboapi_pspk02985": 2970, "igboapi_pspk02986": 2971, "igboapi_pspk02987": 2972, "igboapi_pspk02989": 2973, "igboapi_pspk02990": 2974, "igboapi_pspk02991": 2975, "igboapi_pspk02992": 2976, "igboapi_pspk02993": 2977, "igboapi_pspk02994": 2978, "igboapi_pspk02995": 2979, "igboapi_pspk02996": 2980, "igboapi_pspk02997": 2981, "igboapi_pspk02998": 2982, "igboapi_pspk02999": 2983, "igboapi_pspk03000": 2984, "igboapi_pspk03001": 2985, "igboapi_pspk03002": 2986, "igboapi_pspk03003": 2987, "igboapi_pspk03004": 2988, "igboapi_pspk03005": 2989, "igboapi_pspk03006": 2990, "igboapi_pspk03007": 2991, "igboapi_pspk03008": 2992, "igboapi_pspk03009": 2993, "igboapi_pspk03010": 2994, "igboapi_pspk03011": 2995, "igboapi_pspk03012": 2996, "igboapi_pspk03013": 2997, "igboapi_pspk03014": 2998, "igboapi_pspk03015": 2999, "igboapi_pspk03016": 3000, "igboapi_pspk03017": 3001, "igboapi_pspk03018": 3002, "igboapi_pspk03019": 3003, "igboapi_pspk03020": 3004, "igboapi_pspk03021": 3005, "igboapi_pspk03022": 3006, "igboapi_pspk03023": 3007, "igboapi_pspk03024": 3008, "igboapi_pspk03025": 3009, "igboapi_pspk03026": 3010, "igboapi_pspk03027": 3011, "igboapi_pspk03028": 3012, "igboapi_pspk03029": 3013, "igboapi_pspk03030": 3014, "igboapi_pspk03031": 3015, "igboapi_pspk03032": 3016, "igboapi_pspk03033": 3017, "igboapi_pspk03034": 3018, "igboapi_pspk03035": 3019, "igboapi_pspk03036": 3020, "igboapi_pspk03037": 3021, "igboapi_pspk03038": 3022, "igboapi_pspk03039": 3023, "igboapi_pspk03040": 3024, "igboapi_pspk03041": 3025, "igboapi_pspk03042": 3026, "igboapi_pspk03043": 3027, "igboapi_pspk03044": 3028, "igboapi_pspk03045": 3029, "igboapi_pspk03046": 3030, "igboapi_pspk03047": 3031, "igboapi_pspk03048": 3032, "igboapi_pspk03049": 3033, "igboapi_pspk03050": 3034, "igboapi_pspk03051": 3035, "igboapi_pspk03052": 3036, "igboapi_pspk03053": 3037, "igboapi_pspk03054": 3038, "igboapi_pspk03055": 3039, "igboapi_pspk03056": 3040, "igboapi_pspk03057": 3041, "igboapi_pspk03058": 3042, "igboapi_pspk03059": 3043, "igboapi_pspk03060": 3044, "igboapi_pspk03061": 3045, "igboapi_pspk03062": 3046, "igboapi_pspk03063": 3047, "igboapi_pspk03064": 3048, "igboapi_pspk03065": 3049, "igboapi_pspk03066": 3050, "igboapi_pspk03067": 3051, "igboapi_pspk03068": 3052, "igboapi_pspk03069": 3053, "igboapi_pspk03070": 3054, "igboapi_pspk03071": 3055, "igboapi_pspk03072": 3056, "igboapi_pspk03073": 3057, "igboapi_pspk03074": 3058, "igboapi_pspk03076": 3059, "igboapi_pspk03077": 3060, "igboapi_pspk03078": 3061, "igboapi_pspk03079": 3062, "igboapi_pspk03080": 3063, "igboapi_pspk03081": 3064, "igboapi_pspk03082": 3065, "igboapi_pspk03083": 3066, "igboapi_pspk03084": 3067, "igboapi_pspk03085": 3068, "igboapi_pspk03086": 3069, "igboapi_pspk03087": 3070, "igboapi_pspk03088": 3071, "igboapi_pspk03089": 3072, "igboapi_pspk03090": 3073, "igboapi_pspk03091": 3074, "igboapi_pspk03092": 3075, "igboapi_pspk03093": 3076, "igboapi_pspk03094": 3077, "igboapi_pspk03095": 3078, "igboapi_pspk03096": 3079, "igboapi_pspk03097": 3080, "igboapi_pspk03098": 3081, "igboapi_pspk03099": 3082, "igboapi_pspk03100": 3083, "igboapi_pspk03101": 3084, "igboapi_pspk03102": 3085, "igboapi_pspk03103": 3086, "igboapi_pspk03104": 3087, "igboapi_pspk03105": 3088, "igboapi_pspk03106": 3089, "igboapi_pspk03107": 3090, "igboapi_pspk03108": 3091, "igboapi_pspk03109": 3092, "igboapi_pspk03110": 3093, "igboapi_pspk03111": 3094, "igboapi_pspk03112": 3095, "igboapi_pspk03113": 3096, "igboapi_pspk03114": 3097, "igboapi_pspk03115": 3098, "igboapi_pspk03116": 3099, "igboapi_pspk03117": 3100, "igboapi_pspk03118": 3101, "igboapi_pspk03119": 3102, "igboapi_pspk03120": 3103, "igboapi_pspk03121": 3104, "igboapi_pspk03122": 3105, "igboapi_pspk03123": 3106, "igboapi_pspk03124": 3107, "igboapi_pspk03125": 3108, "igboapi_pspk03126": 3109, "igboapi_pspk03127": 3110, "igboapi_pspk03128": 3111, "igboapi_pspk03129": 3112, "igboapi_pspk03130": 3113, "igboapi_pspk03131": 3114, "igboapi_pspk03132": 3115, "igboapi_pspk03133": 3116, "igboapi_pspk03135": 3117, "igboapi_pspk03136": 3118, "igboapi_pspk03137": 3119, "igboapi_pspk03138": 3120, "igboapi_pspk03139": 3121, "igboapi_pspk03141": 3122, "igboapi_pspk03142": 3123, "igboapi_pspk03143": 3124, "igboapi_pspk03144": 3125, "igboapi_pspk03146": 3126, "igboapi_pspk03148": 3127, "igboapi_pspk03149": 3128, "igboapi_pspk03150": 3129, "igboapi_pspk03152": 3130, "igboapi_pspk03153": 3131, "igboapi_pspk03154": 3132, "igboapi_pspk03155": 3133, "igboapi_pspk03156": 3134, "igboapi_pspk03157": 3135, "igboapi_pspk03158": 3136, "igboapi_pspk03159": 3137, "igboapi_pspk03160": 3138, "igboapi_pspk03161": 3139, "igboapi_pspk03162": 3140, "igboapi_pspk03163": 3141, "igboapi_pspk03164": 3142, "igboapi_pspk03165": 3143, "igboapi_pspk03166": 3144, "igboapi_pspk03167": 3145, "igboapi_pspk03168": 3146, "igboapi_pspk03169": 3147, "igboapi_pspk03170": 3148, "igboapi_pspk03171": 3149, "igboapi_pspk03172": 3150, "igboapi_pspk03173": 3151, "igboapi_pspk03174": 3152, "igboapi_pspk03175": 3153, "igboapi_pspk03176": 3154, "igboapi_pspk03177": 3155, "igboapi_pspk03178": 3156, "igboapi_pspk03179": 3157, "igboapi_pspk03180": 3158, "igboapi_pspk03181": 3159, "igboapi_pspk03182": 3160, "igboapi_pspk03183": 3161, "igboapi_pspk03184": 3162, "igboapi_pspk03185": 3163, "igboapi_pspk03186": 3164, "igboapi_pspk03187": 3165, "igboapi_pspk03188": 3166, "igboapi_pspk03189": 3167, "igboapi_pspk03190": 3168, "igboapi_pspk03191": 3169, "igboapi_pspk03192": 3170, "igboapi_pspk03193": 3171, "igboapi_pspk03194": 3172, "igboapi_pspk03195": 3173, "igboapi_pspk03196": 3174, "igboapi_pspk03197": 3175, "igboapi_pspk03198": 3176, "igboapi_pspk03199": 3177, "igboapi_pspk03201": 3178, "igboapi_pspk03202": 3179, "igboapi_pspk03203": 3180, "igboapi_pspk03204": 3181, "igboapi_pspk03205": 3182, "igboapi_pspk03206": 3183, "igboapi_pspk03207": 3184, "igboapi_pspk03208": 3185, "igboapi_pspk03209": 3186, "igboapi_pspk03210": 3187, "igboapi_pspk03211": 3188, "igboapi_pspk03212": 3189, "igboapi_pspk03213": 3190, "igboapi_pspk03214": 3191, "igboapi_pspk03215": 3192, "igboapi_pspk03216": 3193, "igboapi_pspk03217": 3194, "igboapi_pspk03218": 3195, "igboapi_pspk03219": 3196, "igboapi_pspk03220": 3197, "igboapi_pspk03221": 3198, "igboapi_pspk03222": 3199, "igboapi_pspk03223": 3200, "igboapi_pspk03224": 3201, "igboapi_pspk03225": 3202, "igboapi_pspk03226": 3203, "igboapi_pspk03227": 3204, "igboapi_pspk03228": 3205, "igboapi_pspk03229": 3206, "igboapi_pspk03230": 3207, "igboapi_pspk03231": 3208, "igboapi_pspk03232": 3209, "igboapi_pspk03233": 3210, "igboapi_pspk03234": 3211, "igboapi_pspk03235": 3212, "igboapi_pspk03236": 3213, "igboapi_pspk03237": 3214, "igboapi_pspk03238": 3215, "igboapi_pspk03239": 3216, "igboapi_pspk03240": 3217, "igboapi_pspk03241": 3218, "igboapi_pspk03242": 3219, "igboapi_pspk03243": 3220, "igboapi_pspk03246": 3221, "igboapi_pspk03247": 3222, "igboapi_pspk03248": 3223, "igboapi_pspk03249": 3224, "igboapi_pspk03252": 3225, "igboapi_pspk03253": 3226, "igboapi_pspk03254": 3227, "igboapi_pspk03255": 3228, "igboapi_pspk03256": 3229, "igboapi_pspk03257": 3230, "igboapi_pspk03258": 3231, "igboapi_pspk03259": 3232, "igboapi_pspk03260": 3233, "igboapi_pspk03261": 3234, "igboapi_pspk03262": 3235, "igboapi_pspk03264": 3236, "igboapi_pspk03265": 3237, "igboapi_pspk03266": 3238, "igboapi_pspk03267": 3239, "igboapi_pspk03268": 3240, "igboapi_pspk03269": 3241, "igboapi_pspk03270": 3242, "igboapi_pspk03271": 3243, "igboapi_pspk03272": 3244, "igboapi_pspk03273": 3245, "igboapi_pspk03274": 3246, "igboapi_pspk03275": 3247, "igboapi_pspk03276": 3248, "igboapi_pspk03277": 3249, "igboapi_pspk03278": 3250, "igboapi_pspk03279": 3251, "igboapi_pspk03280": 3252, "igboapi_pspk03281": 3253, "igboapi_pspk03282": 3254, "igboapi_pspk03283": 3255, "igboapi_pspk03284": 3256, "igboapi_pspk03285": 3257, "igboapi_pspk03286": 3258, "igboapi_pspk03287": 3259, "igboapi_pspk03288": 3260, "igboapi_pspk03289": 3261, "igboapi_pspk03290": 3262, "igboapi_pspk03291": 3263, "igboapi_pspk03292": 3264, "igboapi_pspk03293": 3265, "igboapi_pspk03294": 3266, "igboapi_pspk03295": 3267, "igboapi_pspk03296": 3268, "igboapi_pspk03297": 3269, "igboapi_pspk03298": 3270, "igboapi_pspk03299": 3271, "igboapi_pspk03300": 3272, "igboapi_pspk03301": 3273, "igboapi_pspk03302": 3274, "igboapi_pspk03303": 3275, "igboapi_pspk03304": 3276, "igboapi_pspk03305": 3277, "igboapi_pspk03306": 3278, "igboapi_pspk03307": 3279, "igboapi_pspk03308": 3280, "igboapi_pspk03309": 3281, "igboapi_pspk03310": 3282, "igboapi_pspk03311": 3283, "igboapi_pspk03312": 3284, "igboapi_pspk03313": 3285, "igboapi_pspk03314": 3286, "igboapi_pspk03315": 3287, "igboapi_pspk03316": 3288, "igboapi_pspk03317": 3289, "igboapi_pspk03318": 3290, "igboapi_pspk03319": 3291, "igboapi_pspk03320": 3292, "igboapi_pspk03321": 3293, "igboapi_pspk03322": 3294, "igboapi_pspk03324": 3295, "igboapi_pspk03325": 3296, "igboapi_pspk03326": 3297, "igboapi_pspk03327": 3298, "igboapi_pspk03328": 3299, "igboapi_pspk03329": 3300, "igboapi_pspk03330": 3301, "igboapi_pspk03331": 3302, "igboapi_pspk03332": 3303, "igboapi_pspk03333": 3304, "igboapi_pspk03334": 3305, "igboapi_pspk03335": 3306, "igboapi_pspk03336": 3307, "igboapi_pspk03337": 3308, "igboapi_pspk03338": 3309, "igboapi_pspk03339": 3310, "igboapi_pspk03340": 3311, "igboapi_pspk03341": 3312, "igboapi_pspk03342": 3313, "igboapi_pspk03343": 3314, "igboapi_pspk03344": 3315, "igboapi_pspk03345": 3316, "igboapi_pspk03346": 3317, "igboapi_pspk03347": 3318, "igboapi_pspk03348": 3319, "igboapi_pspk03349": 3320, "igboapi_pspk03350": 3321, "igboapi_pspk03352": 3322, "igboapi_pspk03355": 3323, "igboapi_pspk03356": 3324, "igboapi_pspk03357": 3325, "igboapi_pspk03358": 3326, "igboapi_pspk03359": 3327, "igboapi_pspk03360": 3328, "igboapi_pspk03361": 3329, "igboapi_pspk03362": 3330, "igboapi_pspk03364": 3331, "igboapi_pspk03365": 3332, "igboapi_pspk03366": 3333, "igboapi_pspk03367": 3334, "igboapi_pspk03368": 3335, "igboapi_pspk03369": 3336, "igboapi_pspk03370": 3337, "igboapi_pspk03371": 3338, "igboapi_pspk03372": 3339, "igboapi_pspk03373": 3340, "igboapi_pspk03374": 3341, "igboapi_pspk03376": 3342, "igboapi_pspk03377": 3343, "igboapi_pspk03378": 3344, "igboapi_pspk03379": 3345, "igboapi_pspk03380": 3346, "igboapi_pspk03381": 3347, "igboapi_pspk03382": 3348, "igboapi_pspk03383": 3349, "igboapi_pspk03384": 3350, "igboapi_pspk03385": 3351, "igboapi_pspk03386": 3352, "igboapi_pspk03387": 3353, "igboapi_pspk03388": 3354, "igboapi_pspk03389": 3355, "igboapi_pspk03390": 3356, "igboapi_pspk03391": 3357, "igboapi_pspk03392": 3358, "igboapi_pspk03393": 3359, "igboapi_pspk03394": 3360, "igboapi_pspk03395": 3361, "igboapi_pspk03396": 3362, "igboapi_pspk03397": 3363, "igboapi_pspk03398": 3364, "igboapi_pspk03399": 3365, "igboapi_pspk03400": 3366, "igboapi_pspk03401": 3367, "igboapi_pspk03402": 3368, "igboapi_pspk03403": 3369, "igboapi_pspk03404": 3370, "igboapi_pspk03405": 3371, "igboapi_pspk03406": 3372, "igboapi_pspk03407": 3373, "igboapi_pspk03408": 3374, "igboapi_pspk03409": 3375, "igboapi_pspk03410": 3376, "igboapi_pspk03411": 3377, "igboapi_pspk03412": 3378, "igboapi_pspk03413": 3379, "igboapi_pspk03414": 3380, "igboapi_pspk03415": 3381, "igboapi_pspk03416": 3382, "igboapi_pspk03417": 3383, "igboapi_pspk03418": 3384, "igboapi_pspk03419": 3385, "igboapi_pspk03420": 3386, "igboapi_pspk03421": 3387, "igboapi_pspk03422": 3388, "igboapi_pspk03423": 3389, "igboapi_pspk03424": 3390, "igboapi_pspk03425": 3391, "igboapi_pspk03426": 3392, "igboapi_pspk03427": 3393, "igboapi_pspk03428": 3394, "igboapi_pspk03429": 3395, "igboapi_pspk03430": 3396, "igboapi_pspk03431": 3397, "igboapi_pspk03432": 3398, "igboapi_pspk03433": 3399, "igboapi_pspk03434": 3400, "igboapi_pspk03435": 3401, "igboapi_pspk03436": 3402, "igboapi_pspk03437": 3403, "igboapi_pspk03438": 3404, "igboapi_pspk03439": 3405, "igboapi_pspk03440": 3406, "igboapi_pspk03441": 3407, "igboapi_pspk03442": 3408, "igboapi_pspk03443": 3409, "igboapi_pspk03444": 3410, "igboapi_pspk03445": 3411, "igboapi_pspk03446": 3412, "igboapi_pspk03447": 3413, "igboapi_pspk03448": 3414, "igboapi_pspk03449": 3415, "igboapi_pspk03450": 3416, "igboapi_pspk03451": 3417, "igboapi_pspk03452": 3418, "igboapi_pspk03453": 3419, "igboapi_pspk03454": 3420, "igboapi_pspk03455": 3421, "igboapi_pspk03456": 3422, "igboapi_pspk03457": 3423, "igboapi_pspk03458": 3424, "igboapi_pspk03459": 3425, "igboapi_pspk03460": 3426, "igboapi_pspk03461": 3427, "igboapi_pspk03462": 3428, "igboapi_pspk03463": 3429, "igboapi_pspk03464": 3430, "igboapi_pspk03465": 3431, "igboapi_pspk03466": 3432, "igboapi_pspk03467": 3433, "igboapi_pspk03468": 3434, "igboapi_pspk03469": 3435, "igboapi_pspk03470": 3436, "igboapi_pspk03471": 3437, "igboapi_pspk03472": 3438, "igboapi_pspk03473": 3439, "igboapi_pspk03474": 3440, "igboapi_pspk03475": 3441, "igboapi_pspk03476": 3442, "igboapi_pspk03477": 3443, "igboapi_pspk03478": 3444, "igboapi_pspk03479": 3445, "igboapi_pspk03480": 3446, "igboapi_pspk03481": 3447, "igboapi_pspk03482": 3448, "igboapi_pspk03483": 3449, "igboapi_pspk03484": 3450, "igboapi_pspk03486": 3451, "igboapi_pspk03487": 3452, "igboapi_pspk03488": 3453, "igboapi_pspk03489": 3454, "igboapi_pspk03490": 3455, "igboapi_pspk03491": 3456, "igboapi_pspk03492": 3457, "igboapi_pspk03493": 3458, "igboapi_pspk03494": 3459, "igboapi_pspk03495": 3460, "igboapi_pspk03496": 3461, "igboapi_pspk03497": 3462, "igboapi_pspk03498": 3463, "igboapi_pspk03499": 3464, "igboapi_pspk03501": 3465, "igboapi_pspk03503": 3466, "igboapi_pspk03504": 3467, "igboapi_pspk03505": 3468, "igboapi_pspk03507": 3469, "igboapi_pspk03508": 3470, "igboapi_pspk03509": 3471, "igboapi_pspk03510": 3472, "igboapi_pspk03511": 3473, "igboapi_pspk03512": 3474, "igboapi_pspk03513": 3475, "igboapi_pspk03514": 3476, "igboapi_pspk03516": 3477, "igboapi_pspk03518": 3478, "igboapi_pspk03519": 3479, "igboapi_pspk03520": 3480, "igboapi_pspk03521": 3481, "igboapi_pspk03522": 3482, "igboapi_pspk03523": 3483, "igboapi_pspk03524": 3484, "igboapi_pspk03525": 3485, "igboapi_pspk03526": 3486, "igboapi_pspk03527": 3487, "igboapi_pspk03528": 3488, "igboapi_pspk03529": 3489, "igboapi_pspk03530": 3490, "igboapi_pspk03531": 3491, "igboapi_pspk03532": 3492, "igboapi_pspk03533": 3493, "igboapi_pspk03534": 3494, "igboapi_pspk03535": 3495, "igboapi_pspk03536": 3496, "igboapi_pspk03537": 3497, "igboapi_pspk03538": 3498, "igboapi_pspk03539": 3499, "igboapi_pspk03540": 3500, "igboapi_pspk03541": 3501, "igboapi_pspk03542": 3502, "igboapi_pspk03543": 3503, "igboapi_pspk03544": 3504, "igboapi_pspk03545": 3505, "igboapi_pspk03546": 3506, "igboapi_pspk03547": 3507, "igboapi_pspk03548": 3508, "igboapi_pspk03549": 3509, "igboapi_pspk03550": 3510, "igboapi_pspk03551": 3511, "igboapi_pspk03552": 3512, "igboapi_pspk03553": 3513, "igboapi_pspk03554": 3514, "igboapi_pspk03555": 3515, "igboapi_pspk03556": 3516, "igboapi_pspk03557": 3517, "igboapi_pspk03558": 3518, "igboapi_pspk03559": 3519, "igboapi_pspk03560": 3520, "igboapi_pspk03561": 3521, "igboapi_pspk03562": 3522, "igboapi_pspk03563": 3523, "igboapi_pspk03564": 3524, "igboapi_pspk03565": 3525, "igboapi_pspk03566": 3526, "igboapi_pspk03567": 3527, "igboapi_pspk03568": 3528, "igboapi_pspk03570": 3529, "igboapi_pspk03571": 3530, "igboapi_pspk03572": 3531, "igboapi_pspk03573": 3532, "igboapi_pspk03574": 3533, "igboapi_pspk03575": 3534, "igboapi_pspk03576": 3535, "igboapi_pspk03577": 3536, "igboapi_pspk03578": 3537, "igboapi_pspk03579": 3538, "igboapi_pspk03580": 3539, "igboapi_pspk03581": 3540, "igboapi_pspk03582": 3541, "igboapi_pspk03583": 3542, "igboapi_pspk03584": 3543, "igboapi_pspk03585": 3544, "igboapi_pspk03586": 3545, "igboapi_pspk03587": 3546, "igboapi_pspk03588": 3547, "igboapi_pspk03589": 3548, "igboapi_pspk03590": 3549, "igboapi_pspk03591": 3550, "igboapi_pspk03592": 3551, "igboapi_pspk03593": 3552, "igboapi_pspk03594": 3553, "igboapi_pspk03595": 3554, "igboapi_pspk03596": 3555, "igboapi_pspk03597": 3556, "igboapi_pspk03598": 3557, "igboapi_pspk03599": 3558, "igboapi_pspk03600": 3559, "igboapi_pspk03601": 3560, "igboapi_pspk03602": 3561, "igboapi_pspk03603": 3562, "igboapi_pspk03604": 3563, "igboapi_pspk03605": 3564, "igboapi_pspk03607": 3565, "igboapi_pspk03608": 3566, "igboapi_pspk03609": 3567, "igboapi_pspk03610": 3568, "igboapi_pspk03611": 3569, "igboapi_pspk03612": 3570, "igboapi_pspk03613": 3571, "igboapi_pspk03614": 3572, "igboapi_pspk03615": 3573, "igboapi_pspk03616": 3574, "igboapi_pspk03617": 3575, "igboapi_pspk03618": 3576, "igboapi_pspk03619": 3577, "igboapi_pspk03620": 3578, "igboapi_pspk03621": 3579, "igboapi_pspk03622": 3580, "igboapi_pspk03623": 3581, "igboapi_pspk03624": 3582, "igboapi_pspk03625": 3583, "igboapi_pspk03626": 3584, "igboapi_pspk03627": 3585, "igboapi_pspk03628": 3586, "igboapi_pspk03629": 3587, "igboapi_pspk03630": 3588, "igboapi_pspk03631": 3589, "igboapi_pspk03632": 3590, "igboapi_pspk03633": 3591, "igboapi_pspk03634": 3592, "igboapi_pspk03635": 3593, "igboapi_pspk03636": 3594, "igboapi_pspk03637": 3595, "igboapi_pspk03638": 3596, "igboapi_pspk03639": 3597, "igboapi_pspk03640": 3598, "igboapi_pspk03641": 3599, "igboapi_pspk03642": 3600, "igboapi_pspk03643": 3601, "igboapi_pspk03644": 3602, "igboapi_pspk03645": 3603, "igboapi_pspk03646": 3604, "igboapi_pspk03647": 3605, "igboapi_pspk03648": 3606, "igboapi_pspk03649": 3607, "igboapi_pspk03650": 3608, "igboapi_pspk03651": 3609, "igboapi_pspk03652": 3610, "igboapi_pspk03653": 3611, "igboapi_pspk03654": 3612, "igboapi_pspk03655": 3613, "igboapi_pspk03656": 3614, "igboapi_pspk03657": 3615, "igboapi_pspk03658": 3616, "igboapi_pspk03659": 3617, "igboapi_pspk03660": 3618, "igboapi_pspk03661": 3619, "igboapi_pspk03662": 3620, "igboapi_pspk03663": 3621, "igboapi_pspk03664": 3622, "igboapi_pspk03665": 3623, "igboapi_pspk03667": 3624, "igboapi_pspk03669": 3625, "igboapi_pspk03670": 3626, "igboapi_pspk03671": 3627, "igboapi_pspk03672": 3628, "igboapi_pspk03673": 3629, "igboapi_pspk03674": 3630, "igboapi_pspk03675": 3631, "igboapi_pspk03676": 3632, "igboapi_pspk03677": 3633, "igboapi_pspk03678": 3634, "igboapi_pspk03679": 3635, "igboapi_pspk03680": 3636, "igboapi_pspk03681": 3637, "igboapi_pspk03682": 3638, "igboapi_pspk03683": 3639, "igboapi_pspk03684": 3640, "igboapi_pspk03685": 3641, "igboapi_pspk03686": 3642, "igboapi_pspk03687": 3643, "igboapi_pspk03688": 3644, "igboapi_pspk03689": 3645, "igboapi_pspk03690": 3646, "igboapi_pspk03691": 3647, "igboapi_pspk03692": 3648, "igboapi_pspk03693": 3649, "igboapi_pspk03694": 3650, "igboapi_pspk03695": 3651, "igboapi_pspk03696": 3652, "igboapi_pspk03697": 3653, "igboapi_pspk03698": 3654, "igboapi_pspk03699": 3655, "igboapi_pspk03700": 3656, "igboapi_pspk03701": 3657, "igboapi_pspk03702": 3658, "igboapi_pspk03703": 3659, "igboapi_pspk03704": 3660, "igboapi_pspk03706": 3661, "igboapi_pspk03707": 3662, "igboapi_pspk03708": 3663, "igboapi_pspk03709": 3664, "igboapi_pspk03710": 3665, "igboapi_pspk03711": 3666, "igboapi_pspk03713": 3667, "igboapi_pspk03714": 3668, "igboapi_pspk03715": 3669, "igboapi_pspk03716": 3670, "igboapi_pspk03717": 3671, "igboapi_pspk03718": 3672, "igboapi_pspk03719": 3673, "igboapi_pspk03720": 3674, "igboapi_pspk03721": 3675, "igboapi_pspk03722": 3676, "igboapi_pspk03723": 3677, "igboapi_pspk03724": 3678, "igboapi_pspk03725": 3679, "igboapi_pspk03726": 3680, "igboapi_pspk03727": 3681, "igboapi_pspk03728": 3682, "igboapi_pspk03729": 3683, "igboapi_pspk03730": 3684, "igboapi_pspk03732": 3685, "igboapi_pspk03733": 3686, "igboapi_pspk03734": 3687, "igboapi_pspk03735": 3688, "igboapi_pspk03736": 3689, "igboapi_pspk03737": 3690, "igboapi_pspk03738": 3691, "igboapi_pspk03739": 3692, "igboapi_pspk03740": 3693, "igboapi_pspk03741": 3694, "igboapi_pspk03742": 3695, "igboapi_pspk03743": 3696, "igboapi_pspk03744": 3697, "igboapi_pspk03745": 3698, "igboapi_pspk03747": 3699, "igboapi_pspk03748": 3700, "igboapi_pspk03749": 3701, "igboapi_pspk03750": 3702, "igboapi_pspk03751": 3703, "igboapi_pspk03752": 3704, "igboapi_pspk03753": 3705, "igboapi_pspk03754": 3706, "igboapi_pspk03755": 3707, "igboapi_pspk03756": 3708, "igboapi_pspk03757": 3709, "igboapi_pspk03758": 3710, "igboapi_pspk03759": 3711, "igboapi_pspk03760": 3712, "igboapi_pspk03761": 3713, "igboapi_pspk03762": 3714, "igboapi_pspk03763": 3715, "igboapi_pspk03764": 3716, "igboapi_pspk03765": 3717, "igboapi_pspk03766": 3718, "igboapi_pspk03767": 3719, "igboapi_pspk03768": 3720, "igboapi_pspk03769": 3721, "igboapi_pspk03770": 3722, "igboapi_pspk03771": 3723, "igboapi_pspk03772": 3724, "igboapi_pspk03773": 3725, "igboapi_pspk03774": 3726, "igboapi_pspk03775": 3727, "igboapi_pspk03776": 3728, "igboapi_pspk03777": 3729, "igboapi_pspk03778": 3730, "igboapi_pspk03779": 3731, "igboapi_pspk03780": 3732, "igboapi_pspk03781": 3733, "igboapi_pspk03782": 3734, "igboapi_pspk03783": 3735, "igboapi_pspk03784": 3736, "igboapi_pspk03785": 3737, "igboapi_pspk03786": 3738, "igboapi_pspk03788": 3739, "igboapi_pspk03789": 3740, "igboapi_pspk03791": 3741, "igboapi_pspk03793": 3742, "igboapi_pspk03794": 3743, "igboapi_pspk03795": 3744, "igboapi_pspk03797": 3745, "igboapi_pspk03798": 3746, "igboapi_pspk03799": 3747, "igboapi_pspk03800": 3748, "igboapi_pspk03801": 3749, "igboapi_pspk03802": 3750, "igboapi_pspk03803": 3751, "igboapi_pspk03804": 3752, "igboapi_pspk03806": 3753, "igboapi_pspk03807": 3754, "igboapi_pspk03808": 3755, "igboapi_pspk03809": 3756, "igboapi_pspk03810": 3757, "igboapi_pspk03811": 3758, "igboapi_pspk03812": 3759, "igboapi_pspk03813": 3760, "igboapi_pspk03814": 3761, "igboapi_pspk03815": 3762, "igboapi_pspk03816": 3763, "igboapi_pspk03817": 3764, "igboapi_pspk03818": 3765, "igboapi_pspk03819": 3766, "igboapi_pspk03820": 3767, "igboapi_pspk03821": 3768, "igboapi_pspk03822": 3769, "igboapi_pspk03823": 3770, "igboapi_pspk03824": 3771, "igboapi_pspk03825": 3772, "igboapi_pspk03826": 3773, "igboapi_pspk03827": 3774, "igboapi_pspk03828": 3775, "igboapi_pspk03829": 3776, "igboapi_pspk03830": 3777, "igboapi_pspk03831": 3778, "igboapi_pspk03832": 3779, "igboapi_pspk03833": 3780, "igboapi_pspk03835": 3781, "igboapi_pspk03836": 3782, "igboapi_pspk03837": 3783, "igboapi_pspk03838": 3784, "igboapi_pspk03839": 3785, "igboapi_pspk03840": 3786, "igboapi_pspk03841": 3787, "igboapi_pspk03842": 3788, "igboapi_pspk03843": 3789, "igboapi_pspk03844": 3790, "igboapi_pspk03845": 3791, "igboapi_pspk03846": 3792, "igboapi_pspk03847": 3793, "igboapi_pspk03848": 3794, "igboapi_pspk03849": 3795, "igboapi_pspk03851": 3796, "igboapi_pspk03852": 3797, "igboapi_pspk03853": 3798, "igboapi_pspk03854": 3799, "igboapi_pspk03855": 3800, "igboapi_pspk03856": 3801, "igboapi_pspk03857": 3802, "igboapi_pspk03858": 3803, "igboapi_pspk03859": 3804, "igboapi_pspk03860": 3805, "igboapi_pspk03861": 3806, "igboapi_pspk03862": 3807, "igboapi_pspk03863": 3808, "igboapi_pspk03864": 3809, "igboapi_pspk03865": 3810, "igboapi_pspk03866": 3811, "igboapi_pspk03867": 3812, "igboapi_pspk03868": 3813, "igboapi_pspk03869": 3814, "igboapi_pspk03870": 3815, "igboapi_pspk03871": 3816, "igboapi_pspk03872": 3817, "igboapi_pspk03873": 3818, "igboapi_pspk03874": 3819, "igboapi_pspk03875": 3820, "igboapi_pspk03876": 3821, "igboapi_pspk03877": 3822, "igboapi_pspk03878": 3823, "igboapi_pspk03879": 3824, "igboapi_pspk03880": 3825, "igboapi_pspk03881": 3826, "igboapi_pspk03882": 3827, "igboapi_pspk03883": 3828, "igboapi_pspk03884": 3829, "igboapi_pspk03885": 3830, "igboapi_pspk03886": 3831, "igboapi_pspk03887": 3832, "igboapi_pspk03888": 3833, "igboapi_pspk03889": 3834, "igboapi_pspk03890": 3835, "igboapi_pspk03891": 3836, "igboapi_pspk03892": 3837, "igboapi_pspk03893": 3838, "igboapi_pspk03894": 3839, "igboapi_pspk03895": 3840, "igboapi_pspk03896": 3841, "igboapi_pspk03897": 3842, "igboapi_pspk03898": 3843, "igboapi_pspk03899": 3844, "igboapi_pspk03900": 3845, "igboapi_pspk03901": 3846, "igboapi_pspk03902": 3847, "igboapi_pspk03903": 3848, "igboapi_pspk03904": 3849, "igboapi_pspk03905": 3850, "igboapi_pspk03906": 3851, "igboapi_pspk03907": 3852, "igboapi_pspk03908": 3853, "igboapi_pspk03909": 3854, "igboapi_pspk03912": 3855, "igboapi_pspk03913": 3856, "igboapi_pspk03916": 3857, "igboapi_pspk03917": 3858, "igboapi_pspk03918": 3859, "igboapi_pspk03919": 3860, "igboapi_pspk03920": 3861, "igboapi_pspk03922": 3862, "igboapi_pspk03923": 3863, "igboapi_pspk03924": 3864, "igboapi_pspk03925": 3865, "igboapi_pspk03926": 3866, "igboapi_pspk03927": 3867, "igboapi_pspk03928": 3868, "igboapi_pspk03929": 3869, "igboapi_pspk03930": 3870, "igboapi_pspk03931": 3871, "igboapi_pspk03932": 3872, "igboapi_pspk03933": 3873, "igboapi_pspk03934": 3874, "igboapi_pspk03935": 3875, "igboapi_pspk03936": 3876, "igboapi_pspk03937": 3877, "igboapi_pspk03938": 3878, "igboapi_pspk03939": 3879, "igboapi_pspk03940": 3880, "igboapi_pspk03941": 3881, "igboapi_pspk03942": 3882, "igboapi_pspk03943": 3883, "igboapi_pspk03944": 3884, "igboapi_pspk03945": 3885, "igboapi_pspk03946": 3886, "igboapi_pspk03947": 3887, "igboapi_pspk03948": 3888, "igboapi_pspk03949": 3889, "igboapi_pspk03950": 3890, "igboapi_pspk03951": 3891, "igboapi_pspk03952": 3892, "igboapi_pspk03953": 3893, "igboapi_pspk03954": 3894, "igboapi_pspk03955": 3895, "igboapi_pspk03956": 3896, "igboapi_pspk03957": 3897, "igboapi_pspk03958": 3898, "igboapi_pspk03959": 3899, "igboapi_pspk03960": 3900, "igboapi_pspk03961": 3901, "igboapi_pspk03963": 3902, "igboapi_pspk03964": 3903, "igboapi_pspk03966": 3904, "igboapi_pspk03968": 3905, "igboapi_pspk03969": 3906, "igboapi_pspk03970": 3907, "igboapi_pspk03971": 3908, "igboapi_pspk03972": 3909, "igboapi_pspk03973": 3910, "igboapi_pspk03974": 3911, "igboapi_pspk03975": 3912, "igboapi_pspk03976": 3913, "igboapi_pspk03977": 3914, "igboapi_pspk03978": 3915, "igboapi_pspk03979": 3916, "igboapi_pspk03980": 3917, "igboapi_pspk03981": 3918, "igboapi_pspk03982": 3919, "igboapi_pspk03983": 3920, "igboapi_pspk03984": 3921, "igboapi_pspk03985": 3922, "igboapi_pspk03986": 3923, "igboapi_pspk03987": 3924, "igboapi_pspk03988": 3925, "igboapi_pspk03989": 3926, "igboapi_pspk03990": 3927, "igboapi_pspk03991": 3928, "igboapi_pspk03992": 3929, "igboapi_pspk03994": 3930, "igboapi_pspk03995": 3931, "igboapi_pspk03996": 3932, "igboapi_pspk03997": 3933, "igboapi_pspk03998": 3934, "igboapi_pspk03999": 3935, "igboapi_pspk04000": 3936, "igboapi_pspk04001": 3937, "igboapi_pspk04002": 3938, "igboapi_pspk04003": 3939, "igboapi_pspk04004": 3940, "igboapi_pspk04005": 3941, "igboapi_pspk04006": 3942, "igboapi_pspk04007": 3943, "igboapi_pspk04008": 3944, "igboapi_pspk04009": 3945, "igboapi_pspk04010": 3946, "igboapi_pspk04011": 3947, "igboapi_pspk04013": 3948, "igboapi_pspk04015": 3949, "igboapi_pspk04016": 3950, "igboapi_pspk04017": 3951, "igboapi_pspk04018": 3952, "igboapi_pspk04019": 3953, "igboapi_pspk04020": 3954, "igboapi_pspk04021": 3955, "igboapi_pspk04022": 3956, "igboapi_pspk04023": 3957, "igboapi_pspk04024": 3958, "igboapi_pspk04025": 3959, "igboapi_pspk04026": 3960, "igboapi_pspk04027": 3961, "igboapi_pspk04028": 3962, "igboapi_pspk04029": 3963, "igboapi_pspk04030": 3964, "igboapi_pspk04031": 3965, "igboapi_pspk04032": 3966, "igboapi_pspk04033": 3967, "igboapi_pspk04034": 3968, "igboapi_pspk04035": 3969, "igboapi_pspk04036": 3970, "igboapi_pspk04037": 3971, "igboapi_pspk04038": 3972, "igboapi_pspk04039": 3973, "igboapi_pspk04040": 3974, "igboapi_pspk04041": 3975, "igboapi_pspk04042": 3976, "igboapi_pspk04043": 3977, "igboapi_pspk04044": 3978, "igboapi_pspk04045": 3979, "igboapi_pspk04046": 3980, "igboapi_pspk04047": 3981, "igboapi_pspk04048": 3982, "igboapi_pspk04049": 3983, "igboapi_pspk04050": 3984, "igboapi_pspk04051": 3985, "igboapi_pspk04052": 3986, "igboapi_pspk04053": 3987, "igboapi_pspk04054": 3988, "igboapi_pspk04055": 3989, "igboapi_pspk04056": 3990, "igboapi_pspk04057": 3991, "igboapi_pspk04058": 3992, "igboapi_pspk04059": 3993, "igboapi_pspk04060": 3994, "igboapi_pspk04061": 3995, "igboapi_pspk04062": 3996, "igboapi_pspk04063": 3997, "igboapi_pspk04064": 3998, "igboapi_pspk04066": 3999, "igboapi_pspk04067": 4000, "igboapi_pspk04068": 4001, "igboapi_pspk04070": 4002, "igboapi_pspk04071": 4003, "igboapi_pspk04073": 4004, "igboapi_pspk04075": 4005, "igboapi_pspk04076": 4006, "igboapi_pspk04078": 4007, "igboapi_pspk04079": 4008, "igboapi_pspk04080": 4009, "igboapi_pspk04081": 4010, "igboapi_pspk04082": 4011, "igboapi_pspk04083": 4012, "igboapi_pspk04084": 4013, "igboapi_pspk04085": 4014, "igboapi_pspk04086": 4015, "igboapi_pspk04087": 4016, "igboapi_pspk04088": 4017, "igboapi_pspk04089": 4018, "igboapi_pspk04090": 4019, "igboapi_pspk04091": 4020, "igboapi_pspk04092": 4021, "igboapi_pspk04093": 4022, "igboapi_pspk04094": 4023, "igboapi_pspk04095": 4024, "igboapi_pspk04096": 4025, "igboapi_pspk04097": 4026, "igboapi_pspk04098": 4027, "igboapi_pspk04099": 4028, "igboapi_pspk04100": 4029, "igboapi_pspk04101": 4030, "igboapi_pspk04102": 4031, "igboapi_pspk04103": 4032, "igboapi_pspk04104": 4033, "igboapi_pspk04105": 4034, "igboapi_pspk04106": 4035, "igboapi_pspk04107": 4036, "igboapi_pspk04108": 4037, "igboapi_pspk04109": 4038, "igboapi_pspk04110": 4039, "igboapi_pspk04111": 4040, "igboapi_pspk04112": 4041, "igboapi_pspk04113": 4042, "igboapi_pspk04114": 4043, "igboapi_pspk04115": 4044, "igboapi_pspk04116": 4045, "igboapi_pspk04117": 4046, "igboapi_pspk04118": 4047, "igboapi_pspk04119": 4048, "igboapi_pspk04121": 4049, "igboapi_pspk04122": 4050, "igboapi_pspk04123": 4051, "igboapi_pspk04124": 4052, "igboapi_pspk04125": 4053, "igboapi_pspk04126": 4054, "igboapi_pspk04127": 4055, "igboapi_pspk04128": 4056, "igboapi_pspk04129": 4057, "igboapi_pspk04130": 4058, "igboapi_pspk04131": 4059, "igboapi_pspk04132": 4060, "igboapi_pspk04133": 4061, "igboapi_pspk04134": 4062, "igboapi_pspk04135": 4063, "igboapi_pspk04136": 4064, "igboapi_pspk04137": 4065, "igboapi_pspk04138": 4066, "igboapi_pspk04139": 4067, "igboapi_pspk04141": 4068, "igboapi_pspk04142": 4069, "igboapi_pspk04143": 4070, "igboapi_pspk04144": 4071, "igboapi_pspk04145": 4072, "igboapi_pspk04146": 4073, "igboapi_pspk04148": 4074, "igboapi_pspk04149": 4075, "igboapi_pspk04150": 4076, "igboapi_pspk04151": 4077, "igboapi_pspk04152": 4078, "igboapi_pspk04153": 4079, "igboapi_pspk04154": 4080, "igboapi_pspk04157": 4081, "igboapi_pspk04158": 4082, "igboapi_pspk04160": 4083, "igboapi_pspk04161": 4084, "igboapi_pspk04162": 4085, "igboapi_pspk04163": 4086, "igboapi_pspk04164": 4087, "igboapi_pspk04165": 4088, "igboapi_pspk04166": 4089, "igboapi_pspk04167": 4090, "igboapi_pspk04168": 4091, "igboapi_pspk04169": 4092, "igboapi_pspk04170": 4093, "igboapi_pspk04171": 4094, "igboapi_pspk04172": 4095, "igboapi_pspk04173": 4096, "igboapi_pspk04174": 4097, "igboapi_pspk04175": 4098, "igboapi_pspk04176": 4099, "igboapi_pspk04177": 4100, "igboapi_pspk04178": 4101, "igboapi_pspk04179": 4102, "igboapi_pspk04180": 4103, "igboapi_pspk04181": 4104, "igboapi_pspk04182": 4105, "igboapi_pspk04183": 4106, "igboapi_pspk04184": 4107, "igboapi_pspk04185": 4108, "igboapi_pspk04186": 4109, "igboapi_pspk04187": 4110, "igboapi_pspk04188": 4111, "igboapi_pspk04189": 4112, "igboapi_pspk04190": 4113, "igboapi_pspk04192": 4114, "igboapi_pspk04193": 4115, "igboapi_pspk04194": 4116, "igboapi_pspk04196": 4117, "igboapi_pspk04199": 4118, "igboapi_pspk04201": 4119, "igboapi_pspk04202": 4120, "igboapi_pspk04203": 4121, "igboapi_pspk04204": 4122, "igboapi_pspk04205": 4123, "igboapi_pspk04206": 4124, "igboapi_pspk04207": 4125, "igboapi_pspk04208": 4126, "igboapi_pspk04209": 4127, "igboapi_pspk04210": 4128, "igboapi_pspk04211": 4129, "igboapi_pspk04212": 4130, "igboapi_pspk04213": 4131, "igboapi_pspk04214": 4132, "igboapi_pspk04215": 4133, "igboapi_pspk04216": 4134, "igboapi_pspk04217": 4135, "igboapi_pspk04218": 4136, "igboapi_pspk04219": 4137, "igboapi_pspk04220": 4138, "igboapi_pspk04221": 4139, "igboapi_pspk04222": 4140, "igboapi_pspk04223": 4141, "igboapi_pspk04224": 4142, "igboapi_pspk04225": 4143, "igboapi_pspk04226": 4144, "igboapi_pspk04227": 4145, "igboapi_pspk04228": 4146, "igboapi_pspk04229": 4147, "igboapi_pspk04230": 4148, "igboapi_pspk04231": 4149, "igboapi_pspk04232": 4150, "igboapi_pspk04233": 4151, "igboapi_pspk04234": 4152, "igboapi_pspk04235": 4153, "igboapi_pspk04236": 4154, "igboapi_pspk04237": 4155, "igboapi_pspk04238": 4156, "igboapi_pspk04239": 4157, "igboapi_pspk04240": 4158, "igboapi_pspk04241": 4159, "igboapi_pspk04242": 4160, "igboapi_pspk04243": 4161, "igboapi_pspk04244": 4162, "igboapi_pspk04245": 4163, "igboapi_pspk04246": 4164, "igboapi_pspk04247": 4165, "igboapi_pspk04248": 4166, "igboapi_pspk04249": 4167, "igboapi_pspk04250": 4168, "igboapi_pspk04251": 4169, "igboapi_pspk04252": 4170, "igboapi_pspk04253": 4171, "igboapi_pspk04254": 4172, "igboapi_pspk04255": 4173, "igboapi_pspk04256": 4174, "igboapi_pspk04257": 4175, "igboapi_pspk04258": 4176, "igboapi_pspk04260": 4177, "igboapi_pspk04261": 4178, "igboapi_pspk04263": 4179, "igboapi_pspk04265": 4180, "igboapi_pspk04266": 4181, "igboapi_pspk04267": 4182, "igboapi_pspk04268": 4183, "igboapi_pspk04270": 4184, "igboapi_pspk04272": 4185, "igboapi_pspk04273": 4186, "igboapi_pspk04274": 4187, "igboapi_pspk04275": 4188, "igboapi_pspk04277": 4189, "igboapi_pspk04278": 4190, "igboapi_pspk04279": 4191, "igboapi_pspk04280": 4192, "igboapi_pspk04281": 4193, "igboapi_pspk04282": 4194, "igboapi_pspk04283": 4195, "igboapi_pspk04284": 4196, "igboapi_pspk04285": 4197, "igboapi_pspk04286": 4198, "igboapi_pspk04287": 4199, "igboapi_pspk04288": 4200, "igboapi_pspk04289": 4201, "igboapi_pspk04290": 4202, "igboapi_pspk04291": 4203, "igboapi_pspk04292": 4204, "igboapi_pspk04293": 4205, "igboapi_pspk04294": 4206, "igboapi_pspk04295": 4207, "igboapi_pspk04296": 4208, "igboapi_pspk04297": 4209, "igboapi_pspk04298": 4210, "igboapi_pspk04299": 4211, "igboapi_pspk04302": 4212, "igboapi_pspk04303": 4213, "igboapi_pspk04304": 4214, "igboapi_pspk04307": 4215, "igboapi_pspk04308": 4216, "igboapi_pspk04309": 4217, "igboapi_pspk04311": 4218, "igboapi_pspk04312": 4219, "igboapi_pspk04313": 4220, "igboapi_pspk04318": 4221, "igboapi_pspk04319": 4222, "igboapi_pspk04320": 4223, "igboapi_pspk04321": 4224, "igboapi_pspk04322": 4225, "igboapi_pspk04323": 4226, "igboapi_pspk04324": 4227, "igboapi_pspk04325": 4228, "igboapi_pspk04326": 4229, "igboapi_pspk04327": 4230, "igboapi_pspk04328": 4231, "igboapi_pspk04329": 4232, "igboapi_pspk04330": 4233, "igboapi_pspk04331": 4234, "igboapi_pspk04332": 4235, "igboapi_pspk04333": 4236, "igboapi_pspk04334": 4237, "igboapi_pspk04335": 4238, "igboapi_pspk04336": 4239, "igboapi_pspk04337": 4240, "igboapi_pspk04338": 4241, "igboapi_pspk04339": 4242, "igboapi_pspk04340": 4243, "igboapi_pspk04341": 4244, "igboapi_pspk04342": 4245, "igboapi_pspk04343": 4246, "igboapi_pspk04344": 4247, "igboapi_pspk04345": 4248, "igboapi_pspk04346": 4249, "igboapi_pspk04347": 4250, "igboapi_pspk04348": 4251, "igboapi_pspk04349": 4252, "igboapi_pspk04351": 4253, "igboapi_pspk04352": 4254, "igboapi_pspk04353": 4255, "igboapi_pspk04354": 4256, "igboapi_pspk04355": 4257, "igboapi_pspk04356": 4258, "igboapi_pspk04357": 4259, "igboapi_pspk04358": 4260, "igboapi_pspk04359": 4261, "igboapi_pspk04360": 4262, "igboapi_pspk04361": 4263, "igboapi_pspk04362": 4264, "igboapi_pspk04363": 4265, "igboapi_pspk04364": 4266, "igboapi_pspk04365": 4267, "igboapi_pspk04366": 4268, "igboapi_pspk04367": 4269, "igboapi_pspk04368": 4270, "igboapi_pspk04369": 4271, "igboapi_pspk04370": 4272, "igboapi_pspk04371": 4273, "igboapi_pspk04372": 4274, "igboapi_pspk04373": 4275, "igboapi_pspk04374": 4276, "igboapi_pspk04375": 4277, "igboapi_pspk04376": 4278, "igboapi_pspk04377": 4279, "igboapi_pspk04378": 4280, "igboapi_pspk04379": 4281, "igboapi_pspk04380": 4282, "igboapi_pspk04381": 4283, "igboapi_pspk04382": 4284, "igboapi_pspk04383": 4285, "igboapi_pspk04384": 4286, "igboapi_pspk04385": 4287, "igboapi_pspk04386": 4288, "igboapi_pspk04387": 4289, "igboapi_pspk04388": 4290, "igboapi_pspk04389": 4291, "igboapi_pspk04390": 4292, "igboapi_pspk04391": 4293, "igboapi_pspk04392": 4294, "igboapi_pspk04393": 4295, "igboapi_pspk04394": 4296, "igboapi_pspk04395": 4297, "igboapi_pspk04396": 4298, "igboapi_pspk04397": 4299, "igboapi_pspk04398": 4300, "igboapi_pspk04399": 4301, "igboapi_pspk04400": 4302, "igboapi_pspk04401": 4303, "igboapi_pspk04402": 4304, "igboapi_pspk04403": 4305, "igboapi_pspk04405": 4306, "igboapi_pspk04406": 4307, "igboapi_pspk04407": 4308, "igboapi_pspk04408": 4309, "igboapi_pspk04409": 4310, "igboapi_pspk04410": 4311, "igboapi_pspk04411": 4312, "igboapi_pspk04412": 4313, "igboapi_pspk04413": 4314, "igboapi_pspk04414": 4315, "igboapi_pspk04415": 4316, "igboapi_pspk04416": 4317, "igboapi_pspk04417": 4318, "igboapi_pspk04418": 4319, "igboapi_pspk04419": 4320, "igboapi_pspk04420": 4321, "igboapi_pspk04421": 4322, "igboapi_pspk04422": 4323, "igboapi_pspk04423": 4324, "igboapi_pspk04424": 4325, "igboapi_pspk04426": 4326, "igboapi_pspk04427": 4327, "igboapi_pspk04428": 4328, "igboapi_pspk04429": 4329, "igboapi_pspk04431": 4330, "igboapi_pspk04432": 4331, "igboapi_pspk04433": 4332, "igboapi_pspk04434": 4333, "igboapi_pspk04435": 4334, "igboapi_pspk04436": 4335, "igboapi_pspk04437": 4336, "igboapi_pspk04438": 4337, "igboapi_pspk04439": 4338, "igboapi_pspk04440": 4339, "igboapi_pspk04441": 4340, "igboapi_pspk04442": 4341, "igboapi_pspk04443": 4342, "igboapi_pspk04444": 4343, "igboapi_pspk04445": 4344, "igboapi_pspk04446": 4345, "igboapi_pspk04447": 4346, "igboapi_pspk04448": 4347, "igboapi_pspk04450": 4348, "igboapi_pspk04451": 4349, "igboapi_pspk04454": 4350, "igboapi_pspk04456": 4351, "igboapi_pspk04457": 4352, "igboapi_pspk04458": 4353, "igboapi_pspk04459": 4354, "igboapi_pspk04460": 4355, "igboapi_pspk04461": 4356, "igboapi_pspk04462": 4357, "igboapi_pspk04463": 4358, "igboapi_pspk04464": 4359, "igboapi_pspk04465": 4360, "igboapi_pspk04466": 4361, "igboapi_pspk04467": 4362, "igboapi_pspk04468": 4363, "igboapi_pspk04469": 4364, "igboapi_pspk04470": 4365, "igboapi_pspk04473": 4366, "igboapi_pspk04474": 4367, "igboapi_pspk04475": 4368, "igboapi_pspk04476": 4369, "igboapi_pspk04477": 4370, "igboapi_pspk04478": 4371, "igboapi_pspk04479": 4372, "igboapi_pspk04480": 4373, "igboapi_pspk04481": 4374, "igboapi_pspk04482": 4375, "igboapi_pspk04483": 4376, "igboapi_pspk04484": 4377, "igboapi_pspk04485": 4378, "igboapi_pspk04486": 4379, "igboapi_pspk04487": 4380, "igboapi_pspk04488": 4381, "igboapi_pspk04489": 4382, "igboapi_pspk04491": 4383, "igboapi_pspk04492": 4384, "igboapi_pspk04493": 4385, "igboapi_pspk04494": 4386, "igboapi_pspk04495": 4387, "igboapi_pspk04496": 4388, "igboapi_pspk04497": 4389, "igboapi_pspk04498": 4390, "igboapi_pspk04500": 4391, "igboapi_pspk04501": 4392, "igboapi_pspk04502": 4393, "igboapi_pspk04503": 4394, "igboapi_pspk04504": 4395, "igboapi_pspk04505": 4396, "igboapi_pspk04506": 4397, "igboapi_pspk04507": 4398, "igboapi_pspk04508": 4399, "igboapi_pspk04509": 4400, "igboapi_pspk04510": 4401, "igboapi_pspk04511": 4402, "igboapi_pspk04512": 4403, "igboapi_pspk04513": 4404, "igboapi_pspk04514": 4405, "igboapi_pspk04515": 4406, "igboapi_pspk04516": 4407, "igboapi_pspk04517": 4408, "igboapi_pspk04521": 4409, "igboapi_pspk04522": 4410, "igboapi_pspk04523": 4411, "igboapi_pspk04525": 4412, "igboapi_pspk04526": 4413, "igboapi_pspk04527": 4414, "igboapi_pspk04528": 4415, "igboapi_pspk04529": 4416, "igboapi_pspk04530": 4417, "igboapi_pspk04531": 4418, "igboapi_pspk04532": 4419, "igboapi_pspk04533": 4420, "igboapi_pspk04534": 4421, "igboapi_pspk04535": 4422, "igboapi_pspk04536": 4423, "igboapi_pspk04537": 4424, "igboapi_pspk04538": 4425, "igboapi_pspk04539": 4426, "igboapi_pspk04540": 4427, "igboapi_pspk04543": 4428, "igboapi_pspk04544": 4429, "igboapi_pspk04545": 4430, "igboapi_pspk04546": 4431, "igboapi_pspk04547": 4432, "igboapi_pspk04548": 4433, "igboapi_pspk04549": 4434, "igboapi_pspk04550": 4435, "igboapi_pspk04551": 4436, "igboapi_pspk04552": 4437, "igboapi_pspk04553": 4438, "igboapi_pspk04554": 4439, "igboapi_pspk04555": 4440, "igboapi_pspk04556": 4441, "igboapi_pspk04557": 4442, "igboapi_pspk04558": 4443, "igboapi_pspk04559": 4444, "igboapi_pspk04560": 4445, "igboapi_pspk04561": 4446, "igboapi_pspk04562": 4447, "igboapi_pspk04564": 4448, "igboapi_pspk04565": 4449, "igboapi_pspk04566": 4450, "igboapi_pspk04567": 4451, "igboapi_pspk04570": 4452, "igboapi_pspk04571": 4453, "igboapi_pspk04572": 4454, "igboapi_pspk04573": 4455, "igboapi_pspk04574": 4456, "igboapi_pspk04575": 4457, "igboapi_pspk04576": 4458, "igboapi_pspk04577": 4459, "igboapi_pspk04578": 4460, "igboapi_pspk04579": 4461, "igboapi_pspk04580": 4462, "igboapi_pspk04583": 4463, "igboapi_pspk04584": 4464, "igboapi_pspk04585": 4465, "igboapi_pspk04586": 4466, "igboapi_pspk04587": 4467, "igboapi_pspk04588": 4468, "igboapi_pspk04589": 4469, "igboapi_pspk04590": 4470, "igboapi_pspk04591": 4471, "igboapi_pspk04592": 4472, "igboapi_pspk04594": 4473, "igboapi_pspk04595": 4474, "igboapi_pspk04596": 4475, "igboapi_pspk04597": 4476, "igboapi_pspk04598": 4477, "igboapi_pspk04600": 4478, "igboapi_pspk04601": 4479, "igboapi_pspk04602": 4480, "igboapi_pspk04603": 4481, "igboapi_pspk04604": 4482, "igboapi_pspk04605": 4483, "igboapi_pspk04606": 4484, "igboapi_pspk04607": 4485, "igboapi_pspk04608": 4486, "igboapi_pspk04609": 4487, "igboapi_pspk04611": 4488, "igboapi_pspk04614": 4489, "igboapi_pspk04616": 4490, "igboapi_pspk04617": 4491, "igboapi_pspk04618": 4492, "igboapi_pspk04619": 4493, "igboapi_pspk04620": 4494, "igboapi_pspk04622": 4495, "igboapi_pspk04623": 4496, "igboapi_pspk04624": 4497, "igboapi_pspk04626": 4498, "igboapi_pspk04627": 4499, "igboapi_pspk04628": 4500, "igboapi_pspk04629": 4501, "igboapi_pspk04631": 4502, "igboapi_pspk04633": 4503, "igboapi_pspk04634": 4504, "igboapi_pspk04636": 4505, "igboapi_pspk04637": 4506, "igboapi_pspk04638": 4507, "igboapi_pspk04639": 4508, "igboapi_pspk04640": 4509, "igboapi_pspk04641": 4510, "igboapi_pspk04642": 4511, "igboapi_pspk04644": 4512, "igboapi_pspk04645": 4513, "igboapi_pspk04646": 4514, "igboapi_pspk04647": 4515, "igboapi_pspk04650": 4516, "igboapi_pspk04651": 4517, "igboapi_pspk04653": 4518, "igboapi_pspk04654": 4519, "igboapi_pspk04655": 4520, "igboapi_pspk04656": 4521, "igboapi_pspk04657": 4522, "igboapi_pspk04658": 4523, "igboapi_pspk04659": 4524, "igboapi_pspk04660": 4525, "igboapi_pspk04662": 4526, "igboapi_pspk04663": 4527, "igboapi_pspk04664": 4528, "igboapi_pspk04665": 4529, "igboapi_pspk04666": 4530, "igboapi_pspk04667": 4531, "igboapi_pspk04668": 4532, "igboapi_pspk04670": 4533, "igboapi_pspk04671": 4534, "igboapi_pspk04672": 4535, "igboapi_pspk04674": 4536, "igboapi_pspk04675": 4537, "igboapi_pspk04676": 4538, "igboapi_pspk04677": 4539, "igboapi_pspk04680": 4540, "igboapi_pspk04681": 4541, "igboapi_pspk04682": 4542, "igboapi_pspk04683": 4543, "igboapi_pspk04686": 4544, "igboapi_pspk04687": 4545, "igbosyn_Abia_0001": 4546, "igbosyn_Abia_0003": 4547, "igbosyn_Abia_0006": 4548, "igbosyn_Abia_0007": 4549, "igbosyn_Abia_0009": 4550, "igbosyn_Abia_0011": 4551, "igbosyn_Abia_0012": 4552, "igbosyn_Abia_0013": 4553, "igbosyn_Abia_0014": 4554, "igbosyn_Anambra_0004": 4555, "igbosyn_Anambra_0005": 4556, "igbosyn_Anambra_0006": 4557, "igbosyn_Anambra_0007": 4558, "igbosyn_Anambra_0009": 4559, "igbosyn_Anambra_0012": 4560, "igbosyn_Anambra_0013": 4561, "igbosyn_Anambra_0014": 4562, "igbosyn_Anambra_0015": 4563, "igbosyn_Ebonyi_0001": 4564, "igbosyn_Ebonyi_0002": 4565, "igbosyn_Ebonyi_0003": 4566, "igbosyn_Ebonyi_0004": 4567, "igbosyn_Ebonyi_0005": 4568, "igbosyn_Ebonyi_0008": 4569, "igbosyn_Ebonyi_0009": 4570, "igbosyn_Ebonyi_0010": 4571, "igbosyn_Ebonyi_0012": 4572, "igbosyn_Ebonyi_0015": 4573, "igbosyn_Ebonyi_0019": 4574, "igbosyn_Ebonyi_0020": 4575, "igbosyn_Ebonyi_0021": 4576, "igbosyn_Ebonyi_0024": 4577, "igbosyn_Ebonyi_0025": 4578, "igbosyn_Ebonyi_0026": 4579, "igbosyn_Enugu_0001": 4580, "igbosyn_Enugu_0002": 4581, "igbosyn_Enugu_0003": 4582, "igbosyn_Enugu_0008": 4583, "igbosyn_Enugu_0009": 4584, "igbosyn_Enugu_0010": 4585, "igbosyn_Enugu_0011": 4586, "igbosyn_Enugu_0013": 4587, "igbosyn_Enugu_0015": 4588, "igbosyn_Enugu_0016": 4589, "igbosyn_Enugu_0019": 4590, "igbosyn_Enugu_0020": 4591, "igbosyn_Enugu_0023": 4592, "igbosyn_Enugu_0024": 4593, "igbosyn_Enugu_0026": 4594, "igbosyn_Igbo Radio_0003": 4595, "igbosyn_Igbo Radio_0005": 4596, "igbosyn_Igbo Radio_0006": 4597, "igbosyn_Imo_0001": 4598, "igbosyn_Imo_0002": 4599, "igbosyn_Imo_0003": 4600, "igbosyn_Imo_0004": 4601, "igbosyn_Imo_0006": 4602, "igbosyn_Imo_0007": 4603, "igbosyn_Imo_0008": 4604, "igbosyn_Imo_0009": 4605, "igbosyn_Imo_0010": 4606, "igbosyn_Imo_0012": 4607, "igbosyn_Imo_0013": 4608, "igbosyn_Imo_0015": 4609, "igbosyn_Imo_0016": 4610, "igbosyn_Imo_0018": 4611, "igbosyn_Imo_0019": 4612, "igbosyn_Imo_0021": 4613, "igbosyn_Imo_0022": 4614, "igbosyn_Imo_0023": 4615, "igbosyn_Imo_0024": 4616, "igbosyn_Imo_0025": 4617, "igbosyn_Imo_0026": 4618, "igbosyn_Imo_0027": 4619, "naija_I001": 4620, "naija_I002": 4621, "naija_I003": 4622, "naija_I004": 4623, "naija_I006": 4624, "naija_I007": 4625, "naija_I009": 4626, "naija_I010": 4627, "naija_I011": 4628, "naija_I012": 4629, "naija_I013": 4630, "naija_I014": 4631, "naija_I015": 4632, "naija_I016": 4633, "naija_I017": 4634, "naija_I018": 4635, "naija_I019": 4636, "naija_I020": 4637, "naija_I021": 4638, "naija_I022": 4639, "naija_I026": 4640, "naija_I027": 4641, "naija_I029": 4642, "naija_I030": 4643, "naija_I031": 4644, "naija_I033": 4645, "naija_I034": 4646, "naija_I035": 4647, "naija_I036": 4648, "naija_I037": 4649, "naija_I039": 4650, "naija_I040": 4651, "naija_I042": 4652, "naija_I044": 4653, "naija_I045": 4654, "naija_I046": 4655, "naija_I047": 4656, "naija_I048": 4657, "naija_I049": 4658, "naija_I050": 4659, "naija_I051": 4660, "naija_I052": 4661, "naija_I054": 4662, "naija_I055": 4663, "naija_I057": 4664, "naija_I058": 4665, "naija_I059": 4666, "naija_I060": 4667, "naija_I061": 4668, "naija_I062": 4669, "naija_I064": 4670, "naija_I066": 4671, "naija_I067": 4672, "naija_I068": 4673, "naija_I069": 4674, "naija_I070": 4675, "obig_SPEAKER_01": 4676, "obig_SPEAKER_02": 4677, "obig_SPEAKER_03": 4678, "obig_SPEAKER_04": 4679, "obig_SPEAKER_05": 4680, "slr70_ngf_00295": 4681, "slr70_ngf_00610": 4682, "slr70_ngf_01208": 4683, "slr70_ngf_01523": 4684, "slr70_ngf_02121": 4685, "slr70_ngf_02436": 4686, "slr70_ngf_02484": 4687, "slr70_ngf_03349": 4688, "slr70_ngf_03397": 4689, "slr70_ngf_04310": 4690, "slr70_ngf_05223": 4691, "slr70_ngf_06136": 4692, "slr70_ngf_07049": 4693, "slr70_ngf_07508": 4694, "slr70_ngf_08421": 4695, "slr70_ngf_09334": 4696, "slr70_ngf_12484": 4697, "slr70_ngf_13397": 4698, "slr70_ngf_15223": 4699, "slr70_ngm_01523": 4700, "slr70_ngm_02436": 4701, "slr70_ngm_02484": 4702, "slr70_ngm_03349": 4703, "slr70_ngm_03397": 4704, "slr70_ngm_07049": 4705, "slr70_ngm_07508": 4706, "slr70_ngm_08784": 4707, "slr70_ngm_09334": 4708, "slr70_ngm_09697": 4709, "slr70_ngm_12484": 4710, "slr70_ngm_14310": 4711, "slr86_yof_00295": 4712, "slr86_yof_00610": 4713, "slr86_yof_01208": 4714, "slr86_yof_02121": 4715, "slr86_yof_02436": 4716, "slr86_yof_02484": 4717, "slr86_yof_03034": 4718, "slr86_yof_03349": 4719, "slr86_yof_03397": 4720, "slr86_yof_04310": 4721, "slr86_yof_05223": 4722, "slr86_yof_06136": 4723, "slr86_yof_07049": 4724, "slr86_yof_07505": 4725, "slr86_yof_07508": 4726, "slr86_yof_08421": 4727, "slr86_yof_08784": 4728, "slr86_yof_09334": 4729, "slr86_yof_09697": 4730, "slr86_yom_00295": 4731, "slr86_yom_00610": 4732, "slr86_yom_01208": 4733, "slr86_yom_01523": 4734, "slr86_yom_02121": 4735, "slr86_yom_02436": 4736, "slr86_yom_02484": 4737, "slr86_yom_03034": 4738, "slr86_yom_03397": 4739, "slr86_yom_04310": 4740, "slr86_yom_06136": 4741, "slr86_yom_07049": 4742, "slr86_yom_07505": 4743, "slr86_yom_07508": 4744, "slr86_yom_08421": 4745, "slr86_yom_08784": 4746, "slr86_yom_09334": 4747, "syncorp_Abia_0002": 4748, "syncorp_Abia_0004": 4749, "syncorp_Abia_0005": 4750, "syncorp_Abia_0010": 4751, "syncorp_Anambra_0002": 4752, "syncorp_Anambra_0010": 4753, "syncorp_Anambra_0011": 4754, "syncorp_Ebonyi_0018": 4755, "syncorp_Ebonyi_11": 4756, "syncorp_Enugu_0014": 4757, "syncorp_Enugu_0025": 4758, "syncorp_Imo_0005": 4759, "syncorp_Imo_0011": 4760, "waxal_hau_1": 4761, "waxal_hau_2": 4762, "waxal_hau_3": 4763, "waxal_hau_4": 4764, "waxal_hau_5": 4765, "waxal_hau_6": 4766, "waxal_hau_7": 4767, "waxal_hau_8": 4768, "waxal_ibo_1": 4769, "waxal_ibo_2": 4770, "waxal_ibo_3": 4771, "waxal_ibo_4": 4772, "waxal_ibo_5": 4773, "waxal_ibo_6": 4774, "waxal_ibo_7": 4775, "waxal_ibo_8": 4776, "waxal_pcm_F1": 4777, "waxal_pcm_F2": 4778, "waxal_pcm_F3": 4779, "waxal_pcm_F4": 4780, "waxal_pcm_M1": 4781, "waxal_pcm_M2": 4782, "waxal_pcm_M3": 4783, "waxal_pcm_M4": 4784, "waxal_yor_1": 4785, "waxal_yor_2": 4786, "waxal_yor_3": 4787, "waxal_yor_4": 4788, "waxal_yor_5": 4789, "waxal_yor_6": 4790, "waxal_yor_7": 4791, "waxal_yor_8": 4792} \ No newline at end of file diff --git a/code/da/__init__.py b/code/da/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/code/da/data.py b/code/da/data.py new file mode 100644 index 0000000000000000000000000000000000000000..12e8e60c7775d16a8a313df1442d7ef94461e3e1 --- /dev/null +++ b/code/da/data.py @@ -0,0 +1,95 @@ +"""Arm A data: memmapped Vocos mels + byte tokens with per-token language ids; frame-budget bucketing. +Beta-binomial alignment priors are computed in DataLoader workers.""" +import json, os, random +import numpy as np +import torch +from torch.utils.data import Dataset, Sampler + +from .text import encode, LANG2ID +from .model import beta_binomial_prior + +N_MELS = 100 + + +def load_meta(root, datasets, split="train", max_frames=1400, max_tokens=420): + items, mm = [], {} + for ds in datasets: + p = f"{root}/{ds}/meta.jsonl" + if not os.path.exists(p) or os.path.getsize(p) == 0: + continue + mm[ds] = np.memmap(f"{root}/{ds}/mels.f16", dtype=np.float16, mode="r").reshape(-1, N_MELS) + for l in open(p, encoding="utf-8"): + m = json.loads(l) + if m["split"] != split or m["nf"] > max_frames: + continue + if len(m["text"].encode("utf-8")) + 2 > max_tokens: + continue + items.append(m) + return items, mm + + +class TTSData(Dataset): + def __init__(self, items, mm, spk2id): + self.items, self.mm, self.spk2id = items, mm, spk2id + + def __len__(self): + return len(self.items) + + def __getitem__(self, i): + m = self.items[i] + mel = np.asarray(self.mm[m["ds"]][m["off"]: m["off"] + m["nf"]], dtype=np.float32) + tok, _ = encode(f"<{m['lang']}>{m['text']}") + lang = [LANG2ID[m["lang"]]] * len(tok) + return dict(tok=tok, lang=lang, mel=mel, spk=self.spk2id[m["spk"]], id=m["id"]) + + +def collate(batch): + B = len(batch) + N = max(len(b["tok"]) for b in batch) + T = max(len(b["mel"]) for b in batch) + tok = torch.zeros(B, N, dtype=torch.long) + lang = torch.zeros(B, N, dtype=torch.long) + mel = torch.zeros(B, T, N_MELS) + prior = torch.full((B, T, N), -1e4) + tl, ml = torch.zeros(B, dtype=torch.long), torch.zeros(B, dtype=torch.long) + for i, b in enumerate(batch): + n, t = len(b["tok"]), len(b["mel"]) + tok[i, :n] = torch.tensor(b["tok"]); lang[i, :n] = torch.tensor(b["lang"]) + mel[i, :t] = torch.from_numpy(b["mel"]); tl[i], ml[i] = n, t + prior[i, :t, :n] = beta_binomial_prior(n, t) + spk = torch.tensor([b["spk"] for b in batch]) + return dict(tok=tok, lang=lang, tlens=tl, mel=mel, mlens=ml, spk=spk, prior=prior) + + +class FrameBucketSampler(Sampler): + """Batches of similar length under a padded-frame budget; dataset weights by repetition.""" + + def __init__(self, items, frame_budget=40000, weights=None, seed=0): + self.items, self.budget, self.seed, self.epoch = items, frame_budget, seed, 0 + self.weights = weights or {} + + def _batches(self): + rng = random.Random(self.seed + self.epoch) + idx = [] + for i, m in enumerate(self.items): + w = self.weights.get(m["ds"], 1.0) + idx += [i] * int(w) + ([i] if rng.random() < (w - int(w)) else []) + idx.sort(key=lambda i: self.items[i]["nf"] + rng.random() * 30) + batches, cur, mx = [], [], 0 + for i in idx: + nf = self.items[i]["nf"] + if cur and max(mx, nf) * (len(cur) + 1) > self.budget: + batches.append(cur); cur, mx = [], 0 + cur.append(i); mx = max(mx, nf) + if cur: + batches.append(cur) + rng.shuffle(batches) + return batches + + def __iter__(self): + b = self._batches() + self.epoch += 1 + return iter(b) + + def __len__(self): + return len(self._batches()) diff --git a/code/da/model.py b/code/da/model.py new file mode 100644 index 0000000000000000000000000000000000000000..17c495c3e71fa1ff6f5c5abb19f173a7ad761e36 --- /dev/null +++ b/code/da/model.py @@ -0,0 +1,368 @@ +"""Dynamic Accent - Arm A, written from scratch in PyTorch. + +Backbone: Parallel Tacotron 2 (Elias et al. 2021, arXiv 2103.14574): non-autoregressive, token durations, + differentiable learned upsampling (MLP over start/end offsets -> token->frame weights W), iterative mel loss. +Multilingual recipe: Zhang et al. 2019 (arXiv 1907.04448): separate speaker + language embeddings, + domain-adversarial speaker classifier (gradient reversal) on text-encoder outputs, residual VAE. + +Documented deviation (alignment): PT2 trains durations end-to-end with Soft-DTW. Soft-DTW is O(T*N)-sequential and +too slow for a 2 h smoke budget, so we use the internal unsupervised alignment-learning objective of +Badlani et al. 2022 ("One TTS Alignment To Rule Them All", arXiv 2108.10447): a small text/mel attention trained with +a CTC forward-sum loss + beta-binomial prior, hardened with monotonic alignment search (MAS). The hard durations +supervise the duration predictor and drive the SAME differentiable PT2 upsampler at train time; predicted durations +drive it at inference. No external aligner is used. +""" +import math +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F + +from .text import VOCAB, LANGS + +N_MELS = 100 + + +# ---------------------------------------------------------------- utils +def seq_mask(lens, maxlen=None): + maxlen = maxlen or int(lens.max()) + return torch.arange(maxlen, device=lens.device)[None] < lens[:, None] # True = valid + + +def sinusoid(T, d, device): + pos = torch.arange(T, device=device).float()[:, None] + i = torch.arange(0, d, 2, device=device).float()[None] + ang = pos / (10000 ** (i / d)) + pe = torch.zeros(T, d, device=device) + pe[:, 0::2] = torch.sin(ang) + pe[:, 1::2] = torch.cos(ang) + return pe + + +class GradReverse(torch.autograd.Function): + """Gradient reversal. As in Zhang et al. 2019 the reversed gradient sent into the text encoder is clipped + (here: its L2 norm is capped at `clip`), which keeps the min-max game from blowing up the encoder.""" + + @staticmethod + def forward(ctx, x, lam, clip): + ctx.lam, ctx.clip = lam, clip + return x.view_as(x) + + @staticmethod + def backward(ctx, g): + g = -ctx.lam * g + if ctx.clip is not None: + n = g.norm() + if n > ctx.clip: + g = g * (ctx.clip / (n + 1e-12)) + return g, None, None + + +def grad_reverse(x, lam=1.0, clip=0.5): + return GradReverse.apply(x, lam, clip) + + +class ConvNorm(nn.Module): + def __init__(self, cin, cout, k=5, drop=0.1): + super().__init__() + self.conv = nn.Conv1d(cin, cout, k, padding=k // 2) + self.ln = nn.LayerNorm(cout) + self.drop = nn.Dropout(drop) + + def forward(self, x, mask): # x [B,T,C], mask [B,T] + x = x.masked_fill(~mask[..., None], 0) + y = self.conv(x.transpose(1, 2)).transpose(1, 2) + return self.drop(F.relu(self.ln(y))) + + +class FFTBlock(nn.Module): + """Pre-LN self-attention + convolutional feed-forward (FastSpeech/PT2-style lightweight block).""" + + def __init__(self, d, heads=4, ff=4, k=3, drop=0.1): + super().__init__() + self.ln1 = nn.LayerNorm(d) + self.att = nn.MultiheadAttention(d, heads, dropout=drop, batch_first=True) + self.ln2 = nn.LayerNorm(d) + self.c1 = nn.Conv1d(d, d * ff, k, padding=k // 2) + self.c2 = nn.Conv1d(d * ff, d, 1) + self.drop = nn.Dropout(drop) + + def forward(self, x, mask): + h = self.ln1(x) + a, _ = self.att(h, h, h, key_padding_mask=~mask, need_weights=False) + x = x + self.drop(a) + h = self.ln2(x).masked_fill(~mask[..., None], 0).transpose(1, 2) + h = self.c2(F.gelu(self.c1(h))).transpose(1, 2) + x = x + self.drop(h) + return x.masked_fill(~mask[..., None], 0) + + +# ---------------------------------------------------------------- modules +class TextEncoder(nn.Module): + def __init__(self, d, n_lang, layers=6): + super().__init__() + self.emb = nn.Embedding(VOCAB, d, padding_idx=0) + self.lang_in = nn.Embedding(n_lang, d) # per-token language id at the input + self.convs = nn.ModuleList([ConvNorm(d, d, 5) for _ in range(3)]) + self.blocks = nn.ModuleList([FFTBlock(d) for _ in range(layers)]) + + def forward(self, tok, lang, mask): + x = self.emb(tok) + self.lang_in(lang) + for c in self.convs: + x = x + c(x, mask) + x = x + sinusoid(x.shape[1], x.shape[2], x.device)[None] + for b in self.blocks: + x = b(x, mask) + return x + + +class ResidualVAE(nn.Module): + """Global residual (prosody) encoder: mel -> q(z|x); z broadcast to every token (Zhang 2019 / Hsu 2019).""" + + def __init__(self, zdim=16, h=256): + super().__init__() + self.convs = nn.Sequential( + nn.Conv1d(N_MELS, h, 3, padding=1), nn.ReLU(), + nn.Conv1d(h, h, 3, stride=2, padding=1), nn.ReLU(), + nn.Conv1d(h, h, 3, stride=2, padding=1), nn.ReLU()) + self.gru = nn.GRU(h, h // 2, batch_first=True, bidirectional=True) + self.mu = nn.Linear(h, zdim) + self.logvar = nn.Linear(h, zdim) + + def forward(self, mel, mel_lens): + h = self.convs(mel.transpose(1, 2)).transpose(1, 2) + lens = torch.clamp((mel_lens + 3) // 4, min=1) + h, _ = self.gru(h) + m = seq_mask(lens, h.shape[1]).float()[..., None] + h = (h * m).sum(1) / m.sum(1) + mu, lv = self.mu(h), self.logvar(h).clamp(-8, 8) + z = mu + torch.randn_like(mu) * torch.exp(0.5 * lv) if self.training else mu + kl = 0.5 * (mu.pow(2) + lv.exp() - 1 - lv).sum(-1).mean() + return z, kl + + +class Aligner(nn.Module): + """Unsupervised text-mel alignment (Badlani et al. 2022).""" + + def __init__(self, d, h=256, temp=0.0005): + super().__init__() + self.temp = temp + self.key = nn.Sequential(nn.Conv1d(d, h * 2, 3, padding=1), nn.ReLU(), nn.Conv1d(h * 2, 80, 1)) + self.query = nn.Sequential(nn.Conv1d(N_MELS, h * 2, 3, padding=1), nn.ReLU(), + nn.Conv1d(h * 2, h, 1), nn.ReLU(), nn.Conv1d(h, 80, 1)) + + def forward(self, txt, mel, tmask, mmask, prior_log): + k = self.key(txt.transpose(1, 2)) # B,80,N + q = self.query(mel.transpose(1, 2)) # B,80,T + q, k = q.float(), k.float() + dist = (q.pow(2).sum(1)[:, :, None] + k.pow(2).sum(1)[:, None, :] + - 2 * torch.bmm(q.transpose(1, 2), k)).clamp_min(0) # B,T,N (memory-light ||q-k||^2) + logits = -self.temp * dist + logits = logits.masked_fill(~tmask[:, None, :], -1e4) + logp = F.log_softmax(logits, -1) + prior_log # prior in log domain + logp = logp.masked_fill(~tmask[:, None, :], -1e4) + soft = F.softmax(logp, -1) * mmask[..., None] + return logits, logp, soft + + +def forward_sum_loss(logits, tlens, mlens, blank_logprob=-1.0): + """CTC forward-sum: every token must be visited monotonically. logits [B,T,N] (pre-softmax, masked).""" + B, T, N = logits.shape + lp = F.pad(logits, (1, 0), value=blank_logprob) + lp = F.log_softmax(lp, -1).transpose(0, 1) # T,B,N+1 + tgt = torch.arange(1, N + 1, device=logits.device)[None].expand(B, N) + return F.ctc_loss(lp.float(), tgt, mlens, tlens, blank=0, reduction="mean", zero_infinity=True) + + +def _mas_np(logp, N, T): + """Monotonic alignment search on one [T,N] log-prob matrix -> hard [T,N].""" + Q = np.full((T, N), -np.inf, dtype=np.float32) + Q[0, 0] = logp[0, 0] + for t in range(1, T): + for n in range(min(N, t + 1)): + stay = Q[t - 1, n] + move = Q[t - 1, n - 1] if n > 0 else -np.inf + Q[t, n] = max(stay, move) + logp[t, n] + path = np.zeros((T, N), dtype=np.float32) + n = N - 1 + for t in range(T - 1, -1, -1): + path[t, n] = 1 + if t > 0 and n > 0 and (n == t or Q[t - 1, n - 1] > Q[t - 1, n]): + n -= 1 + return path + + +try: + import numba + + _mas_np = numba.njit(cache=True)(_mas_np) # noqa: F811 +except Exception: # numba optional (CPU unit tests) + pass + + +@torch.no_grad() +def mas(logp, tlens, mlens): + out = torch.zeros_like(logp) + lp = logp.float().cpu().numpy() + for b in range(lp.shape[0]): + T, N = int(mlens[b]), int(tlens[b]) + out[b, :T, :N] = torch.from_numpy(_mas_np(np.ascontiguousarray(lp[b, :T, :N]), N, T)) + return out + + +_prior_cache = {} + + +def beta_binomial_prior(N, T, scale=1.0): + from scipy.stats import betabinom + key = (N, T) + if key not in _prior_cache: + x = np.arange(N)[None] + t = np.arange(1, T + 1)[:, None] + pmf = betabinom.pmf(x, N - 1, scale * t, scale * (T + 1 - t)) + _prior_cache[key] = torch.from_numpy(np.log(pmf + 1e-8)).float() + if len(_prior_cache) > 20000: + _prior_cache.clear() + return _prior_cache[key] + + +def batch_prior(tlens, mlens, N, T, device): + p = torch.full((len(tlens), T, N), -1e4) + for b in range(len(tlens)): + n, t = int(tlens[b]), int(mlens[b]) + p[b, :t, :n] = beta_binomial_prior(n, t) + return p.to(device) + + +class DurationPredictor(nn.Module): + def __init__(self, d, h=256): + super().__init__() + self.convs = nn.ModuleList([ConvNorm(d, h, 3), ConvNorm(h, h, 3), ConvNorm(h, h, 3)]) + self.out = nn.Linear(h, 1) + + def forward(self, x, mask): + for c in self.convs: + x = c(x, mask) + return self.out(x).squeeze(-1).masked_fill(~mask, 0) # log(1 + d) + + +class LearnedUpsampler(nn.Module): + """PT2 differentiable upsampling: W[t,k] = softmax_k MLP(S_tk, E_tk, h_k); O = W H + proj(sum_k W C).""" + + def __init__(self, d, hw=16, hc=8): + super().__init__() + self.se_w = nn.Linear(2, hw) + self.h_w = nn.Linear(d, hw) + self.w_out = nn.Linear(hw, 1) + self.se_c = nn.Linear(2, hc) + self.h_c = nn.Linear(d, hc) + self.c_out = nn.Linear(hc, d) + + def forward(self, H, dur, tmask, T): + dur = dur.masked_fill(~tmask, 0) + e = torch.cumsum(dur, 1) + s = e - dur + t = torch.arange(T, device=H.device).float()[None, :, None] + 0.5 + S = (t - s[:, None, :]) / 10.0 + E = (e[:, None, :] - t) / 10.0 + se = torch.stack([S, E], -1) # B,T,N,2 + lw = self.w_out(F.silu(self.se_w(se) + self.h_w(H)[:, None])).squeeze(-1) + lw = lw.masked_fill(~tmask[:, None, :], -1e4) + W = torch.softmax(lw, -1) # B,T,N + C = F.silu(self.se_c(se) + self.h_c(H)[:, None]) # B,T,N,hc + out = torch.bmm(W, H) + self.c_out(torch.einsum("btn,btnc->btc", W, C)) + return out, W + + +class MelDecoder(nn.Module): + def __init__(self, d, layers=6, n_iter=3): + super().__init__() + self.blocks = nn.ModuleList([FFTBlock(d) for _ in range(layers)]) + self.n_iter = n_iter + self.outs = nn.ModuleList([nn.Linear(d, N_MELS) for _ in range(n_iter)]) + + def forward(self, x, mask): + x = x + sinusoid(x.shape[1], x.shape[2], x.device)[None] + mels = [] + L = len(self.blocks) + for i, b in enumerate(self.blocks): + x = b(x, mask) + j = i - (L - self.n_iter) + if j >= 0: + mels.append(self.outs[j](x)) + return mels + + +# ---------------------------------------------------------------- full model +class DynamicAccentTTS(nn.Module): + def __init__(self, n_spk, n_lang=len(LANGS), d=384, enc_layers=6, dec_layers=6, spk_dim=128, lang_dim=64, + zdim=16, adv_hidden=256, mel_mean=None, mel_std=None, align_temp=0.0005): + super().__init__() + self.n_spk = n_spk + self.encoder = TextEncoder(d, n_lang, enc_layers) + self.spk_emb = nn.Embedding(n_spk, spk_dim) + self.lang_out = nn.Embedding(n_lang, lang_dim) # per-token, concatenated to encoder outputs + self.vae = ResidualVAE(zdim) + self.cond = nn.Linear(d + spk_dim + lang_dim + zdim, d) + self.adv = nn.Sequential(nn.LayerNorm(d), nn.Linear(d, adv_hidden), nn.ReLU(), nn.Linear(adv_hidden, n_spk)) + self.aligner = Aligner(d, temp=align_temp) + self.dur = DurationPredictor(d) + self.up = LearnedUpsampler(d) + self.dec_spk = nn.Linear(spk_dim, d) + self.decoder = MelDecoder(d, dec_layers) + self.register_buffer("mel_mean", torch.tensor(mel_mean if mel_mean is not None else -2.5)) + self.register_buffer("mel_std", torch.tensor(mel_std if mel_std is not None else 2.2)) + + def _cond(self, Ht, spk, lang, z): + B, N, _ = Ht.shape + s = self.spk_emb(spk)[:, None].expand(B, N, -1) + zz = z[:, None].expand(B, N, -1) + return self.cond(torch.cat([Ht, s, self.lang_out(lang), zz], -1)), s + + def forward(self, tok, lang, tlens, mel, mlens, spk, step=0, adv_lambda=1.0, bin_start=4000, prior=None): + tmask, mmask = seq_mask(tlens, tok.shape[1]), seq_mask(mlens, mel.shape[1]) + meln = (mel - self.mel_mean) / self.mel_std + Ht = self.encoder(tok, lang, tmask) + # adversarial speaker classifier on text encodings (Zhang 2019): GRL removes speaker info from text path + adv_logits = self.adv(grad_reverse(Ht, adv_lambda)) + adv_ce = F.cross_entropy(adv_logits[tmask], spk[:, None].expand_as(tok)[tmask]) + adv_acc = (adv_logits[tmask].argmax(-1) == spk[:, None].expand_as(tok)[tmask]).float().mean() + z, kl = self.vae(meln, mlens) + H, _ = self._cond(Ht, spk, lang, z) + # alignment (internal, unsupervised) + if prior is None: + prior = batch_prior(tlens, mlens, tok.shape[1], mel.shape[1], tok.device) + logits, logp, soft = self.aligner(self.encoder.emb(tok) + Ht.detach(), meln, tmask, mmask, prior) + l_fs = forward_sum_loss(logits.masked_fill(~mmask[..., None], -1e4) + prior, tlens, mlens) + hard = mas(logp, tlens, mlens) + l_bin = -(hard * torch.log(soft.clamp_min(1e-8))).sum() / hard.sum() if step >= bin_start else soft.new_zeros(()) + dur_t = hard.sum(1) # B,N frames + # durations + logd = self.dur(H.detach() if step < 1000 else H, tmask) + l_dur = F.mse_loss(logd[tmask], torch.log1p(dur_t[tmask])) + # upsample with target durations (train) -> decoder + up, W = self.up(H, dur_t, tmask, mel.shape[1]) + x = up + self.dec_spk(self.spk_emb(spk))[:, None] + mels = self.decoder(x, mmask) + m = mmask[..., None].float() + l_mel = sum((F.l1_loss(o, meln, reduction="none") * m).sum() / (m.sum() * N_MELS) for o in mels) / len(mels) + return dict(mel=l_mel, dur=l_dur, fs=l_fs, bin=l_bin, kl=kl, adv=adv_ce, adv_acc=adv_acc.detach()), mels[-1] + + @torch.no_grad() + def infer(self, tok, lang, spk, pace=1.0, z=None): + tlens = torch.full((tok.shape[0],), tok.shape[1], device=tok.device) + tmask = seq_mask(tlens, tok.shape[1]) + Ht = self.encoder(tok, lang, tmask) + if z is None: + z = torch.zeros(tok.shape[0], self.vae.mu.out_features, device=tok.device) + H, _ = self._cond(Ht, spk, lang, z) + dur = (torch.expm1(self.dur(H, tmask)).clamp(min=0) * pace).masked_fill(~tmask, 0) + T = max(int(dur.sum(1).max().round().item()), 1) + up, W = self.up(H, dur, tmask, T) + x = up + self.dec_spk(self.spk_emb(spk))[:, None] + mel = self.decoder(x, torch.ones(tok.shape[0], T, dtype=torch.bool, device=tok.device))[-1] + return mel * self.mel_std + self.mel_mean, dur + + +def count_params(m): + return sum(p.numel() for p in m.parameters()) diff --git a/code/da/p2common.py b/code/da/p2common.py new file mode 100644 index 0000000000000000000000000000000000000000..420dcbe54b7101d03067444be15816bc13b594d4 --- /dev/null +++ b/code/da/p2common.py @@ -0,0 +1,308 @@ +"""Phase 2 shared tools: romanisation for MMS forced alignment, chunked aligner, pause normalisation, +pause / F0 / Yoruba-tone metrics. Used by prep2.py (data) and eval2.py (metrics).""" +import re, unicodedata +import numpy as np + +# ------------------------------------------------------------------ text +_MAP = {"ọ": "o", "ẹ": "e", "ṣ": "s", "ị": "i", "ụ": "u", "ṅ": "n", "ń": "n", "ǹ": "n", "ḿ": "m", "ɓ": "b", "ɗ": "d", + "ƙ": "k", "ƴ": "y", "ŋ": "n", "ɛ": "e", "ɔ": "o", "ß": "s", "æ": "ae", "œ": "oe", "ø": "o", "đ": "d", "ł": "l"} +TONE_MARK = {"\u0301": 2, "\u0300": 0, "\u0304": 1} # acute=H, grave=L, macron=M +SENT_END = set(".?!") +CLAUSE = set(";:") +COMMA = set(",") +_VOW = set("aeiou") + + +def roman_char(c): + """one NFC char -> (romanised latin string, tone or None).""" + lc = c.lower() + if lc in _MAP: + return _MAP[lc], None + d = unicodedata.normalize("NFD", lc) + base = "".join(x for x in d if unicodedata.category(x) != "Mn") + tone = None + for x in d: + if x in TONE_MARK: + tone = TONE_MARK[x] + base = "".join(_MAP.get(b, b) for b in base) + base = "".join(b for b in base if ("a" <= b <= "z") or b == "'") + return base, tone + + +def words_for_align(text): + """text -> list of dict(word, rom, punct_after, chars=[(rom_char, orig_index, tone)]). + Words that romanise to nothing (digits, symbols) are aligned with the '*' star token.""" + out = [] + for m in re.finditer(r"\S+", text): + w = m.group(0) + chars = [] + for k, c in enumerate(w): + r, t = roman_char(c) + for j, rc in enumerate(r): + chars.append((rc, m.start() + k, t if j == len(r) - 1 else None)) + trail = re.sub(r"[\w'’]+", "", w[-3:]) if w else "" + p = None + if any(c in SENT_END for c in trail): + p = "sent" + elif any(c in CLAUSE for c in trail): + p = "clause" + elif any(c in COMMA for c in trail): + p = "comma" + rom = "".join(c[0] for c in chars) + out.append(dict(word=w, rom=rom if rom else "*", punct=p, chars=chars)) + return out + + +# ------------------------------------------------------------------ aligner +class Aligner: + """torchaudio MMS_FA (CC-BY-NC weights, used only as a data tool/judge) with chunked emissions for long audio.""" + + def __init__(self, device="cuda"): + import torch, torchaudio + self.torch = torch + self.F = torchaudio.functional + b = torchaudio.pipelines.MMS_FA + self.model = b.get_model().to(device).eval().half() + self.dict = b.get_dict() + self.dev = device + self.rate = 320 # samples per emission frame at 16 kHz (20 ms) + + def emission(self, y16, chunk_s=30, ctx_s=2): + torch = self.torch + sr = 16000 + x = torch.from_numpy(y16).float().to(self.dev) + if len(y16) <= (chunk_s + 2 * ctx_s) * sr: + with torch.inference_mode(): + e, _ = self.model(x[None].half()) + return e[0].float().log_softmax(-1) + outs = [] + step, ctx = chunk_s * sr, ctx_s * sr + nfr_total = len(y16) // self.rate + for s in range(0, len(y16), step): + a, b = max(0, s - ctx), min(len(y16), s + step + ctx) + with torch.inference_mode(): + e, _ = self.model(x[a:b][None].half()) + e = e[0].float().log_softmax(-1) + f0 = (s - a) // self.rate + nfr = min(step, len(y16) - s) // self.rate + outs.append(e[f0:f0 + nfr]) + e = torch.cat(outs, 0) + return e[:max(nfr_total, 1)] + + def align(self, y16, words): + """-> per word (start_s, end_s, score) and per char spans [(start_s, end_s)], or None on failure.""" + torch = self.torch + em = self.emission(y16) + toks, wid = [], [] + for i, w in enumerate(words): + for c in w["rom"]: + if c in self.dict: + toks.append(self.dict[c]); wid.append(i) + if not toks or len(toks) * 1 > em.shape[0]: + return None + t = torch.tensor([toks], dtype=torch.int32, device=self.dev) + try: + ali, sc = self.F.forced_align(em[None], t, blank=0) + except Exception: + return None + ali, sc = ali[0].cpu().numpy(), sc[0].exp().cpu().numpy() + # token spans (merge repeats; blank=0) + spans, i, k = [], 0, 0 + T = len(ali) + while i < T: + if ali[i] == 0: + i += 1; continue + j = i + while j + 1 < T and ali[j + 1] == ali[i] and not (j + 1 < T and False): + j += 1 + spans.append((i, j + 1, float(sc[i:j + 1].mean()))) + i = j + 1 + # repeated identical consecutive tokens (e.g. "aa") collapse in the path above only if no blank between; + # forced_align always inserts a blank between identical tokens, so len(spans)==len(toks) + if len(spans) != len(toks): + return None + r = self.rate / 16000 + W = [[None, None, []] for _ in words] + chars = [[] for _ in words] + for (a, b, s), w in zip(spans, wid): + if W[w][0] is None: + W[w][0] = a * r + W[w][1] = b * r; W[w][2].append(s); chars[w].append((a * r, b * r)) + res = [] + for i, (a, b, s) in enumerate(W): + if a is None: + return None + res.append(dict(start=a, end=b, score=float(np.mean(s)), chars=chars[i])) + return res + + +# ------------------------------------------------------------------ energy / pauses +def frame_db(y, sr, hop_s=0.01): + hop = int(sr * hop_s) + n = max(1, len(y) // hop) + fr = y[: n * hop].reshape(n, hop) + e = 10 * np.log10((fr.astype(np.float64) ** 2).mean(1) + 1e-10) + return e + + +def silence_mask(y, sr, rel_db=-40.0, hop_s=0.01): + e = frame_db(y, sr, hop_s) + ref = np.percentile(e, 98) + return e < ref + rel_db + + +def runs(mask): + """-> list of (start, end) index runs where mask is True.""" + out, i, n = [], 0, len(mask) + while i < n: + if mask[i]: + j = i + while j < n and mask[j]: + j += 1 + out.append((i, j)); i = j + else: + i += 1 + return out + + +PAUSE_TARGET = { # (min, max) silence seconds after a word, by the punctuation that follows it + "sent": (0.30, 0.60), + "clause": (0.20, 0.50), + "comma": (0.00, 0.40), + None: (0.00, 0.20), +} +# profile B (Phase 2 v2): keeps natural phrase breaks where there is no punctuation (the real anchor pauses at +# ~14% of unpunctuated word boundaries), still removes hesitations; set P2PAUSE=B +import os as _os +if _os.environ.get("P2PAUSE") == "B": + PAUSE_TARGET = {"sent": (0.35, 0.70), "clause": (0.25, 0.55), "comma": (0.00, 0.50), None: (0.00, 0.35)} +LEAD_S, TAIL_S = 0.12, 0.18 + + +def normalise_pauses(y, sr, words, ali, seg_a, seg_b, sil=None, lo_s=0.0, hi_s=None): + """Rebuild one segment [word seg_a .. seg_b] of y: edge silence fixed to LEAD_S/TAIL_S, every inter-word + silence clamped to PAUSE_TARGET[punct] (only truly silent frames are removed; silence is inserted, as a + copy of the quietest local frames, when a sentence/clause boundary has too little). -> (y_out, pauses)""" + hop = int(0.01 * sr) + if sil is None: + sil = silence_mask(y, sr) + hi_s = len(y) / sr if hi_s is None else hi_s + xf = int(0.005 * sr) + pieces, pauses = [], [] + a0 = ali[seg_a]["start"] + start = int(max(lo_s, a0 - LEAD_S, 0.0) * sr) + cur = start + for i in range(seg_a, seg_b): + e_i, s_n = ali[i]["end"], ali[i + 1]["start"] + f_a, f_b = int(e_i / 0.01), int(s_n / 0.01) + 1 + # longest silent run inside the gap (with 20 ms guard to the word edges) + best = None + for ra, rb in runs(sil[f_a:f_b]): + if best is None or rb - ra > best[1] - best[0]: + best = (ra, rb) + lo, hi = PAUSE_TARGET[words[i]["punct"]] + if best is None: + cur_sil = 0.0; mid = int((e_i + s_n) / 2 * sr) + else: + ra, rb = (f_a + best[0]) * hop, (f_a + best[1]) * hop + cur_sil = (rb - ra) / sr; mid = (ra + rb) // 2 + if cur_sil > hi and best is not None: # shrink: cut the middle of the silent run + cut = int((cur_sil - hi) * sr) + c0 = mid - cut // 2 + pieces.append(y[cur:c0]); cur = c0 + cut + pauses.append(hi) + elif cur_sil < lo: # insert quiet frames at the quietest point of the gap + add = int((lo - cur_sil) * sr) + g0, g1 = int(e_i * sr), max(int(s_n * sr), int(e_i * sr) + hop) + seg = y[g0:g1] + if len(seg) >= hop: + k = int(np.argmin(frame_db(seg, sr))) * hop + ins_at = g0 + k + q = y[ins_at:ins_at + hop] + else: + ins_at, q = mid, np.zeros(hop, dtype=y.dtype) + filler = np.tile(q, add // hop + 1)[:add] * 0.5 + pieces.append(y[cur:ins_at]); pieces.append(filler); cur = ins_at + pauses.append(lo) + else: + pauses.append(cur_sil) + end = min(len(y), int(min(hi_s, ali[seg_b]["end"] + TAIL_S) * sr)) + pieces.append(y[cur:end]) + out = np.concatenate([p for p in pieces if len(p)]) if pieces else y[start:end] + # pad edges with silence if the source had less than LEAD/TAIL available + lead_have = a0 - start / sr + if lead_have < LEAD_S - 0.02: + out = np.concatenate([np.zeros(int((LEAD_S - lead_have) * sr), out.dtype), out]) + tail_have = end / sr - ali[seg_b]["end"] + if tail_have < TAIL_S - 0.02: + out = np.concatenate([out, np.zeros(int((TAIL_S - tail_have) * sr), out.dtype)]) + # tiny fades at the edges + f = min(xf, len(out) // 4) + if f > 0: + out[:f] *= np.linspace(0, 1, f); out[-f:] *= np.linspace(1, 0, f) + return out.astype(np.float32), pauses + + +# ------------------------------------------------------------------ metrics on audio +def pause_stats(y, sr, min_pause=0.15): + """VAD (energy, -40 dB vs 98th pct) pauses: leading/trailing silence and internal pauses >= min_pause.""" + sil = silence_mask(y, sr) + R = runs(sil) + n = len(sil) + lead = R[0][1] * 0.01 if R and R[0][0] == 0 else 0.0 + tail = (n - R[-1][0]) * 0.01 if R and R[-1][1] == n else 0.0 + internal = [(b - a) * 0.01 for a, b in R if a > 0 and b < n and (b - a) * 0.01 >= min_pause] + speech = max(1e-3, n * 0.01 - lead - tail) + return dict(lead=round(lead, 3), tail=round(tail, 3), pauses=[round(p, 3) for p in internal], + n_pauses=len(internal), pause_rate=round(len(internal) / speech, 4), + pause_frac=round(sum(internal) / speech, 4), speech_s=round(speech, 3)) + + +def f0_track(y16): + import pyworld as pw + x = y16.astype(np.float64) + f0, t = pw.dio(x, 16000, f0_floor=60, f0_ceil=500, frame_period=10) + return pw.stonemask(x, f0, t, 16000) + + +def f0_stats(y16): + f0 = f0_track(y16) + v = f0[f0 > 0] + if len(v) < 20: + return dict(voiced=float(len(v)), f0_med=None, st_std=None, st_range=None) + st = 12 * np.log2(v / np.median(v)) + d = np.diff(st) + return dict(voiced_frac=round(len(v) / len(f0), 3), f0_med=round(float(np.median(v)), 1), + st_std=round(float(st.std()), 3), st_range=round(float(np.percentile(st, 95) - np.percentile(st, 5)), 3), + st_vel=round(float(np.abs(d[np.abs(d) < 3]).mean()), 4)) + + +def tone_pairs(y16, words, ali): + """Yoruba tone-direction accuracy: for consecutive tone-bearing vowels whose tones differ, is the sign of the + median-F0 change (semitones) the same as the sign of the tone change? -> (n_pairs, n_correct)""" + f0 = f0_track(y16) + vals = [] + for w, a in zip(words, ali): + k = 0 + for (rc, oi, tone), (s, e) in zip([c for c in w["chars"] if c[0] in "abcdefghijklmnopqrstuvwxyz'"], a["chars"]): + if rc in _VOW or (rc in "nm" and tone is not None): + t = 1 if tone is None else tone + fr = f0[int(s / 0.01): int(e / 0.01) + 1] + fr = fr[fr > 0] + vals.append((t, float(np.median(12 * np.log2(fr))) if len(fr) else None)) + n = c = 0 + for (t1, f1), (t2, f2) in zip(vals, vals[1:]): + if t1 == t2 or f1 is None or f2 is None: + continue + n += 1 + c += int(np.sign(f2 - f1) == np.sign(t2 - t1)) + return n, c + + +def wasserstein1(a, b): + """1-D earth mover's distance between two samples.""" + if not len(a) or not len(b): + return None + a, b = np.sort(a), np.sort(b) + q = np.linspace(0, 1, 101) + return float(np.mean(np.abs(np.quantile(a, q) - np.quantile(b, q)))) diff --git a/code/da/prep2.py b/code/da/prep2.py new file mode 100644 index 0000000000000000000000000000000000000000..799eb96738f7e300b4718dc371a7dc4a745f288e --- /dev/null +++ b/code/da/prep2.py @@ -0,0 +1,259 @@ +"""Phase 2 data prep (H100 node): every source -> MMS forced alignment -> sentence segmentation of long clips -> +punctuation-consistent pause normalisation -> 24 kHz int16 + meta. Output ~/data2//{pcm24k.i16, meta.jsonl, stats.json} + + python -m da.prep2 waxal bible_hau bible_yor slr86 slr70 fleurs +""" +import os, sys, io, json, glob, csv, hashlib, time +import numpy as np, soundfile as sf, librosa +from multiprocessing import Pool +sys.path.insert(0, os.path.expanduser("~/da")) +from da.p2common import words_for_align, Aligner, normalise_pauses # noqa: E402 + +RAW = "/scratch/raw" +OUT = os.path.expanduser(os.environ.get("P2OUT", "~/data2")) +OLD = os.path.expanduser("~/data") +SR = 24000 +BIBLE_HAU_CAP_H = 30.0 +FLOW_OK = {"waxal", "bible_yor", "bible_hau", "slr86", "slr70"} # 48 kHz studio-ish sources (FLEURS is 16 kHz) + + +def clean_text(t): + import unicodedata, re + t = unicodedata.normalize("NFC", t or "") + t = t.replace("\u2019", "'").replace("\u2018", "'").replace("\u201c", '"').replace("\u201d", '"') + return re.sub(r"\s+", " ", t).strip() + + +def hsplit(i): + return "val" if int(hashlib.md5(i.encode()).hexdigest(), 16) % 100 == 0 else "train" + + +# ------------------------------------------------------------------ enumerators (yield meta + loader args) +def items_waxal(): + import pyarrow.parquet as pq + for lang in ["yor", "ibo", "hau", "pcm"]: + files = sorted(glob.glob(f"{RAW}/waxal/data/TTS/{lang}/*.parquet")) + files = [f for f in files if not os.path.basename(f).startswith("yor_tts")] + for f in files: + bn = os.path.basename(f) + split = "test" if "test" in bn else ("val" if "validation" in bn else "train") + pf = pq.ParquetFile(f) + for b in pf.iter_batches(batch_size=8): + for r in b.to_pylist(): + yield dict(id=f"waxal_{lang}_{r['id']}", ds="waxal", lang=lang, + spk=f"waxal_{lang}_{r.get('speaker_id') or 'spk'}", gender=r.get("gender") or "", + split=split, text=r["text"]), ("bytes", r["audio"]["bytes"]) + + +FLEURS = {"hau": "ha_ng", "ibo": "ig_ng", "yor": "yo_ng", "eng": "en_us"} + + +def items_fleurs(): + """raw FLEURS train audio (16 kHz); speaker ids = Phase 1 ECAPA pseudo-speakers from ~/data/fleurs/meta.jsonl""" + for l in open(f"{OLD}/fleurs/meta.jsonl", encoding="utf-8"): + m = json.loads(l) + fn = m["id"].split("_", 2)[2] + ".wav" + p = f"{RAW}/fleurs/data/{FLEURS[m['lang']]}/audio/train/{fn}" + yield dict(id=m["id"], ds="fleurs", lang=m["lang"], spk=m["spk"], gender=m.get("gender", ""), + split=hsplit(m["id"]), text=m["text"]), ("path", p) + + +def items_slr(num, lang, prefix): + for idx in sorted(glob.glob(f"{RAW}/slr{num}/**/line_index.tsv", recursive=True)): + d = os.path.dirname(idx) + for line in open(idx, encoding="utf-8"): + parts = line.rstrip("\n").split("\t") + if len(parts) < 2: + continue + fid, text = parts[0].strip(), parts[-1] + p = os.path.join(d, fid + ".wav") + if os.path.exists(p): + spk = "_".join(fid.split("_")[:2]) + yield dict(id=f"{prefix}_{fid}", ds=prefix, lang=lang, spk=f"{prefix}_{spk}", + gender="female" if fid[2] == "f" else "male", split=hsplit(fid), text=text), ("path", p) + + +def items_bible(lang_dir, lang, ds, cap_h=None): + root = f"{RAW}/bible/{lang_dir}" + auds = sorted(glob.glob(f"{root}/**/*.flac", recursive=True) + glob.glob(f"{root}/**/*.wav", recursive=True)) + if cap_h: # deterministic random subset of the train split, roughly cap_h hours (verse ~ 9 s on average) + auds.sort(key=lambda p: hashlib.md5(p.encode()).hexdigest()) + tot = 0.0 + for p in auds: + txt = os.path.splitext(p)[0] + ".txt" + if not os.path.exists(txt): + continue + split = "test" if "/test/" in p else ("val" if "/dev/" in p else "train") + if cap_h and split == "train": + if tot > cap_h * 3600: + continue + try: + tot += sf.info(p).duration + except Exception: + continue + yield dict(id=f"{ds}_{os.path.splitext(os.path.basename(p))[0]}", ds=ds, lang=lang, spk=ds, gender="", + split=split, text=open(txt, encoding="utf-8").read()), ("path", p) + + +def enum(ds): + return {"waxal": items_waxal, "fleurs": items_fleurs, + "slr86": lambda: items_slr(86, "yor", "slr86"), "slr70": lambda: items_slr(70, "eng", "slr70"), + "bible_yor": lambda: items_bible("yoruba", "yor", "bible_yor"), + "bible_hau": lambda: items_bible("hausa", "hau", "bible_hau", BIBLE_HAU_CAP_H)}[ds]() + + +# ------------------------------------------------------------------ workers +def load(arg): + meta, src = arg + try: + if src[0] == "bytes": + y, sr = sf.read(io.BytesIO(src[1]), dtype="float32", always_2d=True); y = y.mean(1) + elif src[0] == "path": + y, sr = sf.read(src[1], dtype="float32", always_2d=True); y = y.mean(1) + else: + pcm = np.memmap(src[1], dtype=np.int16, mode="r") + y, sr = pcm[src[2]:src[2] + src[3]].astype(np.float32) / 32768.0, 16000 + meta["text"] = clean_text(meta["text"]) + meta["sr_in"] = int(sr) + if not meta["text"] or len(y) < 0.5 * sr: + return meta, None, None, "empty" + y24 = librosa.resample(y, orig_sr=sr, target_sr=SR, res_type="soxr_hq") if sr != SR else y + y16 = librosa.resample(y, orig_sr=sr, target_sr=16000, res_type="soxr_hq") if sr != 16000 else y + return meta, y16, y24, None + except Exception as e: # noqa + return meta, None, None, f"err:{type(e).__name__}" + + +def plan_segments(words, ali, dur): + """word-index ranges [(a, b)] (inclusive). Short clips: one segment. Long clips: whole sentences grouped + into 3-14 s segments; an over-long sentence is cut at clause/comma marks.""" + n = len(words) + if dur <= 20.0: + return [(0, n - 1)] + sents, a = [], 0 + for i, w in enumerate(words): + if w["punct"] == "sent" or i == n - 1: + sents.append((a, i)); a = i + 1 + pieces = [] + for a, b in sents: + if ali[b]["end"] - ali[a]["start"] <= 16.0: + pieces.append((a, b)); continue + s = a + for i in range(a, b + 1): + if (words[i]["punct"] in ("clause", "comma") and ali[i]["end"] - ali[s]["start"] > 3.0) or i == b: + pieces.append((s, i)); s = i + 1 + segs, cur = [], None + for a, b in pieces: + if cur is None: + cur = [a, b]; continue + if ali[b]["end"] - ali[cur[0]]["start"] <= 14.0 and ali[cur[1]]["end"] - ali[cur[0]]["start"] < 4.0: + cur[1] = b + else: + segs.append(tuple(cur)); cur = [a, b] + if cur: + segs.append(tuple(cur)) + return segs + + +def post(arg): + meta, y24, words, ali = arg + out, why = [], {} + segs = plan_segments(words, ali, len(y24) / SR) + from da.p2common import silence_mask + sil = silence_mask(y24, SR) + for k, (a, b) in enumerate(segs): + sc = float(np.mean([ali[i]["score"] for i in range(a, b + 1)])) + maxw = max(ali[i]["end"] - ali[i]["start"] for i in range(a, b + 1)) + text = " ".join(words[i]["word"] for i in range(a, b + 1)) + if sc < 0.30: + why["low_score"] = why.get("low_score", 0) + 1; continue + if maxw > 2.5: + why["long_word"] = why.get("long_word", 0) + 1; continue + lo = ali[a - 1]["end"] + 0.02 if a > 0 else 0.0 + hi = ali[b + 1]["start"] - 0.02 if b + 1 < len(ali) else None + y, pauses = normalise_pauses(y24, SR, words, ali, a, b, sil=sil, lo_s=lo, hi_s=hi) + dur = len(y) / SR + cps = len(text) / dur + if not (0.8 <= dur <= 20.0): + why["dur"] = why.get("dur", 0) + 1; continue + if not (4.0 <= cps <= 25.0): + why["cps"] = why.get("cps", 0) + 1; continue + rms = np.sqrt((y ** 2).mean() + 1e-12) + y = y * (0.07 / rms) + pk = np.abs(y).max() + if pk > 0.99: + y = y * (0.99 / pk) + m = dict(meta) + if len(segs) > 1: + m["id"] = f"{meta['id']}_s{k:03d}"; m["src_id"] = meta["id"]; m["longform"] = True + m.update(text=text, dur=round(dur, 3), ascore=round(sc, 3), cps=round(cps, 2), + pauses=[round(p, 2) for p in pauses], + puncts=[words[i]["punct"] for i in range(a, b)], flow_ok=meta["ds"] in FLOW_OK) + out.append((m, np.clip(y * 32767, -32768, 32767).astype(np.int16))) + return out, why + + +def run(ds): + od = f"{OUT}/{ds}" + os.makedirs(od, exist_ok=True) + al = Aligner() + stats = dict(src_items=0, src_hours=0.0, kept=0, hours=0.0, rej={}, longform_src=0, longform_hours_in=0.0, + longform_segments=0) + t0 = time.time(); off = 0 + pending = [] + with open(f"{od}/pcm24k.i16", "wb") as pf, open(f"{od}/meta.jsonl", "w", encoding="utf-8") as jf, \ + Pool(20) as lp, Pool(6) as pp: + def drain(block=False): + nonlocal off + keep = [] + for r in pending: + if block or r.ready(): + segs, why = r.get() + for k, v in why.items(): + stats["rej"][k] = stats["rej"].get(k, 0) + v + for m, pcm in segs: + m["off24"] = off; m["n24"] = int(len(pcm)) + pf.write(pcm.tobytes()); off += len(pcm) + jf.write(json.dumps(m, ensure_ascii=False) + "\n") + stats["kept"] += 1; stats["hours"] += m["dur"] / 3600 + if m.get("longform"): + stats["longform_segments"] += 1 + else: + keep.append(r) + pending[:] = keep + import itertools + src = enum(ds) + if os.environ.get("LIMIT"): + src = itertools.islice(src, 0, None, int(os.environ.get("STRIDE", "1"))) + src = itertools.islice(src, int(os.environ["LIMIT"])) + for meta, y16, y24, err in lp.imap(load, src, chunksize=2): + stats["src_items"] += 1 + if err: + stats["rej"][err] = stats["rej"].get(err, 0) + 1; continue + d = len(y24) / SR + stats["src_hours"] += d / 3600 + if d > 20: + stats["longform_src"] += 1; stats["longform_hours_in"] += d / 3600 + if d > 2400: + stats["rej"]["too_long"] = stats["rej"].get("too_long", 0) + 1; continue + words = words_for_align(meta["text"]) + ali = al.align(y16, words) if words else None + if ali is None: + stats["rej"]["align_fail"] = stats["rej"].get("align_fail", 0) + 1; continue + pending.append(pp.apply_async(post, ((meta, y24, words, ali),))) + if len(pending) > 64: + drain(block=False) + while len(pending) > 128: + time.sleep(0.05); drain(block=False) + if stats["src_items"] % 2000 == 0: + print(ds, stats["src_items"], "kept", stats["kept"], "h", round(stats["hours"], 2), + "rej", stats["rej"], round(time.time() - t0), "s", flush=True) + drain(block=True) + stats = {k: (round(v, 3) if isinstance(v, float) else v) for k, v in stats.items()} + json.dump(stats, open(f"{od}/stats.json", "w"), indent=1) + print(ds, "PREP2_DONE", json.dumps(stats), round(time.time() - t0), "s", flush=True) + + +if __name__ == "__main__": + for ds in sys.argv[1:]: + run(ds) diff --git a/code/da/s3_2.py b/code/da/s3_2.py new file mode 100644 index 0000000000000000000000000000000000000000..9d80237357289ecab7c86de4e6baefb94bb14a4d --- /dev/null +++ b/code/da/s3_2.py @@ -0,0 +1,130 @@ +"""Phase 2: S3 speech tokens (speech_tokenizer_v2.onnx, CUDA EP) + CAM++ speaker embeddings for ~/data2/, +plus code-switched training pairs (ds "cs_pairs"). + + python -m da.s3_2 tok waxal fleurs ... -> s3tok.i32, s3tok.jsonl, campplus.npy + python -m da.s3_2 cs 4000 -> ~/data2/cs_pairs/{pcm24k.i16, meta.jsonl} (+ run tok on it) +""" +import os, sys, json, random, hashlib +import numpy as np, librosa, torch +from multiprocessing import Pool + +D2 = os.path.expanduser(os.environ.get("P2OUT", "~/data2")) +MD = os.path.expanduser("~/models/CosyVoice2-0.5B") +_S = {} +_FB = torch.from_numpy(librosa.filters.mel(sr=16000, n_fft=400, n_mels=128)).float() +_WIN = torch.hann_window(400) + + +def log_mel_128(a): + st = torch.stft(a, 400, 160, window=_WIN, return_complex=True) + mag = st[..., :-1].abs() ** 2 + lm = torch.clamp(_FB @ mag, min=1e-10).log10() + lm = torch.maximum(lm, lm.max() - 8.0) + return (lm + 4.0) / 4.0 + + +def sess(name, gpu): + if name not in _S: + import onnxruntime as ort + torch.set_num_threads(1) + o = ort.SessionOptions(); o.intra_op_num_threads = 2; o.inter_op_num_threads = 1 + prov = ["CUDAExecutionProvider", "CPUExecutionProvider"] if gpu else ["CPUExecutionProvider"] + _S[name] = ort.InferenceSession(f"{MD}/{name}", sess_options=o, providers=prov) + return _S[name] + + +def work(args): + path, off, n = args + import torchaudio.compliance.kaldi as kaldi + pcm = np.memmap(path, dtype=np.int16, mode="r") + y24 = pcm[off:off + n].astype(np.float32) / 32768.0 + y16 = librosa.resample(y24, orig_sr=24000, target_sr=16000, res_type="soxr_hq") + a = torch.from_numpy(y16) + f = log_mel_128(a)[None] + s = sess("speech_tokenizer_v2.onnx", True) + ins = s.get_inputs() + tok = s.run(None, {ins[0].name: f.numpy(), ins[1].name: np.array([f.shape[2]], dtype=np.int32)})[0] + fb = kaldi.fbank(a[None], num_mel_bins=80, dither=0, sample_frequency=16000) + fb = fb - fb.mean(0, keepdim=True) + c = sess("campplus.onnx", False) + emb = c.run(None, {c.get_inputs()[0].name: fb[None].numpy()})[0].reshape(-1).astype(np.float32) + return np.asarray(tok).reshape(-1).astype(np.int32), emb + + +def tok(ds): + od = f"{D2}/{ds}" + meta = [json.loads(l) for l in open(f"{od}/meta.jsonl", encoding="utf-8")] + jobs = [(f"{od}/pcm24k.i16", m["off24"], m["n24"]) for m in meta] + toff, embs = 0, [] + with open(f"{od}/s3tok.i32", "wb") as tf, open(f"{od}/s3tok.jsonl", "w") as jf, Pool(12) as pool: + for i, (m, (t, e)) in enumerate(zip(meta, pool.imap(work, jobs, chunksize=4))): + tf.write(t.tobytes()); embs.append(e) + jf.write(json.dumps({"id": m["id"], "toff": toff, "ntok": int(len(t))}) + "\n") + toff += len(t) + if i % 5000 == 0: + print(ds, i, len(meta), "tok/s", round(len(t) / max(m["dur"], 1e-3), 1), flush=True) + np.save(f"{od}/campplus.npy", np.stack(embs)) + print(ds, "S3_DONE", toff, flush=True) + + +def build_cs(n_pairs=4000, seed=0): + """Join two train utterances in different languages (prefer same gender) into one code-switched item. + Junction silence follows the first clip's final punctuation (sentence 0.45 s, comma 0.25 s, else 0.20 s).""" + rng = random.Random(seed) + pools = {} + for ds in ["waxal", "slr70", "slr86", "fleurs"]: + p = f"{D2}/{ds}/meta.jsonl" + if not os.path.exists(p): + continue + for l in open(p, encoding="utf-8"): + m = json.loads(l) + if m["split"] != "train" or not (1.0 <= m["dur"] <= 6.5): + continue + if ds == "fleurs" and m["lang"] != "eng": + continue + pools.setdefault(m["lang"], []).append(m) + langs = sorted(pools) + print("cs pools", {l: len(v) for l, v in pools.items()}) + od = f"{D2}/cs_pairs"; os.makedirs(od, exist_ok=True) + mm = {} + off = 0 + with open(f"{od}/pcm24k.i16", "wb") as pf, open(f"{od}/meta.jsonl", "w", encoding="utf-8") as jf: + for k in range(n_pairs): + l1, l2 = rng.sample(langs, 2) + if rng.random() < 0.5 and "eng" in langs and "eng" not in (l1, l2): + l2 = "eng" # English is the most common partner in real Nigerian code-switching + a = rng.choice(pools[l1]) + g = (a.get("gender") or "").lower()[:1] + cand = [b for b in rng.sample(pools[l2], min(50, len(pools[l2]))) if (b.get("gender") or "").lower()[:1] == g] + b = cand[0] if cand else rng.choice(pools[l2]) + ys = [] + for m in (a, b): + if m["ds"] not in mm: + mm[m["ds"]] = np.memmap(f"{D2}/{m['ds']}/pcm24k.i16", dtype=np.int16, mode="r") + ys.append(np.asarray(mm[m["ds"]][m["off24"]:m["off24"] + m["n24"]])) + last = a["text"].rstrip()[-1:] + junc = 0.45 if last in ".?!" else (0.25 if last in ",;:" else 0.20) + have = 0.18 + 0.12 + y1 = ys[0] + if junc < have: + y1 = y1[: len(y1) - int((have - junc) * 24000)] + gap = np.zeros(0, np.int16) + else: + gap = np.zeros(int((junc - have) * 24000), np.int16) + y = np.concatenate([y1, gap, ys[1]]) + text = f"<{l1}>{a['text']} <{l2}>{b['text']}" + m = dict(id=f"cs_{k:05d}", ds="cs_pairs", lang="cs", langs=[l1, l2], spk=a["spk"], gender=a.get("gender", ""), + split="train", text=text, dur=round(len(y) / 24000, 3), off24=off, n24=int(len(y)), + src=[a["id"], b["id"]], flow_ok=False) + pf.write(y.tobytes()); off += len(y) + jf.write(json.dumps(m, ensure_ascii=False) + "\n") + print("cs pairs", n_pairs, "hours", round(off / 24000 / 3600, 2), flush=True) + + +if __name__ == "__main__": + if sys.argv[1] == "tok": + for ds in sys.argv[2:]: + tok(ds) + elif sys.argv[1] == "cs": + build_cs(int(sys.argv[2])) + tok("cs_pairs") diff --git a/code/da/text.py b/code/da/text.py new file mode 100644 index 0000000000000000000000000000000000000000..69c4f418527bad9797bf78c1ea206c675f0bc044 --- /dev/null +++ b/code/da/text.py @@ -0,0 +1,60 @@ +"""Text front-end: Unicode NFC, tone diacritics preserved, UTF-8 byte tokens with per-token language ids. + +Code-switched input uses inline tags: "Mo fẹ́ lọ sí ọjà to buy some bread" +Untagged text takes the default language. Each byte inherits the language of the span it is in. +""" +import re +import unicodedata + +LANGS = ["eng", "ibo", "hau", "yor", "pcm"] +LANG2ID = {l: i for i, l in enumerate(LANGS)} +PAD, BOS, EOS = 0, 1, 2 # byte b -> token b + 3 +VOCAB = 256 + 3 +_TAG = re.compile(r"<(eng|ibo|hau|yor|pcm)>(.*?)", re.S) + + +def normalize(t: str) -> str: + t = unicodedata.normalize("NFC", t) + t = t.replace("\u2019", "'").replace("\u2018", "'").replace("\u201c", '"').replace("\u201d", '"') + return re.sub(r"\s+", " ", t).strip() + + +def segments(text: str, default_lang: str = "eng"): + """-> list of (lang, normalized_text) spans, order preserved.""" + out, pos = [], 0 + for m in _TAG.finditer(text): + pre = text[pos:m.start()] + if pre.strip(): + out.append((default_lang, pre)) + out.append((m.group(1), m.group(2))) + pos = m.end() + if text[pos:].strip(): + out.append((default_lang, text[pos:])) + # keep a single space between spans + res = [] + for i, (l, s) in enumerate(out): + s = normalize(s) + if not s: + continue + if res: + s = " " + s + res.append((l, s)) + return res + + +def encode(text: str, default_lang: str = "eng"): + """-> (token ids, language ids) incl. BOS/EOS. BOS takes the first span's language, EOS the last.""" + segs = segments(text, default_lang) + toks, langs = [BOS], [LANG2ID[segs[0][0]] if segs else LANG2ID[default_lang]] + for l, s in segs: + b = s.encode("utf-8") + toks += [x + 3 for x in b] + langs += [LANG2ID[l]] * len(b) + toks.append(EOS) + langs.append(langs[-1]) + return toks, langs + + +def plain(text: str) -> str: + """Tagged text -> plain text (for ASR references).""" + return " ".join(s.strip() for _, s in segments(text)) diff --git a/code/train_b.py b/code/train_b.py new file mode 100644 index 0000000000000000000000000000000000000000..bb5954ee7612c57907ab37acf5a0775b15c812d1 --- /dev/null +++ b/code/train_b.py @@ -0,0 +1,353 @@ +"""Arm B: CosyVoice2-0.5B (Apache-2.0) fine-tune with the Zhang et al. 2019 recipe added to its text->speech-token LM. + +Additions (new, ours): + * per-token language embedding added to the Qwen text-token embeddings (zero-init) + * learned speaker embedding inserted as one token AFTER the text, before the task token (zero-init), so text + positions cannot see it; speech positions attend to it (anchor-voice conditioning) + * domain-adversarial speaker classifier with gradient reversal on the LM hidden states at text positions +Flow-matching decoder + HiFT vocoder stay frozen; at inference they are prompted with an anchor-voice clip. + + python train_b.py train --out ~/runs/b1 --minutes 30 + python train_b.py synth --ckpt ~/runs/b1/last.pt --out ~/eval/sys/armB_ft + python train_b.py zeroshot --out ~/eval/sys/cosyvoice2_zeroshot (pretrained model, cross-lingual, no fine-tune) +""" +import argparse, inspect, json, math, os, random, sys, time +import numpy as np, torch, torch.nn as nn, torch.nn.functional as F, soundfile as sf, librosa + +HOME = os.path.expanduser("~") +sys.path[:0] = [f"{HOME}/CosyVoice", f"{HOME}/CosyVoice/third_party/Matcha-TTS"] +MODEL_DIR = f"{HOME}/models/CosyVoice2-0.5B" +LANGS = ["eng", "ibo", "hau", "yor", "pcm"] +L2I = {l: i for i, l in enumerate(LANGS)} +sys.path.insert(0, f"{HOME}/da") +from da.text import segments # noqa: E402 + +from da.model import grad_reverse # noqa: E402 + + +def _sf_load_wav(wav, target_sr, min_sr=16000): + """soundfile replacement for cosyvoice load_wav (torchaudio>=2.9 load needs torchcodec).""" + y, sr = sf.read(wav, dtype="float32", always_2d=True) + y = y.mean(1) + if sr != target_sr: + y = librosa.resample(y, orig_sr=sr, target_sr=target_sr) + return torch.from_numpy(y)[None] + + +def load_cosy(): + import cosyvoice.utils.file_utils as fu + fu.load_wav = _sf_load_wav + import cosyvoice.cli.frontend as cf + cf.load_wav = _sf_load_wav + from cosyvoice.cli.cosyvoice import CosyVoice2 + kw = dict(load_jit=False, load_trt=False, fp16=False) + sig = inspect.signature(CosyVoice2.__init__).parameters + kw = {k: v for k, v in kw.items() if k in sig} + return CosyVoice2(MODEL_DIR, **kw) + + +def text_tokens(tok, text, default="eng"): + """tagged text -> (qwen ids, per-token lang ids).""" + ids, langs = [], [] + for l, s in segments(text, default): + t = tok(s, add_special_tokens=False)["input_ids"] + ids += t; langs += [L2I[l]] * len(t) + return ids, langs + + +class DALM(nn.Module): + def __init__(self, qlm, n_spk, d=896): + super().__init__() + self.q = qlm # cosyvoice Qwen2LM + self.lang_emb = nn.Embedding(len(LANGS), d); nn.init.zeros_(self.lang_emb.weight) + self.spk_emb = nn.Embedding(n_spk, d); nn.init.normal_(self.spk_emb.weight, std=0.02) + self.adv = nn.Sequential(nn.Linear(d, 256), nn.ReLU(), nn.Linear(256, n_spk)) + self.V = qlm.speech_token_size + + def embed_text(self, ids, langs): + return self.q.llm.model.model.embed_tokens(ids) + self.lang_emb(langs) + + def build(self, ids, langs, spk, speech=None): + """one sequence: [sos, text+lang, spk, task, speech...]""" + sos = self.q.llm_embedding.weight[self.q.sos][None] + task = self.q.llm_embedding.weight[self.q.task_id][None] + parts = [sos, self.embed_text(ids, langs), self.spk_emb(spk)[None], task] + if speech is not None: + parts.append(self.q.speech_embedding(speech)) + return torch.cat(parts, 0) + + def hidden(self, x, mask): + out = self.q.llm.model.model(inputs_embeds=x, attention_mask=mask, return_dict=True) + return out.last_hidden_state + + def forward(self, batch, adv_lambda=1.0): + seqs, tgts, tpos = [], [], [] + for ids, langs, spk, sp in batch: + x = self.build(ids, langs, spk, sp) + n_text = len(ids) + tgt = torch.full((x.shape[0],), -100, device=x.device, dtype=torch.long) + # position of task token = 1 + n_text + 1; it predicts speech[0]; last speech pos predicts EOS + start = 1 + n_text + 1 + tgt[start: start + len(sp)] = sp + tgt[start + len(sp)] = self.V # EOS + seqs.append(x); tgts.append(tgt); tpos.append((1, 1 + n_text)) + T = max(s.shape[0] for s in seqs) + 1 + D = seqs[0].shape[1] + X = seqs[0].new_zeros(len(seqs), T, D) + Y = torch.full((len(seqs), T), -100, device=X.device, dtype=torch.long) + M = torch.zeros(len(seqs), T, device=X.device, dtype=torch.long) + for i, (s, t) in enumerate(zip(seqs, tgts)): + X[i, :len(s)] = s; Y[i, :len(t)] = t[:len(s)] if len(t) > len(s) else t; M[i, :len(s)] = 1 + # targets were laid out on positions; shift is implicit (position p predicts tgt[p]) + H = self.hidden(X, M) + logits = self.q.llm_decoder(H) + ce = F.cross_entropy(logits.float().reshape(-1, logits.shape[-1]), Y.reshape(-1), ignore_index=-100) + valid = Y != -100 + acc = (logits.argmax(-1)[valid] == Y[valid]).float().mean() + # adversarial speaker classifier on text positions + hs, ys = [], [] + for i, (a, b) in enumerate(tpos): + hs.append(H[i, a:b]); ys.append(torch.full((b - a,), int(batch[i][2]), device=X.device)) + hs = torch.cat(hs); ys = torch.cat(ys) + al = self.adv(grad_reverse(hs.float(), adv_lambda)) + adv = F.cross_entropy(al, ys) + adv_acc = (al.argmax(-1) == ys).float().mean() + return ce, acc, adv, adv_acc + + @torch.no_grad() + def generate(self, ids, langs, spk, top_p=0.8, top_k=25, win=10, tau_r=0.1, seed=0): + g = torch.Generator(device=ids.device).manual_seed(seed) + x = self.build(ids, langs, spk)[None] + n = len(ids) + min_len, max_len = int(n * 2), int(n * 20) + past, out = None, [] + inp = x + for i in range(max_len): + o = self.q.llm.model.model(inputs_embeds=inp, past_key_values=past, use_cache=True, return_dict=True) + past = o.past_key_values + logp = self.q.llm_decoder(o.last_hidden_state[:, -1]).float().log_softmax(-1)[0] + if i < min_len: + logp[self.V] = -float("inf") + logp[self.V + 1:] = -float("inf") + p = logp.exp() + sp, si = p.sort(descending=True) + keep = (sp.cumsum(0) - sp) < top_p + keep[top_k:] = False + cand_p, cand_i = sp[keep], si[keep] + tokid = int(cand_i[torch.multinomial(cand_p / cand_p.sum(), 1, generator=g)]) + if out and sum(1 for t in out[-win:] if t == tokid) >= win * tau_r and tokid != self.V: # RAS + tokid = int(torch.multinomial(p, 1, generator=g)) + if tokid >= self.V: + break + out.append(tokid) + inp = self.q.speech_embedding.weight[tokid][None, None] + return out + + +# ------------------------------------------------------------------ data +def load_items(root, datasets, split, max_tok=500): + items, mm = [], {} + for ds in datasets: + if not os.path.exists(f"{root}/{ds}/s3tok.jsonl"): + continue + mm[ds] = np.memmap(f"{root}/{ds}/s3tok.i32", dtype=np.int32, mode="r") + tk = {} + for l in open(f"{root}/{ds}/s3tok.jsonl"): + r = json.loads(l); tk[r["id"]] = (r["toff"], r["ntok"]) + for l in open(f"{root}/{ds}/meta.jsonl", encoding="utf-8"): + m = json.loads(l) + if m["split"] != split or m["id"] not in tk: + continue + m["toff"], m["ntok"] = tk[m["id"]] + if 5 < m["ntok"] <= max_tok: + items.append(m) + return items, mm + + +def batches(items, budget, weights, rng): + idx = [] + for i, m in enumerate(items): + w = weights.get(m["ds"], 1.0) + idx += [i] * int(w) + ([i] if rng.random() < w - int(w) else []) + idx.sort(key=lambda i: items[i]["ntok"] + rng.random() * 10) + out, cur, mx = [], [], 0 + for i in idx: + L = items[i]["ntok"] + len(items[i]["text"]) // 2 + 4 + if cur and max(mx, L) * (len(cur) + 1) > budget: + out.append(cur); cur, mx = [], 0 + cur.append(i); mx = max(mx, L) + if cur: + out.append(cur) + rng.shuffle(out) + return out + + +def train(a): + from transformers import AutoTokenizer + tok = AutoTokenizer.from_pretrained(f"{MODEL_DIR}/CosyVoice-BlankEN") + cosy = load_cosy() + items, mm = load_items(a.data, a.datasets.split(","), "train") + val, _ = load_items(a.data, a.datasets.split(","), "val") + spks = sorted({m["spk"] for m in items}); spk2id = {s: i for i, s in enumerate(spks)} + val = [m for m in val if m["spk"] in spk2id][:200] + os.makedirs(a.out, exist_ok=True) + json.dump(spk2id, open(f"{a.out}/spk2id.json", "w")) + print("armB items", len(items), "hours", round(sum(m["dur"] for m in items) / 3600, 1), "spk", len(spks), flush=True) + model = DALM(cosy.model.llm, len(spks)).cuda().train() + for p in cosy.model.flow.parameters(): + p.requires_grad = False + for p in cosy.model.hift.parameters(): + p.requires_grad = False + new = list(model.lang_emb.parameters()) + list(model.spk_emb.parameters()) + list(model.adv.parameters()) + newids = {id(p) for p in new} + old = [p for p in model.parameters() if id(p) not in newids and p.requires_grad] + opt = torch.optim.AdamW([{"params": old, "lr": a.lr}, {"params": new, "lr": a.lr_new}], weight_decay=0.01) + base = [a.lr, a.lr_new] + cache = {} + + def enc(m): + if m["id"] not in cache: + cache[m["id"]] = text_tokens(tok, f"<{m['lang']}>{m['text']}") + ids, langs = cache[m["id"]] + sp = np.asarray(mm[m["ds"]][m["toff"]: m["toff"] + m["ntok"]], dtype=np.int64) + return (torch.tensor(ids).cuda(), torch.tensor(langs).cuda(), torch.tensor(spk2id[m["spk"]]).cuda(), + torch.from_numpy(sp).cuda()) + + rng = random.Random(0); step = 0; nskip = 0; t0 = time.time(); logf = open(f"{a.out}/log.jsonl", "a") + + def validate(): + model.eval(); L = [] + with torch.no_grad(), torch.autocast("cuda", dtype=torch.bfloat16): + for i in range(0, len(val), 16): + ce, acc, _, _ = model([enc(m) for m in val[i:i + 16]]); L.append(float(ce)) + model.train(); return float(np.mean(L)) + + v0 = validate(); print(json.dumps({"step": 0, "val_ce": v0}), flush=True); logf.write(json.dumps({"step": 0, "val_ce": v0}) + "\n") + done = False + while not done: + for bi in batches(items, a.tokens, {"waxal": a.waxal_w}, rng): + lrm = min(1.0, (step + 1) / 200) + for g, b in zip(opt.param_groups, base): + g["lr"] = b * lrm + with torch.autocast("cuda", dtype=torch.bfloat16): + ce, acc, adv, adv_acc = model([enc(items[i]) for i in bi]) + loss = ce + a.adv_w * adv + opt.zero_grad(set_to_none=True) + if not torch.isfinite(loss): + nskip += 1 + print(json.dumps({"skip_nonfinite_fwd": step, "ce": float(ce), "adv": float(adv), "B": len(bi), + "maxtok": max(items[i]["ntok"] for i in bi)}), flush=True) + continue + loss.backward() + gn = torch.nn.utils.clip_grad_norm_([p for p in model.parameters() if p.requires_grad], 1.0) + if not torch.isfinite(gn): + nskip += 1; opt.zero_grad(set_to_none=True) + print(json.dumps({"skip_nonfinite_grad": step}), flush=True); continue + opt.step(); step += 1 + if step % 25 == 0: + r = dict(step=step, ce=round(float(ce), 4), acc=round(float(acc), 4), adv=round(float(adv), 3), + adv_acc=round(float(adv_acc), 3), gn=round(float(gn), 2), B=len(bi), min=round((time.time() - t0) / 60, 2)) + print(json.dumps(r), flush=True); logf.write(json.dumps(r) + "\n"); logf.flush() + if step % 500 == 0: + v = validate(); print(json.dumps({"step": step, "val_ce": v}), flush=True) + logf.write(json.dumps({"step": step, "val_ce": v}) + "\n"); logf.flush() + if (time.time() - t0) / 60 > a.minutes: + done = True; break + v = validate(); logf.write(json.dumps({"step": step, "val_ce": v, "final": True}) + "\n") + sd = {k: v for k, v in model.state_dict().items()} + torch.save(dict(model=sd, step=step, n_spk=len(spks)), f"{a.out}/last.pt") + print("TRAIN_B_DONE", step, v, flush=True) + + +# ------------------------------------------------------------------ inference +def prompt_feats(cosy, wav_path): + """flow prompt (speech tokens, 24k mel feat, campplus embedding) from an anchor clip.""" + fe = cosy.frontend + tokn, tokl = fe._extract_speech_token(wav_path) + feat, featl = fe._extract_speech_feat(wav_path) + # CosyVoice2 aligns: feat frames = 2 * tokens + n = min(feat.shape[1] // 2, tokn.shape[1]) + feat, tokn = feat[:, :2 * n], tokn[:, :n] + emb = fe._extract_spk_embedding(wav_path) + return tokn.cuda(), feat.cuda(), emb.cuda() + + +@torch.no_grad() +def token2wav(cosy, tokens, ptok, pfeat, emb): + flow = cosy.model.flow + t = torch.tensor([tokens], dtype=torch.int32).cuda() + kw = dict(token=t, token_len=torch.tensor([t.shape[1]], dtype=torch.int32).cuda(), + prompt_token=ptok, prompt_token_len=torch.tensor([ptok.shape[1]], dtype=torch.int32).cuda(), + prompt_feat=pfeat, prompt_feat_len=torch.tensor([pfeat.shape[1]], dtype=torch.int32).cuda(), + embedding=emb) + sig = inspect.signature(flow.inference).parameters + if "streaming" in sig: + kw["streaming"] = False + if "finalize" in sig: + kw["finalize"] = True + if "flow_cache" in sig: + kw["flow_cache"] = torch.zeros(1, 80, 0, 2).cuda() + mel = flow.inference(**kw) + mel = mel[0] if isinstance(mel, tuple) else mel + hs = inspect.signature(cosy.model.hift.inference).parameters + w = cosy.model.hift.inference(speech_feat=mel, **({"cache_source": torch.zeros(1, 1, 0).cuda()} if "cache_source" in hs else {})) + w = w[0] if isinstance(w, tuple) else w + return w.squeeze().float().cpu().numpy() + + +def eval_rows(): + return [json.loads(l) for l in open(f"{HOME}/eval/eval_set.jsonl", encoding="utf-8")] + + +def synth(a): + from transformers import AutoTokenizer + tok = AutoTokenizer.from_pretrained(f"{MODEL_DIR}/CosyVoice-BlankEN") + cosy = load_cosy() + ck = torch.load(a.ckpt, map_location="cuda") + spk2id = json.load(open(os.path.join(os.path.dirname(a.ckpt), "spk2id.json"))) + model = DALM(cosy.model.llm, ck["n_spk"]).cuda().eval() + model.load_state_dict(ck["model"]) + anc = json.load(open(f"{HOME}/eval/anchor.json")) + ptok, pfeat, emb = prompt_feats(cosy, anc["ref_wavs"][0]) + os.makedirs(a.out, exist_ok=True) + for r in eval_rows(): + text = r["text"] if r["lang"] == "cs" else f"<{r['lang']}>{r['text']}" + ids, langs = text_tokens(tok, text) + with torch.autocast("cuda", dtype=torch.bfloat16): + sp = model.generate(torch.tensor(ids).cuda(), torch.tensor(langs).cuda(), torch.tensor(spk2id[anc["spk"]]).cuda()) + sf.write(f"{a.out}/{r['id']}.wav", token2wav(cosy, sp, ptok, pfeat, emb), cosy.sample_rate) + print("synth_b done", a.out, flush=True) + + +def zeroshot(a): + """Unmodified pretrained CosyVoice2, cross-lingual mode with the anchor prompt (reference point).""" + cosy = load_cosy() + anc = json.load(open(f"{HOME}/eval/anchor.json")) + from cosyvoice.utils.file_utils import load_wav + pw = anc["ref_wavs"][0] + sig = inspect.signature(cosy.inference_cross_lingual).parameters + os.makedirs(a.out, exist_ok=True) + for r in eval_rows(): + text = " ".join(s.strip() for _, s in segments(r["text"])) if r["lang"] == "cs" else r["text"] + prompt = pw + outs = [o["tts_speech"] for o in cosy.inference_cross_lingual(text, prompt, stream=False)] + w = torch.cat(outs, 1).squeeze().cpu().numpy() + sf.write(f"{a.out}/{r['id']}.wav", w, cosy.sample_rate) + print("zeroshot done", flush=True) + + +if __name__ == "__main__": + ap = argparse.ArgumentParser() + ap.add_argument("cmd") + ap.add_argument("--data", default=f"{HOME}/data") + ap.add_argument("--datasets", default="waxal,fleurs,slr86,slr70,bible_yor") + ap.add_argument("--out", default=f"{HOME}/runs/b1") + ap.add_argument("--ckpt", default="") + ap.add_argument("--minutes", type=float, default=30) + ap.add_argument("--lr", type=float, default=1e-5) + ap.add_argument("--lr_new", type=float, default=1e-3) + ap.add_argument("--tokens", type=int, default=12000) + ap.add_argument("--waxal_w", type=float, default=2.0) + ap.add_argument("--adv_w", type=float, default=0.02) + a = ap.parse_args() + {"train": train, "synth": synth, "zeroshot": zeroshot}[a.cmd](a) diff --git a/code/train_b2.py b/code/train_b2.py new file mode 100644 index 0000000000000000000000000000000000000000..83c1cd511d4124a4a13b33d8af8f95e0281b13e8 --- /dev/null +++ b/code/train_b2.py @@ -0,0 +1,590 @@ +"""Arm B, Phase 2. CosyVoice2-0.5B (Apache-2.0) + language embeddings + speaker token + GRL speaker classifier. + +New in Phase 2 + * data from ~/data2 (24 kHz, forced-aligned, pause-normalised; long WAXAL clips segmented; BibleTTS Hausa; CS pairs) + * in-context prompts during LM training (p=0.5): [sos, prompt_text+text, spk, task, prompt_speech, speech] with + loss on the target speech only -> at inference the anchor clip steers prosody and pause style + * keep-best on validation CE (every --val_every steps), cosine LR, early stop + * flow-matching decoder fine-tune on 24 kHz studio sources (tones / pitch / timing detail) + * synthesis: anchor prompt at LM and flow, sentence-level generation for multi-sentence text with anchor-matched + inter-sentence pauses, N candidates per text (seed + prompt clip vary) + + python train_b2.py lm --out ~/runs/p2_lm --minutes 90 + python train_b2.py flow --out ~/runs/p2_flow --minutes 60 + python train_b2.py synth --ckpt ~/runs/p2_lm/best.pt [--flow ~/runs/p2_flow/best.pt] --out DIR --n 4 +""" +import argparse, inspect, json, math, os, random, re, sys, time +import numpy as np, torch, torch.nn as nn, torch.nn.functional as F, soundfile as sf, librosa + +HOME = os.path.expanduser("~") +sys.path[:0] = [f"{HOME}/CosyVoice", f"{HOME}/CosyVoice/third_party/Matcha-TTS", f"{HOME}/da"] +from train_b import load_cosy, text_tokens, DALM, LANGS, L2I, MODEL_DIR, token2wav # noqa: E402 +from da.model import grad_reverse # noqa: E402 +from da.text import segments # noqa: E402 + +D2 = os.path.expanduser(os.environ.get("P2OUT", f"{HOME}/data2")) +ANCHOR = "waxal_yor_4" + + +# ------------------------------------------------------------------ model +class DALM2(DALM): + def build2(self, ids, langs, spk, sp=None, pids=None, plangs=None, psp=None): + """[sos, (prompt_text) text, spk, task, (prompt_speech) speech] -> embeddings, n_text_total, n_prompt_speech""" + sos = self.q.llm_embedding.weight[self.q.sos][None] + task = self.q.llm_embedding.weight[self.q.task_id][None] + if pids is not None: + ids = torch.cat([pids, ids]); langs = torch.cat([plangs, langs]) + parts = [sos, self.embed_text(ids, langs), self.spk_emb(spk)[None], task] + npsp = 0 + if psp is not None: + parts.append(self.q.speech_embedding(psp)); npsp = len(psp) + if sp is not None: + parts.append(self.q.speech_embedding(sp)) + return torch.cat(parts, 0), len(ids), npsp + + def forward2(self, batch, adv_lambda=1.0): + seqs, tgts, tpos, spks = [], [], [], [] + for b in batch: + ids, langs, spk, sp = b[:4] + pr = b[4] if len(b) > 4 else None + x, nt, npsp = self.build2(ids, langs, spk, sp, *(pr if pr else (None, None, None))) + tgt = torch.full((x.shape[0] + 1,), -100, device=x.device, dtype=torch.long) + start = 1 + nt + 2 + npsp - 1 # position whose output predicts speech[0] + tgt[start: start + len(sp)] = sp + tgt[start + len(sp)] = self.V + seqs.append(x); tgts.append(tgt[:x.shape[0]]); tpos.append((1, 1 + nt)); spks.append(int(spk)) + T = max(s.shape[0] for s in seqs) + D = seqs[0].shape[1] + X = seqs[0].new_zeros(len(seqs), T, D) + Y = torch.full((len(seqs), T), -100, device=X.device, dtype=torch.long) + M = torch.zeros(len(seqs), T, device=X.device, dtype=torch.long) + for i, (s, t) in enumerate(zip(seqs, tgts)): + X[i, :len(s)] = s; Y[i, :len(t)] = t; M[i, :len(s)] = 1 + H = self.hidden(X, M) + logits = self.q.llm_decoder(H) + ce = F.cross_entropy(logits.float().reshape(-1, logits.shape[-1]), Y.reshape(-1), ignore_index=-100) + valid = Y != -100 + acc = (logits.argmax(-1)[valid] == Y[valid]).float().mean() + hs, ys = [], [] + for i, (a, b) in enumerate(tpos): + hs.append(H[i, a:b]); ys.append(torch.full((b - a,), spks[i], device=X.device)) + hs = torch.cat(hs); ys = torch.cat(ys) + al = self.adv(grad_reverse(hs.float(), adv_lambda)) + adv = F.cross_entropy(al, ys) + return ce, acc, adv, (al.argmax(-1) == ys).float().mean() + + @torch.no_grad() + def generate2(self, ids, langs, spk, prompt=None, top_p=0.8, top_k=25, win=10, tau_r=0.1, seed=0, + min_ratio=2.0, max_ratio=20.0): + g = torch.Generator(device=ids.device).manual_seed(seed) + x, _, _ = self.build2(ids, langs, spk, None, *(prompt if prompt else (None, None, None))) + x = x[None] + n = len(ids) + min_len, max_len = int(n * min_ratio), int(n * max_ratio) + past, out, inp = None, [], x + for i in range(max_len): + o = self.q.llm.model.model(inputs_embeds=inp, past_key_values=past, use_cache=True, return_dict=True) + past = o.past_key_values + logp = self.q.llm_decoder(o.last_hidden_state[:, -1]).float().log_softmax(-1)[0] + if i < min_len: + logp[self.V] = -float("inf") + logp[self.V + 1:] = -float("inf") + p = logp.exp() + sp, si = p.sort(descending=True) + keep = (sp.cumsum(0) - sp) < top_p + keep[top_k:] = False + cp, ci = sp[keep], si[keep] + tokid = int(ci[torch.multinomial(cp / cp.sum(), 1, generator=g)]) + if out and sum(1 for t in out[-win:] if t == tokid) >= win * tau_r and tokid != self.V: + tokid = int(torch.multinomial(p, 1, generator=g)) + if tokid >= self.V: + break + out.append(tokid) + inp = self.q.speech_embedding.weight[tokid][None, None] + return out + + +# ------------------------------------------------------------------ data +def load_items(datasets, split, max_tok=500, flow=False): + items, mm = [], {} + for ds in datasets: + od = f"{D2}/{ds}" + if not os.path.exists(f"{od}/s3tok.jsonl"): + print("missing", ds, flush=True); continue + mm[ds] = np.memmap(f"{od}/s3tok.i32", dtype=np.int32, mode="r") + tk = [json.loads(l) for l in open(f"{od}/s3tok.jsonl")] + for k, l in enumerate(open(f"{od}/meta.jsonl", encoding="utf-8")): + m = json.loads(l) + t = tk[k] + assert t["id"] == m["id"] + if m["split"] != split: + continue + if flow and not m.get("flow_ok"): + continue + m["toff"], m["ntok"], m["row"] = t["toff"], t["ntok"], k + if 5 < m["ntok"] <= max_tok: + items.append(m) + return items, mm + + +def tagged(m): + return m["text"] if m["lang"] == "cs" else f"<{m['lang']}>{m['text']}" + + +DS_W = {"waxal": 2.0, "bible_yor": 0.5, "bible_hau": 0.6, "fleurs": 1.0, "slr86": 1.0, "slr70": 1.0, "cs_pairs": 1.0} + + +def batches(items, budget, rng, extra_w=None, cost=None): + idx = [] + for i, m in enumerate(items): + w = DS_W.get(m["ds"], 1.0) * (extra_w(m) if extra_w else 1.0) + idx += [i] * int(w) + ([i] if rng.random() < w - int(w) else []) + cost = cost or (lambda m: m["ntok"] + len(m["text"]) // 2 + 4) + idx.sort(key=lambda i: cost(items[i]) + rng.random() * 10) + out, cur, mx = [], [], 0 + for i in idx: + L = cost(items[i]) + if cur and max(mx, L) * (len(cur) + 1) > budget: + out.append(cur); cur, mx = [], 0 + cur.append(i); mx = max(mx, L) + if cur: + out.append(cur) + rng.shuffle(out) + return out + + +# ------------------------------------------------------------------ LM training +def train_lm(a): + from transformers import AutoTokenizer + tok = AutoTokenizer.from_pretrained(f"{MODEL_DIR}/CosyVoice-BlankEN") + cosy = load_cosy() + dss = a.datasets.split(",") + items, mm = load_items(dss, "train") + val_all, _ = load_items(dss, "val") + if a.anchor_only_w > 0: + print("anchor items", sum(m["spk"] == ANCHOR for m in items), flush=True) + spks = sorted({m["spk"] for m in items}) + if a.init: + ck = torch.load(a.init, map_location="cpu") + spk2id = json.load(open(os.path.join(os.path.dirname(a.init), "spk2id.json"))) + for s in spks: + if s not in spk2id: + spk2id[s] = len(spk2id) + else: + spk2id = {s: i for i, s in enumerate(spks)} + n_spk = max(len(spk2id), ck["n_spk"]) if a.init else len(spk2id) + # balanced val: up to 60 per language (+ anchor), never more than 15 per speaker + rng = random.Random(1); rng.shuffle(val_all) + val, per_l, per_s = [], {}, {} + for m in val_all: + if m["spk"] not in spk2id or m["lang"] == "cs": + continue + L = "anchor" if m["spk"] == ANCHOR else m["lang"] + if per_l.get(L, 0) >= 60 or per_s.get(m["spk"], 0) >= 15: + continue + per_l[L] = per_l.get(L, 0) + 1; per_s[m["spk"]] = per_s.get(m["spk"], 0) + 1; val.append(m) + by_spk = {} + for m in items: + if m["ntok"] <= 160 and m["lang"] != "cs": + by_spk.setdefault(m["spk"], []).append(m) + os.makedirs(a.out, exist_ok=True) + json.dump(spk2id, open(f"{a.out}/spk2id.json", "w")) + print("lm items", len(items), "hours", round(sum(m["dur"] for m in items) / 3600, 1), "spk", len(spk2id), + "val", len(val), per_l, flush=True) + model = DALM2(cosy.model.llm, n_spk).cuda().train() + if a.init: + sd = ck["model"] + if sd["spk_emb.weight"].shape[0] < n_spk: + for k in ["spk_emb.weight"]: + w = model.state_dict()[k].clone(); w[:sd[k].shape[0]] = sd[k]; sd[k] = w + for k in ["adv.2.weight", "adv.2.bias"]: + w = model.state_dict()[k].clone(); w[:sd[k].shape[0]] = sd[k]; sd[k] = w + print(model.load_state_dict(sd, strict=False), flush=True) + for p in cosy.model.flow.parameters(): + p.requires_grad = False + for p in cosy.model.hift.parameters(): + p.requires_grad = False + new = list(model.lang_emb.parameters()) + list(model.spk_emb.parameters()) + list(model.adv.parameters()) + newids = {id(p) for p in new} + old = [p for p in model.parameters() if id(p) not in newids and p.requires_grad] + opt = torch.optim.AdamW([{"params": old, "lr": a.lr}, {"params": new, "lr": a.lr_new}], weight_decay=0.01) + base = [a.lr, a.lr_new] + cache = {} + + def tt(text, key): + if key not in cache: + cache[key] = text_tokens(tok, text) + return cache[key] + + def enc(m, prng=None, force_prompt=None): + ids, langs = tt(tagged(m), m["id"]) + sp = np.asarray(mm[m["ds"]][m["toff"]: m["toff"] + m["ntok"]], dtype=np.int64) + r = [torch.tensor(ids).cuda(), torch.tensor(langs).cuda(), torch.tensor(spk2id[m["spk"]]).cuda(), + torch.from_numpy(sp).cuda()] + use = force_prompt if force_prompt is not None else (prng is not None and prng.random() < a.p_prompt) + if use: + c = by_spk.get(m["spk"], []) + c = [x for x in (prng or random).sample(c, min(6, len(c))) if x["id"] != m["id"]] if c else [] + if c and c[0]["ntok"] + m["ntok"] <= 600: + p = c[0] + pids, plangs = tt(tagged(p), p["id"]) + psp = np.asarray(mm[p["ds"]][p["toff"]: p["toff"] + p["ntok"]], dtype=np.int64) + r.append((torch.tensor(pids).cuda(), torch.tensor(plangs).cuda(), torch.from_numpy(psp).cuda())) + return r + + def validate(): + model.eval(); out = {} + with torch.no_grad(), torch.autocast("cuda", dtype=torch.bfloat16): + for mode in (False, True): + vr = random.Random(7); L, W = [], [] + for i in range(0, len(val), 16): + bt = val[i:i + 16] + ce, _, _, _ = model.forward2([enc(m, vr, force_prompt=mode) for m in bt]) + L.append(float(ce) * len(bt)); W.append(len(bt)) + out["p" if mode else "np"] = sum(L) / sum(W) + # anchor-only val + av = [m for m in val if m["spk"] == ANCHOR] + if av: + vr = random.Random(7) + ce, _, _, _ = model.forward2([enc(m, vr, force_prompt=True) for m in av[:32]]) + out["anchor_p"] = float(ce) + model.train() + out["mean"] = (out["np"] + out["p"]) / 2 + return out + + extra = (lambda m: a.anchor_only_w if m["spk"] == ANCHOR else 1.0) if a.anchor_only_w > 0 else None + rng = random.Random(0); prng = random.Random(3) + step, nskip, t0 = 0, 0, time.time() + logf = open(f"{a.out}/log.jsonl", "a") + v = validate(); best = v["mean"] if not a.select_anchor else 0.5 * (v["mean"] + v.get("anchor_p", v["mean"])); bad = 0 + logf.write(json.dumps({"step": 0, "val": v}) + "\n"); print(json.dumps({"step": 0, "val": v}), flush=True) + torch.save(dict(model=model.state_dict(), step=0, n_spk=n_spk, val=v), f"{a.out}/best.pt") + total = a.max_steps + done = False + while not done: + for bi in batches(items, a.tokens, rng, extra, cost=lambda m: m["ntok"] + len(m["text"]) // 2 + 4 + int(a.p_prompt * 200)): + lrm = min(1.0, (step + 1) / a.warmup) * (0.1 + 0.9 * 0.5 * (1 + math.cos(math.pi * min(1.0, step / total)))) + for g, b in zip(opt.param_groups, base): + g["lr"] = b * lrm + batch = [enc(items[i], prng) for i in bi] + # keep the padded length inside the budget when prompts were added + with torch.autocast("cuda", dtype=torch.bfloat16): + ce, acc, adv, adv_acc = model.forward2(batch, adv_lambda=min(1.0, step / 1000)) + loss = ce + a.adv_w * adv + opt.zero_grad(set_to_none=True) + if not torch.isfinite(loss): + nskip += 1; print(json.dumps({"skip_nonfinite_fwd": step, "ce": float(ce), "adv": float(adv)}), flush=True) + continue + loss.backward() + gn = torch.nn.utils.clip_grad_norm_([p for p in model.parameters() if p.requires_grad], 1.0) + if not torch.isfinite(gn): + nskip += 1; opt.zero_grad(set_to_none=True); print(json.dumps({"skip_nonfinite_grad": step}), flush=True) + continue + opt.step(); step += 1 + if step % 25 == 0: + r = dict(step=step, ce=round(float(ce), 4), acc=round(float(acc), 4), adv=round(float(adv), 3), + adv_acc=round(float(adv_acc), 3), gn=round(float(gn), 2), B=len(bi), lr=round(lrm * a.lr, 9), + skips=nskip, min=round((time.time() - t0) / 60, 2)) + print(json.dumps(r), flush=True); logf.write(json.dumps(r) + "\n"); logf.flush() + if step % a.val_every == 0: + v = validate() + score = v["mean"] if not a.select_anchor else 0.5 * (v["mean"] + v.get("anchor_p", v["mean"])) + imp = score < best - 1e-4 + rec = {"step": step, "val": v, "best": imp} + print(json.dumps(rec), flush=True); logf.write(json.dumps(rec) + "\n"); logf.flush() + if imp: + best, bad = score, 0 + torch.save(dict(model=model.state_dict(), step=step, n_spk=n_spk, val=v), f"{a.out}/best.pt") + else: + bad += 1 + if bad >= a.patience: + print("EARLY_STOP", step, flush=True); done = True; break + if (time.time() - t0) / 60 > a.minutes or step >= total: + done = True; break + print("TRAIN_LM_DONE", step, "best", best, "skips", nskip, flush=True) + + +# ------------------------------------------------------------------ flow training +def train_flow(a): + cosy = load_cosy() + flow = cosy.model.flow + if a.flow_init: + flow.load_state_dict(torch.load(a.flow_init, map_location="cuda")["flow"]) + flow.train() + for p in cosy.model.llm.parameters(): + p.requires_grad = False + fe = cosy.frontend.feat_extractor + dss = a.datasets.split(",") + items, mm = load_items(dss, "train", max_tok=400, flow=True) + val, _ = load_items(dss, "val", max_tok=400, flow=True) + rng0 = random.Random(1); rng0.shuffle(val); val = val[:256] + pcm = {ds: np.memmap(f"{D2}/{ds}/pcm24k.i16", dtype=np.int16, mode="r") for ds in dss if os.path.exists(f"{D2}/{ds}/pcm24k.i16")} + emb = {ds: np.load(f"{D2}/{ds}/campplus.npy", mmap_mode="r") for ds in dss if os.path.exists(f"{D2}/{ds}/campplus.npy")} + os.makedirs(a.out, exist_ok=True) + print("flow items", len(items), "hours", round(sum(m["dur"] for m in items) / 3600, 1), "val", len(val), flush=True) + extra = (lambda m: a.anchor_only_w if m["spk"] == ANCHOR else 1.0) if a.anchor_only_w > 0 else None + + def make(bt): + toks, feats, es = [], [], [] + for m in bt: + y = torch.from_numpy(np.asarray(pcm[m["ds"]][m["off24"]: m["off24"] + m["n24"]], dtype=np.float32) / 32768.0) + n = int(math.ceil(len(y) / 960)) * 960 + y = F.pad(y, (0, n - len(y)))[None].cuda() + ft = fe(y).squeeze(0).transpose(0, 1) # [T,80] + tk = torch.from_numpy(np.asarray(mm[m["ds"]][m["toff"]: m["toff"] + m["ntok"]], dtype=np.int64)) + k = min(ft.shape[0] // 2, len(tk)) + toks.append(tk[:k]); feats.append(ft[:2 * k]); es.append(torch.from_numpy(np.array(emb[m["ds"]][m["row"]]))) + tl = torch.tensor([len(t) for t in toks], dtype=torch.int32) + fl = torch.tensor([len(f) for f in feats], dtype=torch.int32) + T = int(tl.max()); Fm = int(fl.max()) + tok = torch.zeros(len(bt), T, dtype=torch.long); feat = torch.zeros(len(bt), Fm, 80) + for i, (t, f) in enumerate(zip(toks, feats)): + tok[i, :len(t)] = t; feat[i, :len(f)] = f.cpu() + return dict(speech_token=tok, speech_token_len=tl, speech_feat=feat, speech_feat_len=fl, + embedding=torch.stack(es).float()) + + params = [p for p in flow.parameters() if p.requires_grad] + opt = torch.optim.AdamW(params, lr=a.lr, weight_decay=0.0) + logf = open(f"{a.out}/log.jsonl", "a") + + def validate(): + flow.eval(); L = [] + st = torch.random.get_rng_state(); cst = torch.cuda.get_rng_state(); pst = random.getstate() + torch.manual_seed(0); random.seed(0) + with torch.no_grad(): + for i in range(0, len(val), 16): + L.append(float(flow(make(val[i:i + 16]), torch.device("cuda"))["loss"])) + torch.random.set_rng_state(st); torch.cuda.set_rng_state(cst); random.setstate(pst) + flow.train(); return float(np.mean(L)) + + v = validate(); best, bad = v, 0 + print(json.dumps({"step": 0, "val": v}), flush=True); logf.write(json.dumps({"step": 0, "val": v}) + "\n") + torch.save(dict(flow=flow.state_dict(), step=0, val=v), f"{a.out}/best.pt") + rng = random.Random(0); step, t0, done = 0, time.time(), False + while not done: + for bi in batches(items, a.tokens, rng, extra, cost=lambda m: m["ntok"]): + lrm = min(1.0, (step + 1) / a.warmup) + for g in opt.param_groups: + g["lr"] = a.lr * lrm + with torch.autocast("cuda", dtype=torch.bfloat16): + loss = flow(make([items[i] for i in bi]), torch.device("cuda"))["loss"] + opt.zero_grad(set_to_none=True) + if not torch.isfinite(loss): + continue + loss.backward() + gn = torch.nn.utils.clip_grad_norm_(params, 5.0) + if not torch.isfinite(gn): + continue + opt.step(); step += 1 + if step % 50 == 0: + r = dict(step=step, loss=round(float(loss), 4), gn=round(float(gn), 2), B=len(bi), min=round((time.time() - t0) / 60, 2)) + print(json.dumps(r), flush=True); logf.write(json.dumps(r) + "\n"); logf.flush() + if step % a.val_every == 0: + v = validate(); imp = v < best - 1e-4 + print(json.dumps({"step": step, "val": v, "best": imp}), flush=True) + logf.write(json.dumps({"step": step, "val": v, "best": imp}) + "\n"); logf.flush() + if imp: + best, bad = v, 0 + torch.save(dict(flow=flow.state_dict(), step=step, val=v), f"{a.out}/best.pt") + else: + bad += 1 + if bad >= a.patience: + done = True; break + if (time.time() - t0) / 60 > a.minutes or step >= a.max_steps: + done = True; break + print("TRAIN_FLOW_DONE", step, "best", best, flush=True) + + +# ------------------------------------------------------------------ synthesis +_SENT = re.compile(r"(?<=[.?!])\s+") + + +def split_sentences(text): + """tagged text -> list of tagged sentences. A sentence ends at . ? ! inside a span or at the end of a span; + language spans are cut there and re-tagged, so a sentence keeps its in-sentence switches.""" + out, cur = [], [] + for l, s in segments(text): + parts = _SENT.split(s.strip()) + for k, p in enumerate(parts): + p = p.strip() + if p: + cur.append(f"<{l}>{p}") + if (k < len(parts) - 1 or p[-1:] in ".?!") and cur: + out.append(" ".join(cur)); cur = [] + if cur: + out.append(" ".join(cur)) + return [o for o in out if o.strip()] + + +def trim_edges(w, sr, keep_lead=0.12, keep_tail=0.18): + from da.p2common import silence_mask + sil = silence_mask(w, sr) + sp = np.where(~sil)[0] + if not len(sp): + return w + a, b = sp[0] * 0.01, (sp[-1] + 1) * 0.01 + out = w[int(max(0, a - keep_lead) * sr): int(min(len(w) / sr, b + keep_tail) * sr)] + lead = a - max(0, a - keep_lead) + if lead < keep_lead: + out = np.concatenate([np.zeros(int((keep_lead - lead) * sr), out.dtype), out]) + return out + + +def cap_internal(w, sr, cap=0.6): + """shorten every internal silence longer than cap seconds to cap (the centre of the silent run is removed).""" + from da.p2common import silence_mask, runs + sil = silence_mask(w, sr) + hop = int(0.01 * sr) + R = [(a, b) for a, b in runs(sil) if a > 0 and b < len(sil) and (b - a) * 0.01 > cap] + if not R: + return w + keep, cur = [], 0 + for a, b in R: + cut0 = a * hop + int(cap / 2 * sr) + cut1 = b * hop - int(cap / 2 * sr) + keep.append(w[cur:cut0]); cur = cut1 + keep.append(w[cur:]) + return np.concatenate(keep) + + +class Synth: + def __init__(self, ckpt, flow_ckpt=None, sent_pause=0.5): + from transformers import AutoTokenizer + self.tok = AutoTokenizer.from_pretrained(f"{MODEL_DIR}/CosyVoice-BlankEN") + self.cosy = load_cosy() + if flow_ckpt: + self.cosy.model.flow.load_state_dict(torch.load(flow_ckpt, map_location="cuda")["flow"]) + ck = torch.load(ckpt, map_location="cuda") + self.spk2id = json.load(open(os.path.join(os.path.dirname(ckpt), "spk2id.json"))) + self.m = DALM2(self.cosy.model.llm, ck["n_spk"]).cuda().eval() + self.m.load_state_dict(ck["model"]) + self.sent_pause = sent_pause + self.cap = 0.6 + # anchor prompts: normalised anchor val clips from data2 (never in train), 3-7 s, highest SNR first + meta = [json.loads(l) for l in open(f"{D2}/waxal/meta.jsonl", encoding="utf-8")] + tk = [json.loads(l) for l in open(f"{D2}/waxal/s3tok.jsonl")] + pcm = np.memmap(f"{D2}/waxal/pcm24k.i16", dtype=np.int16, mode="r") + s3 = np.memmap(f"{D2}/waxal/s3tok.i32", dtype=np.int32, mode="r") + cand = [(m, t) for m, t in zip(meta, tk) if m["spk"] == ANCHOR and m["split"] == "val" and 3.0 <= m["dur"] <= 7.0 + and not m.get("longform")] + cand.sort(key=lambda x: -x[0]["ascore"]) + self.prompts = [] + os.makedirs(f"{HOME}/eval/prompts", exist_ok=True) + for m, t in cand[:4]: + y = np.asarray(pcm[m["off24"]: m["off24"] + m["n24"]], dtype=np.float32) / 32768.0 + p = f"{HOME}/eval/prompts/{m['id']}.wav"; sf.write(p, y, 24000) + from train_b import prompt_feats + ptok, pfeat, pemb = prompt_feats(self.cosy, p) + ids, langs = text_tokens(self.tok, tagged(m)) + lmp = (torch.tensor(ids).cuda(), torch.tensor(langs).cuda(), + torch.from_numpy(np.asarray(s3[t["toff"]: t["toff"] + t["ntok"]], dtype=np.int64)).cuda()) + self.prompts.append(dict(id=m["id"], text=m["text"], lm=lmp, flow=(ptok, pfeat, pemb))) + print("anchor prompts", [p["id"] for p in self.prompts], flush=True) + + def one(self, tagged_text, seed=0, prompt_k=0, use_lm_prompt=True): + ids, langs = text_tokens(self.tok, tagged_text) + P = self.prompts[prompt_k % len(self.prompts)] + with torch.autocast("cuda", dtype=torch.bfloat16): + sp = self.m.generate2(torch.tensor(ids).cuda(), torch.tensor(langs).cuda(), + torch.tensor(self.spk2id[ANCHOR]).cuda(), prompt=P["lm"] if use_lm_prompt else None, + seed=seed) + if len(sp) < 3: + sp = sp + [0] * 3 + self.last_tokens.append(sp) + return token2wav(self.cosy, sp, *P["flow"]) + + def join(self, ws): + sr = self.cosy.sample_rate + if len(ws) == 1: + return ws[0] + out = [] + for i, w in enumerate(ws): + if i: + out.append(np.zeros(max(0, int((self.sent_pause - 0.30) * sr)), np.float32)) + out.append(w) + return np.concatenate(out) + + def redecode(self, toks, prompt_k=0): + """same LM tokens, (possibly different) flow -> identical post-processing""" + sr = self.cosy.sample_rate + P = self.prompts[prompt_k % len(self.prompts)] + return self.join([cap_internal(trim_edges(token2wav(self.cosy, t, *P["flow"]), sr), sr, self.cap) for t in toks]) + + def say(self, tagged_text, seed=0, prompt_k=0, stitch=True, use_lm_prompt=True): + sr = self.cosy.sample_rate + self.last_tokens = [] + sents = split_sentences(tagged_text) if stitch else [tagged_text] + ws = [cap_internal(trim_edges(self.one(s, seed=seed + 1000 * i, prompt_k=prompt_k, use_lm_prompt=use_lm_prompt), sr), sr, self.cap) + for i, s in enumerate(sents)] + # trailing 0.18 + leading 0.12 already present; join() tops up to the anchor's sentence pause + return self.join(ws) + + +def redecode(a): + """decode the LM tokens saved by synth (--src DIR) with another flow checkpoint -> --out""" + S = Synth(a.ckpt, a.flow or None, a.sent_pause) + os.makedirs(a.out, exist_ok=True) + import glob + for f in sorted(glob.glob(f"{a.src}/*.tok.json")): + d = json.load(open(f)) + i = os.path.basename(f)[:-9] + sf.write(f"{a.out}/{i}.wav", S.redecode(d["tokens"], d["prompt_k"]), S.cosy.sample_rate) + print("REDECODE_DONE", a.out, flush=True) + + +def synth(a): + S = Synth(a.ckpt, a.flow or None, a.sent_pause) + rows = [json.loads(l) for l in open(a.eval_set, encoding="utf-8")] + if a.only: + keep = set(a.only.split(",")); rows = [r for r in rows if r["id"] in keep or r["lang"] in keep] + if a.nper: + cnt, sel = {}, [] + for r in rows: + if r["lang"] in ("cs", "para") or cnt.get(r["lang"], 0) < a.nper: + sel.append(r); cnt[r["lang"]] = cnt.get(r["lang"], 0) + 1 + rows = sel + ks = [a.k] if a.k >= 0 else list(range(a.n)) + for k in ks: + od = a.out if len(ks) == 1 else f"{a.out}/cand{k}" + os.makedirs(od, exist_ok=True) + for r in rows: + text = r["text"] if r["lang"] in ("cs", "para") else f"<{r['lang']}>{r['text']}" + w = S.say(text, seed=k, prompt_k=k, stitch=not a.no_stitch, use_lm_prompt=not a.no_lm_prompt) + sf.write(f"{od}/{r['id']}.wav", w, S.cosy.sample_rate) + json.dump(dict(tokens=S.last_tokens, prompt_k=k), open(f"{od}/{r['id']}.tok.json", "w")) + print("synth cand", k, "done", od, flush=True) + print("SYNTH2_DONE", a.out, flush=True) + + +if __name__ == "__main__": + ap = argparse.ArgumentParser() + ap.add_argument("cmd") + ap.add_argument("--datasets", default="waxal,fleurs,slr86,slr70,bible_yor,bible_hau,cs_pairs") + ap.add_argument("--out", default=f"{HOME}/runs/p2_lm") + ap.add_argument("--init", default="") + ap.add_argument("--flow_init", default="") + ap.add_argument("--ckpt", default="") + ap.add_argument("--flow", default="") + ap.add_argument("--minutes", type=float, default=90) + ap.add_argument("--max_steps", type=int, default=4000) + ap.add_argument("--lr", type=float, default=1e-5) + ap.add_argument("--lr_new", type=float, default=5e-4) + ap.add_argument("--warmup", type=int, default=300) + ap.add_argument("--tokens", type=int, default=16000) + ap.add_argument("--adv_w", type=float, default=0.02) + ap.add_argument("--p_prompt", type=float, default=0.5) + ap.add_argument("--val_every", type=int, default=200) + ap.add_argument("--patience", type=int, default=5) + ap.add_argument("--anchor_only_w", type=float, default=0.0) + ap.add_argument("--select_anchor", action="store_true") + ap.add_argument("--eval_set", default=f"{HOME}/eval/eval_set2.jsonl") + ap.add_argument("--n", type=int, default=1) + ap.add_argument("--nper", type=int, default=0) + ap.add_argument("--k", type=int, default=-1) + ap.add_argument("--src", default="") + ap.add_argument("--only", default="") + ap.add_argument("--sent_pause", type=float, default=0.6) + ap.add_argument("--no_stitch", action="store_true") + ap.add_argument("--no_lm_prompt", action="store_true") + a = ap.parse_args() + {"lm": train_lm, "flow": train_flow, "synth": synth, "redecode": redecode}[a.cmd](a) diff --git a/config.json b/config.json new file mode 100644 index 0000000000000000000000000000000000000000..6752fd24c7d53927fdfad8945f9566d1a674737f --- /dev/null +++ b/config.json @@ -0,0 +1,41 @@ +{ + "model_type": "dynamic-accent", + "name": "Dynamic Accent Pro v1.1", + "version": "1.1", + "task": "text-to-speech", + "experimental": true, + "languages": [ + "en", + "pcm", + "yo", + "ig", + "ha" + ], + "default_version": "flash", + "versions": [ + "pro", + "flash" + ], + "voices": [ + "anchor_male", + "waxal_ibo_5_female", + "waxal_yor_5_female" + ], + "controls": { + "instruct": "natural-language style instruction (--instruct)", + "codeswitch_prompt": "optional text-LLM code-switch step (--codeswitch_prompt)" + }, + "settings": "settings.json", + "entrypoints": { + "cli": "infer.py" + }, + "base_models": [ + "FunAudioLLM/Fun-CosyVoice3-0.5B-2512" + ], + "codeswitch_llm": "Qwen/Qwen3-32B (Apache-2.0), optional", + "licence": { + "weights_prompts_samples": "CC-BY-SA-4.0 (LICENSE)", + "code": "Apache-2.0 (LICENSE-CODE)" + }, + "note": "Repo-level manifest (lets the Hub count downloads). Runtime settings live in settings.json; per-checkpoint configs under checkpoints/. Default: flash; --instruct switches to pro unless --version is given." +} \ No newline at end of file diff --git a/cosyvoice_requirements.txt b/cosyvoice_requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..989bccf6108a1ba886e4021cd4a4d620aadab592 --- /dev/null +++ b/cosyvoice_requirements.txt @@ -0,0 +1,42 @@ +--extra-index-url https://download.pytorch.org/whl/cu121 +--extra-index-url https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple/ # https://github.com/microsoft/onnxruntime/issues/21684 +conformer==0.3.2 +deepspeed==0.15.1; sys_platform == 'linux' +diffusers==0.29.0 +fastapi==0.115.6 +fastapi-cli==0.0.4 +gdown==5.1.0 +gradio==5.4.0 +grpcio==1.57.0 +grpcio-tools==1.57.0 +hydra-core==1.3.2 +HyperPyYAML==1.2.3 +inflect==7.3.1 +librosa==0.10.2 +lightning==2.2.4 +matplotlib==3.7.5 +modelscope==1.20.0 +networkx==3.1 +numpy==1.26.4 +omegaconf==2.3.0 +onnx==1.16.0 +onnxruntime-gpu==1.18.0; sys_platform == 'linux' +onnxruntime==1.18.0; sys_platform == 'darwin' or sys_platform == 'win32' +openai-whisper==20231117 +protobuf==4.25 +pyarrow==18.1.0 +pydantic==2.7.0 +pyworld==0.3.4 +rich==13.7.1 +soundfile==0.12.1 +tensorboard==2.14.0 +tensorrt-cu12==10.13.3.9; sys_platform == 'linux' +tensorrt-cu12-bindings==10.13.3.9; sys_platform == 'linux' +tensorrt-cu12-libs==10.13.3.9; sys_platform == 'linux' +torch==2.3.1 +torchaudio==2.3.1 +transformers==4.51.3 +x-transformers==2.11.24 +uvicorn==0.30.0 +wetext==0.0.4 +wget==3.2 diff --git a/cs_frontend.py b/cs_frontend.py new file mode 100644 index 0000000000000000000000000000000000000000..0788cac69ab8f78fe0ef17eab91df870589b21e7 --- /dev/null +++ b/cs_frontend.py @@ -0,0 +1,187 @@ +"""Code-switch text front end (pipeline step, NOT native model capability). + +Turns a plain sentence + a code-switch request ("mostly English, Igbo greetings") into language-tagged text +(.. ..) that the TTS then speaks. Uses a permissively licensed text LLM: +default Qwen/Qwen3-14B (Apache-2.0), thinking disabled. N-ATLaS was not used: its terms cap use at 1,000 active +users and require a commercial licence beyond that. + +Safety rails (MADLAD looped in Flash): max_new_tokens tied to input length, repetition_penalty, no_repeat_ngram, +output validation (tags only from allowed set, full coverage, length ratio, n-gram repetition cap), up to 2 retries, +then a safe fallback (the input as single-language text) with a warning. +""" +import re, os, json +from collections import Counter + +LANGS = {"eng": "English", "ibo": "Igbo", "yor": "Yoruba", "hau": "Hausa", "pcm": "Nigerian Pidgin"} +SYS = """You rewrite text for a Nigerian text-to-speech system as natural code-switched speech. +Follow the user's code-switch request exactly (which languages, how much of each, which parts). +Rules: +- Keep the meaning. Do not add new information, greetings or sentences that the request does not ask for. +- Write Igbo, Yoruba and Hausa in standard orthography (use tone marks/diacritics for Yoruba and Igbo if you can). +- Wrap every span in a language tag: ..., ..., ..., ..., .... +- Nothing may appear outside tags. Output ONLY the tagged text on one line, no explanations. +- Prefer these common, correct phrases when greetings/thanks/closings are requested: + Igbo: Ndewo (hello), Ụtụtụ ọma (good morning), Ehihie ọma (good afternoon), Mgbede ọma (good evening), Kedu? (how are you), Daalụ (thank you), Nnọọ (welcome), Ka ọ dị (goodbye) + Yoruba: Ẹ káàárọ̀ (good morning), Ẹ káàsán (good afternoon), Ẹ kú ìrọ̀lẹ́ (good evening), Ẹ kú àbọ̀ (welcome), Ẹ ṣé (thank you), Ó dàbọ̀ (goodbye) + Hausa: Sannu (hello), Barka da safiya (good morning), Barka da yamma (good evening), Maraba (welcome), Na gode (thank you), Sai an jima (goodbye) + Nigerian Pidgin: How far (hello), Abeg (please), Thank you well well, We go see later (goodbye) +- If you cannot translate a part reliably, keep that part in English rather than guessing.""" +SHOT_U = "Request: mostly English, Igbo greetings and closing\nText: Good morning everyone. The meeting starts at ten, please be on time. Thank you." +SHOT_A = "Ụtụtụ ọma, unu niile. The meeting starts at ten, please be on time. Daalụ." + +TAG = re.compile(r"<(eng|ibo|yor|hau|pcm)>(.*?)", re.S) + + +def requested_langs(req): + r = req.lower() + out = {k for k, v in LANGS.items() if v.lower().split()[-1] in r or k in r} + if "pidgin" in r: + out.add("pcm") + return out or {"eng"} + + +def validate(out, src, allowed): + out = out.strip() + spans = TAG.findall(out) + if not spans: + return False, "no tags" + rest = TAG.sub(" ", out).strip() + if re.sub(r"[\s.,!?;:'\"-]", "", rest): + return False, f"text outside tags: {rest[:40]}" + bad = {l for l, _ in spans} - allowed - {"eng"} + if bad: + return False, f"unrequested language {bad}" + words = " ".join(t for _, t in spans).split() + n_src = len(src.split()) + if not (0.4 * n_src <= len(words) <= 2.5 * n_src + 6): + return False, f"length ratio {len(words)}/{n_src}" + low = [w.lower().strip(".,!?") for w in words] + for k in range(len(low) - 3): + if low[k] == low[k + 1] == low[k + 2] == low[k + 3]: + return False, "word loop" + tri = Counter(tuple(low[i:i + 3]) for i in range(len(low) - 2)) + if tri and tri.most_common(1)[0][1] > 2: + return False, "trigram loop" + if any(not t.strip() for _, t in spans): + return False, "empty span" + return True, "ok" + + +def chrf(hyp, ref, n=6, beta=2.0): + def grams(s, k): + s = " ".join(s.lower().split()) + return Counter(s[i:i + k] for i in range(len(s) - k + 1)) + P, R = [], [] + for k in range(1, n + 1): + h, r = grams(hyp, k), grams(ref, k) + if not h or not r: + continue + m = sum((h & r).values()) + P.append(m / sum(h.values())); R.append(m / sum(r.values())) + if not P: + return 0.0 + p, r = sum(P) / len(P), sum(R) / len(R) + return 0.0 if p + r == 0 else 100 * (1 + beta ** 2) * p * r / (beta ** 2 * p + r) + + +class CodeSwitchFrontEnd: + def __init__(self, model=os.environ.get("CS_LLM", "Qwen/Qwen3-14B"), device="cuda", cache_dir=None, diac=None): + import torch + from transformers import AutoTokenizer, AutoModelForCausalLM + self.torch = torch + self.tok = AutoTokenizer.from_pretrained(model, cache_dir=cache_dir) + self.lm = AutoModelForCausalLM.from_pretrained(model, torch_dtype=torch.bfloat16, cache_dir=cache_dir).to(device).eval() + self.device = device + self.diac = diac # optional flash FrontEnd (ByT5 diacritic restoration) for ibo/yor spans + + def _gen(self, req, text, temp): + msgs = [{"role": "system", "content": SYS}, {"role": "user", "content": SHOT_U}, {"role": "assistant", "content": SHOT_A}, + {"role": "user", "content": f"Request: {req}\nText: {text}"}] + try: + p = self.tok.apply_chat_template(msgs, tokenize=False, add_generation_prompt=True, enable_thinking=False) + except TypeError: + p = self.tok.apply_chat_template(msgs, tokenize=False, add_generation_prompt=True) + ids = self.tok(p, return_tensors="pt").to(self.device) + n_in = len(self.tok(text)["input_ids"]) + # repetition rails act on the GENERATED tokens only (a prompt-wide no_repeat_ngram/repetition_penalty + # forbids copying the input and garbles spelling): hard length cap + loop stop + validation below + from transformers import StoppingCriteria, StoppingCriteriaList + P0 = ids["input_ids"].shape[1] + + class LoopStop(StoppingCriteria): + def __call__(s_, input_ids, scores, **k): + g = input_ids[0, P0:].tolist() + if len(g) < 18: + return False + tail = tuple(g[-6:]) + hits = sum(1 for i in range(len(g) - 6) if tuple(g[i:i + 6]) == tail) + return hits >= 2 + kw = dict(max_new_tokens=int(3 * n_in + 48), pad_token_id=self.tok.eos_token_id, + stopping_criteria=StoppingCriteriaList([LoopStop()])) + if temp > 0: + kw.update(do_sample=True, temperature=temp, top_p=0.9) + else: + kw.update(do_sample=False, temperature=None, top_p=None, top_k=None) + with self.torch.no_grad(): + o = self.lm.generate(**ids, **kw) + s = self.tok.decode(o[0, ids["input_ids"].shape[1]:], skip_special_tokens=True) + s = re.sub(r".*?", "", s, flags=re.S).strip().splitlines() + return s[0].strip() if s else "" + + def back_translate(self, span, lang): + msgs = [{"role": "user", "content": f"Translate this {LANGS[lang]} text into English. Output only the translation.\n\n{span}"}] + try: + p = self.tok.apply_chat_template(msgs, tokenize=False, add_generation_prompt=True, enable_thinking=False) + except TypeError: + p = self.tok.apply_chat_template(msgs, tokenize=False, add_generation_prompt=True) + ids = self.tok(p, return_tensors="pt").to(self.device) + with self.torch.no_grad(): + o = self.lm.generate(**ids, max_new_tokens=3 * len(span.split()) + 24, do_sample=False, temperature=None, + top_p=None, top_k=None, pad_token_id=self.tok.eos_token_id) + return self.tok.decode(o[0, ids["input_ids"].shape[1]:], skip_special_tokens=True).strip() + + def meaning_check(self, out, src, thr=45.0): + """round trip: back-translate every non-English span; the English reconstruction must match the input (chrF).""" + parts = [t if l == "eng" else self.back_translate(t, l) for l, t in TAG.findall(out)] + back = " ".join(parts) + score = min(chrf(back, src), chrf(back, src, beta=0.5)) # recall- and precision-weighted (added content fails) + return score >= thr, round(score, 1), back + + def __call__(self, text, req, fallback_lang="eng"): + allowed = requested_langs(req) + log = [] + for k, temp in enumerate((0.0, 0.6, 0.8)): # first try + at most 2 retries + out = self._gen(req, text, temp) + ok, why = validate(out, text, allowed) + rt = None + if ok: + ok, sc, back = self.meaning_check(out, text) + rt = dict(chrf=sc, back=back[:300]) + if not ok: + why = f"meaning drift (round-trip chrF {sc})" + log.append(dict(try_=k, ok=ok, why=why, out=out[:300], roundtrip=rt)) + if ok: + if self.diac is not None: + out = TAG.sub(lambda m: f"<{m.group(1)}>{self.diac.diacritise(m.group(2), m.group(1)) if m.group(1) in ('ibo', 'yor') else m.group(2)}", out) + return out, dict(ok=True, tries=log) + return f"<{fallback_lang}>{text}", dict(ok=False, tries=log, warning="code-switch front end failed validation; spoke the input as-is") + + +if __name__ == "__main__": + import sys + cache = os.environ.get("HF_CACHE") + fe = CodeSwitchFrontEnd(cache_dir=cache) + tests = json.load(open(sys.argv[1])) if len(sys.argv) > 1 else [ + ["Good morning everyone, welcome to the market. Prices are good today, come and buy.", "mostly English, Igbo greetings"], + ["Please call your mother tonight, she has been waiting to hear from you.", "Igbo sentence with a few English words"], + ["Welcome to our shop. Everything is half price this weekend, so tell your friends.", "Yoruba greeting, the rest in English"], + ["Good evening. The rain will be heavy tomorrow, so carry an umbrella.", "half Hausa, half English"], + ["My brother, the work is too much today, but we will finish it.", "Nigerian Pidgin mixed with Igbo"], + ["Thank you for coming. We will start the program in five minutes.", "English with a Yoruba closing"], + ] + res = [] + for t, r in tests: + o, info = fe(t, r) + res.append(dict(text=t, request=r, out=o, ok=info["ok"], tries=len(info["tries"]), log=info["tries"])) + print(json.dumps(res[-1], ensure_ascii=False), flush=True) + json.dump(res, open(os.environ.get("CS_OUT", "cs_test.json"), "w"), ensure_ascii=False, indent=1) diff --git a/flash_instruct.py b/flash_instruct.py new file mode 100644 index 0000000000000000000000000000000000000000..94bb52a86d5c9f694c8e6ca6df9af371804c3162 --- /dev/null +++ b/flash_instruct.py @@ -0,0 +1,433 @@ +"""Flash-Instruct: natural-language instruction control for the Flash-DPO CosyVoice3 LM. + +The CV3 system-prompt slot ("You are a helpful assistant.<|endofprompt|>") becomes an instruction, e.g. +"Speak in an Igbo-English mix, slowly, with plenty of pauses." Labels come from label.py (audio-derived). +~18% of training items keep the original default prompt (and the original rate embedding), so default behaviour is kept. +Instruction items use rate embedding 0 (none): the instruction carries pace. + + python flash_instruct.py train --init ~/flash/runs/flash_dpo/best.pt --out /scratch/fi_run + python flash_instruct.py gen --lm CKPT --out DIR --voice anchor [--instruct "..."] [--rows ROWS] [--shard i/n] + python flash_instruct.py adh --lm CKPT --out DIR --voice anchor --shard i/n (adherence grid) +""" +import argparse, json, math, os, random, sys, time +import numpy as np, torch, torch.nn.functional as F, soundfile as sf +HOME = os.path.expanduser("~") +_HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path[:0] = [_HERE] +import flash_lm as FL # noqa: E402 +import instruct_text as IT # noqa: E402 + +EOP = 151646 # <|endofprompt|> +# extra source weights on top of flash_lm.FLASH_W (natural/spontaneous speech up, Bible narration down) +MIXW = {"igbosyncorp": 2.0, "igbosyn_pseudo": 1.5, "naijas2st_igbo": 1.5, "open_bible_igbo": 0.5, "bible_hau": 0.5, + "bible_yor": 0.5} + + +PI = {"few": 1, "normal": 2, "many": 3} +EI = {"calm": 1, "neutral": 2, "lively": 3} +RI = {"slow": FL.R2I["slow"], "normal": FL.R2I["normal"], "fast": FL.R2I["fast"]} + + +def add_ctrl(model, d=896): + """explicit control embeddings (pause, energy) added to the speaker vector next to the existing rate embedding.""" + import torch.nn as nn + if not hasattr(model, "pause_emb"): + model.pause_emb = nn.Embedding(4, d, padding_idx=0).cuda(); nn.init.zeros_(model.pause_emb.weight) + model.energy_emb = nn.Embedding(4, d, padding_idx=0).cuda(); nn.init.zeros_(model.energy_emb.weight) + return model + + +def parse_controls(instr): + """keyword parser: natural-language instruction -> (rate, pause, energy) control ids (0 = not requested).""" + t = (instr or "").lower() + def has(*w): + return any(x in t for x in w) + rate = RI["slow"] if has("slow", "unhurried", "take your time", "taking your time", "deliberate") else \ + RI["fast"] if has("fast", "quick", "brisk", "rapid", "hurr") else RI["normal"] if has("natural pace", "normal pace", "steady pace", "comfortable pace", "everyday pace") else 0 + pause = PI["few"] if has("few pause", "no pause", "hardly any pause", "without stopping", "one smooth flow", "minimal paus", "running the phrases") else \ + PI["many"] if has("plenty of pause", "many pause", "lots of pause", "pausing often", "frequent pause", "deliberate pause", "breaks between", "more pause") else \ + PI["normal"] if has("natural pause", "normal phrasing") else 0 + energy = EI["calm"] if has("calm", "gentle", "soft", "relaxed", "low-key", "quiet", "even-toned", "even tone") else \ + EI["lively"] if has("lively", "animated", "energetic", "expressive", "upbeat", "excited", "bright") else \ + EI["neutral"] if has("neutral", "matter-of-fact", "plain") else 0 + return rate, pause, energy + + +class IFLM(FL.FLM): + """FLM whose system-prompt prefix can be set per item (self._pre); optional pause/energy control embeddings.""" + _pre = None + ctrl_scale = 1.0 + + def spkvec(self, spk): + v = self.spk_emb(spk[0]) + self.rate_emb(spk[1]) + if hasattr(self, "pause_emb") and spk.shape[0] > 2: + v = v + self.ctrl_scale * (self.pause_emb(spk[2]) + self.energy_emb(spk[3])) + return v + + def build(self, ids, langs, spk, sp=None, pids=None, plangs=None, psp=None): + E = self.q.speech_embedding.weight + if pids is not None: + ids = torch.cat([pids, ids]); langs = torch.cat([plangs, langs]) + pfx = self._pre if self._pre is not None else self.prefix + pre = self.q.llm.model.model.embed_tokens(pfx.to(ids.device)) + parts = [E[self.q.sos][None], pre, self.embed_text(ids, langs), self.spkvec(spk)[None], E[self.q.task_id][None]] + npsp = 0 + if psp is not None: + parts.append(self.q.speech_embedding(psp)); npsp = len(psp) + if sp is not None: + parts.append(self.q.speech_embedding(sp)) + return torch.cat(parts, 0), len(pre), len(ids), npsp + + def pack(self, batch): + seqs, tgts, tpos, spks = [], [], [], [] + for b in batch: + ids, langs, spk, sp = b[:4] + pr = b[4] if len(b) > 4 else None + self._pre = b[5] if len(b) > 5 else None + x, P, nt, npsp = self.build(ids, langs, spk, sp, *(pr if pr else (None, None, None))) + self._pre = None + tgt = torch.full((x.shape[0] + 1,), -100, device=x.device, dtype=torch.long) + start = P + nt + 2 + npsp + tgt[start: start + len(sp)] = sp + tgt[start + len(sp)] = self.EOS + seqs.append(x); tgts.append(tgt[:x.shape[0]]); tpos.append((1 + P, 1 + P + nt)); spks.append(int(spk[0])) + Tm = max(s.shape[0] for s in seqs); D = seqs[0].shape[1] + X = seqs[0].new_zeros(len(seqs), Tm, D) + Y = torch.full((len(seqs), Tm), -100, device=X.device, dtype=torch.long) + M = torch.zeros(len(seqs), Tm, device=X.device, dtype=torch.long) + for i, (s, t) in enumerate(zip(seqs, tgts)): + X[i, :len(s)] = s; Y[i, :len(t)] = t; M[i, :len(s)] = 1 + return X, Y, M, tpos, spks + + +@torch.no_grad() +def generate_cfg(m, ids, langs, spk_c, spk_u, pre_c, prompt=None, g=1.5, top_p=0.8, top_k=25, win=10, tau_r=0.1, seed=0, + min_ratio=2.0, max_ratio=30.0, max_tok=900): + """classifier-free guidance on the instruction: logp = lp_cond + g * (lp_cond - lp_default). + cond = instruction prefix + controls; uncond = the default prompt, no controls. Same silexcap sampler as Flash.""" + gen = torch.Generator(device=ids.device).manual_seed(seed) + m._pre = pre_c + xc, _, _, _ = m.build(ids, langs, spk_c, None, *(prompt if prompt else (None, None, None))) + m._pre = None + xu, _, _, _ = m.build(ids, langs, spk_u, None, *(prompt if prompt else (None, None, None))) + n = len(ids); min_len, max_len = int(n * min_ratio), min(int(n * max_ratio), max_tok) + E = m.q.speech_embedding.weight + pc = pu = None; ic, iu = xc[None], xu[None]; out = [] + for i in range(max_len): + oc = m.q.llm.model.model(inputs_embeds=ic, past_key_values=pc, use_cache=True, return_dict=True); pc = oc.past_key_values + ou = m.q.llm.model.model(inputs_embeds=iu, past_key_values=pu, use_cache=True, return_dict=True); pu = ou.past_key_values + lc = m.q.llm_decoder(oc.last_hidden_state[:, -1]).float().log_softmax(-1)[0] + lu = m.q.llm_decoder(ou.last_hidden_state[:, -1]).float().log_softmax(-1)[0] + logp = (lc + g * (lc - lu)).log_softmax(-1) + eos_lp = logp[m.EOS].clone() + logp[m.V:] = -float("inf") + if i >= min_len: + logp[m.EOS] = eos_lp + p = logp.exp(); p = p / p.sum() + sp_, si = p.sort(descending=True) + keep = (sp_.cumsum(0) - sp_) < top_p; keep[top_k:] = False + cp, ci = sp_[keep], si[keep] + t = int(ci[torch.multinomial(cp / cp.sum(), 1, generator=gen)]) + exempt = False + if t in FL.SIL: + run = 0 + for u in reversed(out): + if u not in FL.SIL: + break + run += 1 + exempt = run < 25 + if not exempt and out and t != m.EOS and sum(1 for u in out[-win:] if u == t) >= win * tau_r: + t = int(torch.multinomial(p, 1, generator=gen)) + if t >= m.V: + break + out.append(t) + nx = E[torch.tensor([t], device=ids.device)][:, None] + ic = iu = nx + return [out] + + +def pre_ids(tok, instruct): + return tok.t.encode(instruct or IT.DEFAULT) + [EOP] + + +def train(a): + cosy = FL.load_cv3(); tok = FL.Tok(cosy) + model, spk2id = FL.load_model(cosy, a.init, tok) + model.__class__ = IFLM + if a.ctrl: + add_ctrl(model) + model.train() + L = {} + for l in open(a.labels, encoding="utf-8"): + x = json.loads(l); L[x["id"]] = x + items, mm = FL.load_data({"train"}, a.max_tok, a.extra_man, a.qual, a.qual_drop) + items = [m for m in items if m["spk"] in spk2id and not m["src"].startswith("av_")] + # ACE add-on: cleaner + more native mix. drop noisy/low-SNR/clipped clips; up-weight natural speech, down-weight Bible + MX = json.load(open(a.mix)) if a.mix and os.path.exists(a.mix) else {"drop": [], "elig": []} + drop, elig = set(MX["drop"]), set(MX["elig"]) + n0 = len(items) + items = [m for m in items if m["id"] not in drop] + for m in items: + m["w"] *= MIXW.get(m["src"], 1.0) + n_el = sum(m["id"] in elig for m in items) + p_def_e = min(0.6, a.p_default * len(items) / max(1, n_el)) + hrs = {} + for m in items: + hrs[m["src"]] = hrs.get(m["src"], 0) + m["dur"] * m["w"] / 3600 + print("mix: dropped", n0 - len(items), "default-eligible", n_el, "p_default_eligible", round(p_def_e, 3), + "effective weighted hours", {k: round(v, 1) for k, v in sorted(hrs.items())}, flush=True) + val_all, mmv = FL.load_data(FL.VAL_SPLITS, a.max_tok, None) + mm.update(mmv) + rng = random.Random(1); rng.shuffle(val_all) + val, per_l = [], {} + for m in val_all: + if m["spk"] not in spk2id or m["lang"] == "cs" or m["id"] not in L or m["src"].startswith("av_"): + continue + if per_l.get(m["lang"], 0) >= 50: + continue + per_l[m["lang"]] = per_l.get(m["lang"], 0) + 1; val.append(m) + nl = sum(m["id"] in L for m in items) + print("items", len(items), "labelled", nl, "val", len(val), per_l, flush=True) + by_spk = {} + for m in items: + if m["ntok"] <= 200 and m["lang"] != "cs": + by_spk.setdefault(m["spk"], []).append(m) + for p in list(cosy.model.flow.parameters()) + list(cosy.model.hift.parameters()): + p.requires_grad = False + new = list(model.lang_emb.parameters()) + list(model.spk_emb.parameters()) + list(model.adv.parameters()) + list(model.rate_emb.parameters()) + ctl = list(model.pause_emb.parameters()) + list(model.energy_emb.parameters()) if a.ctrl else [] + newids = {id(p) for p in new + ctl} + old = [p for p in model.parameters() if id(p) not in newids and p.requires_grad] + groups = [{"params": old, "lr": a.lr}, {"params": new, "lr": a.lr_new}] + base = [a.lr, a.lr_new] + if ctl: + groups.append({"params": ctl, "lr": a.lr_ctrl}); base.append(a.lr_ctrl) + opt = torch.optim.AdamW(groups, weight_decay=0.01) + os.makedirs(a.out, exist_ok=True) + json.dump(spk2id, open(f"{a.out}/spk2id.json", "w")); json.dump(vars(a), open(f"{a.out}/args.json", "w")) + pcache = {} + + def P_(s): + if s not in pcache: + pcache[s] = torch.tensor(pre_ids(tok, s)).cuda() + return pcache[s] + + def full_labels(m): + x = L.get(m["id"]) + if not x: + return None + return dict(lang=x["lang"], rate=x["rate"], pause=x["pause"], energy=x["energy"], gender=x["gender"]) + + def enc(m, r, mode="sample", shuf=None): + """mode: sample (train) | default | inst (fixed rng) ; shuf=labels from another item (diagnostic).""" + text = m["tags"] if (m["tags"] and r.random() >= a.tag_drop) else FL.strip_tags(m["text"]) + ids, langs = tok(FL.tagged(text, m["lang"])) + lab = shuf if shuf is not None else full_labels(m) + # DEFAULT prompt only on native-like clips (pace and pauses close to real native speech), so the + # no-instruction behaviour moves toward native pace/pauses; other clips always carry an instruction + use_def = mode == "default" or lab is None or (mode == "sample" and m["id"] in elig and r.random() < p_def_e) + pc = ec = 0 + if use_def: + rate = m["rate"] if (mode != "sample" or r.random() >= a.rate_drop) else 0 + instr = IT.DEFAULT + else: + sub = IT.sample_labels(lab, r) if mode == "sample" else {k: v for k, v in lab.items() if k != "gender"} + instr = IT.make(sub, r) + rate = 0 + if a.ctrl: # the instruction's controls also go in as explicit embeddings (what it mentions, nothing else) + rate = RI.get(sub.get("rate"), 0); pc = PI.get(sub.get("pause"), 0); ec = EI.get(sub.get("energy"), 0) + sp = np.asarray(mm[m["ds"]][m["toff"]: m["toff"] + m["ntok"]], dtype=np.int64) + spk_t = [spk2id[m["spk"]], rate] + ([pc, ec] if a.ctrl else []) + out = [torch.tensor(ids).cuda(), torch.tensor(langs).cuda(), torch.tensor(spk_t).cuda(), + torch.from_numpy(sp).cuda(), None, P_(instr)] + if r.random() < a.p_prompt: + c = by_spk.get(m["spk"], []) + c = [x for x in r.sample(c, min(6, len(c))) if x["id"] != m["id"]] if c else [] + if c and c[0]["ntok"] + m["ntok"] <= a.max_tok + 150: + p = c[0] + pids, plangs = tok(FL.tagged(FL.strip_tags(p["text"]), p["lang"])) + psp = np.asarray(mm[p["ds"]][p["toff"]: p["toff"] + p["ntok"]], dtype=np.int64) + out[4] = (torch.tensor(pids).cuda(), torch.tensor(plangs).cuda(), torch.from_numpy(psp).cuda()) + return out + + def validate(): + model.eval(); res = {} + with torch.no_grad(), torch.autocast("cuda", dtype=torch.bfloat16): + for mode in ("default", "inst", "shuf"): + vr = random.Random(7); sr_ = random.Random(9); Ls, W = [], [] + for i in range(0, len(val), 16): + bt = val[i:i + 16] + if mode == "shuf": + b = [enc(m, vr, "inst", shuf=full_labels(sr_.choice(val))) for m in bt] + else: + b = [enc(m, vr, mode) for m in bt] + ce, _, _, _ = model.forward2(b) + Ls.append(float(ce) * len(bt)); W.append(len(bt)) + res[mode] = sum(Ls) / sum(W) + model.train() + res["inst_gain"] = res["shuf"] - res["inst"] # >0: the instruction carries information the model uses + res["mean"] = (res["default"] + res["inst"]) / 2 + return res + + wfn = lambda m: m["w"] # noqa + cost = lambda m: (lambda Lq: int(Lq * (1 + Lq / a.quad)))(m["ntok"] + len(m["text"]) // 2 + 40 + int(a.p_prompt * 200)) # noqa + rng = random.Random(0); prng = random.Random(3) + step, nskip, t0 = 0, 0, time.time() + logf = open(f"{a.out}/log.jsonl", "a") + v = validate(); best = v["mean"]; bad = 0 + print(json.dumps({"step": 0, "val": v}), flush=True); logf.write(json.dumps({"step": 0, "val": v}) + "\n") + total, done = a.max_steps, False + while not done: + for bi in FL.batches(items, a.tokens, rng, wfn, cost): + lrm = min(1.0, (step + 1) / a.warmup) * (0.1 + 0.9 * 0.5 * (1 + math.cos(math.pi * min(1.0, step / total)))) + for gp, b in zip(opt.param_groups, base): + gp["lr"] = b * lrm + batch = [enc(items[i], prng) for i in bi] + opt.zero_grad(set_to_none=True) + try: + with torch.autocast("cuda", dtype=torch.bfloat16): + ce, acc, adv, adv_acc = model.forward2(batch, adv_lambda=1.0) + loss = ce + a.adv_w * adv + if not torch.isfinite(loss): + nskip += 1; continue + loss.backward() + except torch.cuda.OutOfMemoryError: + batch = None; opt.zero_grad(set_to_none=True); torch.cuda.empty_cache(); nskip += 1; continue + gn = torch.nn.utils.clip_grad_norm_([p for p in model.parameters() if p.requires_grad], 1.0) + if not torch.isfinite(gn): + nskip += 1; opt.zero_grad(set_to_none=True); continue + opt.step(); step += 1 + if step % 50 == 0: + r = dict(step=step, ce=round(float(ce), 4), acc=round(float(acc), 4), gn=round(float(gn), 2), B=len(bi), + skips=nskip, min=round((time.time() - t0) / 60, 2)) + print(json.dumps(r), flush=True); logf.write(json.dumps(r) + "\n"); logf.flush() + if step % a.val_every == 0: + v = validate(); imp = v["mean"] < best - 1e-4 + print(json.dumps({"step": step, "val": v, "best": imp}), flush=True) + logf.write(json.dumps({"step": step, "val": v, "best": imp}) + "\n"); logf.flush() + torch.save(dict(model=model.state_dict(), step=step, n_spk=len(spk2id), val=v), f"{a.out}/last.pt") + if imp: + best, bad = v["mean"], 0 + os.replace(f"{a.out}/last.pt", f"{a.out}/best.pt") + else: + bad += 1 + if bad >= a.patience: + print("EARLY_STOP", step, flush=True); done = True; break + if (time.time() - t0) / 60 > a.minutes or step >= total: + done = True; break + print("TRAIN_FI_DONE", step, "best", best, "skips", nskip, flush=True) + + +class ISynth(FL.Synth): + cfg = 0.0 # classifier-free guidance strength on the instruction (0 = off) + + def __init__(self, lm_ckpt, ctrl_scale=1.0, cfg=0.0): + super().__init__(lm_ckpt) + self.cfg = cfg + self.m.__class__ = IFLM + sd = torch.load(lm_ckpt, map_location="cuda", weights_only=False)["model"] + self.ctrl = "pause_emb.weight" in sd + if self.ctrl: + add_ctrl(self.m) + self.m.pause_emb.weight.data.copy_(sd["pause_emb.weight"]); self.m.energy_emb.weight.data.copy_(sd["energy_emb.weight"]) + self.m.ctrl_scale = ctrl_scale + del sd + + text_pauses = True # pipeline step: "few/many pauses" also edits the text ([pause] tags / commas), see pause_text.py + + def tokens(self, text, voice="anchor", rate="normal", seed=0, B=1, sampler="silexcap", instruct=None): + ptok, pfeat, emb, lmp, sid = self.voice(voice) + if instruct and instruct != IT.DEFAULT and self.text_pauses: + import pause_text + pc_ = parse_controls(instruct)[1] + if pc_ in (PI["few"], PI["many"]): + text = pause_text.apply(text, "few" if pc_ == PI["few"] else "many") + ids, langs = self.tok(text) + if instruct and instruct != IT.DEFAULT: + self.m._pre = torch.tensor(pre_ids(self.tok, instruct)).cuda() + rc, pc, ec = parse_controls(instruct) if self.ctrl else (0, 0, 0) + spk = [sid, rc] + ([pc, ec] if self.ctrl else []) + else: + self.m._pre = None + spk = [sid, FL.R2I.get(None if rate == "none" else rate, 0)] + ([0, 0] if self.ctrl else []) + try: + if self.cfg > 0 and self.m._pre is not None and B == 1: + spk_u = [sid, FL.R2I["normal"]] + ([0, 0] if self.ctrl else []) + with torch.autocast("cuda", dtype=torch.bfloat16): + return generate_cfg(self.m, torch.tensor(ids).cuda(), torch.tensor(langs).cuda(), torch.tensor(spk).cuda(), + torch.tensor(spk_u).cuda(), self.m._pre, prompt=lmp, g=self.cfg, seed=seed) + with torch.autocast("cuda", dtype=torch.bfloat16): + return self.m.generate(torch.tensor(ids).cuda(), torch.tensor(langs).cuda(), torch.tensor(spk).cuda(), + prompt=lmp, B=B, seed=seed, sampler=sampler) + finally: + self.m._pre = None + + +ADH = { # condition -> instruction (held-out phrasings, not verbatim training templates) + "default": IT.DEFAULT, + "slow": "Please speak slowly and take your time.", + "fast": "Speak fast, like a quick everyday conversation.", + "few_pauses": "Say it in one smooth flow with hardly any pauses.", + "many_pauses": "Speak with plenty of pauses between phrases.", + "calm": "Use a calm, gentle, even tone.", + "lively": "Sound lively and expressive.", +} + + +def gen(a): + S = ISynth(a.lm, a.ctrl_scale, a.cfg); S.text_pauses = not a.no_text_pauses + rows = [json.loads(l) for l in open(a.rows, encoding="utf-8")] + if a.shard: + i, n = map(int, a.shard.split("/")); rows = rows[i::n] + os.makedirs(a.out, exist_ok=True) + json.dump(vars(a), open(f"{a.out}/variant.json", "w")) + for r in rows: + if os.path.exists(f"{a.out}/{r['id']}.wav"): + continue + text = r.get("tts_text") or FL.item_text(r) + toks = S.tokens(text, a.voice, a.rate, a.seed, instruct=r.get("instruct", a.instruct))[0] + sf.write(f"{a.out}/{r['id']}.wav", S.wav(toks, a.voice), S.cosy.sample_rate) + print("GEN_DONE", a.out, len(rows), flush=True) + + +def adh(a): + """adherence grid: each held-out row x each condition in ADH -> OUT//.wav""" + S = ISynth(a.lm, a.ctrl_scale, a.cfg); S.text_pauses = not a.no_text_pauses + rows = [json.loads(l) for l in open(a.rows, encoding="utf-8")] + C = a.conds.split(",") if a.conds else list(ADH) + jobs = [(c, r) for r in rows for c in C] + i, n = map(int, a.shard.split("/")); jobs = jobs[i::n] + for c, r in jobs: + d = f"{a.out}/{c}"; os.makedirs(d, exist_ok=True) + if os.path.exists(f"{d}/{r['id']}.wav"): + continue + text = r.get("tts_text") or FL.item_text(r) + toks = S.tokens(text, a.voice, "normal", a.seed, instruct=ADH[c])[0] + sf.write(f"{d}/{r['id']}.wav", S.wav(toks, a.voice), S.cosy.sample_rate) + print("ADH_DONE", i, flush=True) + + +if __name__ == "__main__": + ap = argparse.ArgumentParser() + ap.add_argument("cmd"); ap.add_argument("--out", required=True); ap.add_argument("--lm"); ap.add_argument("--init") + ap.add_argument("--rows", default=f"{HOME}/flash/score/rows_flash.jsonl") + ap.add_argument("--voice", default="anchor"); ap.add_argument("--rate", default="normal"); ap.add_argument("--seed", type=int, default=0) + ap.add_argument("--instruct", default=None); ap.add_argument("--shard") + ap.add_argument("--labels", default=f"{HOME}/fi/labels.jsonl"); ap.add_argument("--mix", default=f"{HOME}/fi/mix.json") + ap.add_argument("--minutes", type=float, default=50); ap.add_argument("--max_steps", type=int, default=4000) + ap.add_argument("--lr", type=float, default=1e-5); ap.add_argument("--lr_new", type=float, default=1e-4) + ap.add_argument("--warmup", type=int, default=200); ap.add_argument("--tokens", type=int, default=16000) + ap.add_argument("--max_tok", type=int, default=750); ap.add_argument("--adv_w", type=float, default=0.02) + ap.add_argument("--p_prompt", type=float, default=0.5); ap.add_argument("--p_default", type=float, default=0.18) + ap.add_argument("--tag_drop", type=float, default=0.5); ap.add_argument("--rate_drop", type=float, default=0.5) + ap.add_argument("--val_every", type=int, default=500); ap.add_argument("--patience", type=int, default=3) + ap.add_argument("--quad", type=float, default=600.0) + ap.add_argument("--ctrl", action="store_true", help="also feed the instruction's controls as explicit embeddings") + ap.add_argument("--lr_ctrl", type=float, default=1e-3); ap.add_argument("--ctrl_scale", type=float, default=1.0) + ap.add_argument("--cfg", type=float, default=0.0, help="classifier-free guidance strength on the instruction") + ap.add_argument("--no_text_pauses", action="store_true", help="do not edit the text for few/many-pause instructions") + ap.add_argument("--conds", default=None, help="adh: comma-separated subset of conditions") + ap.add_argument("--extra_man", default=f"{HOME}/flash/data/igbosyn_pseudo/manifest.jsonl") + ap.add_argument("--qual", default=f"{HOME}/flash/qual/utmos_train.jsonl"); ap.add_argument("--qual_drop", type=float, default=0.1) + a = ap.parse_args() + {"train": train, "gen": gen, "adh": adh}[a.cmd](a) diff --git a/flash_lm.py b/flash_lm.py new file mode 100644 index 0000000000000000000000000000000000000000..8df82c41b12e3b8fc1fbc02253db1ab7b9151035 --- /dev/null +++ b/flash_lm.py @@ -0,0 +1,532 @@ +"""Dynamic Accent Flash: CosyVoice3 (Fun-CosyVoice3-0.5B-2512, Apache-2.0) LM fine-tune + synthesis. + +Same additions as Phase 2/3 (per-token language embedding, learned speaker token after the text, rate embedding added to +the speaker vector, GRL speaker classifier on text positions, in-context same-speaker prompt p=0.5), ported to the CV3 LM: + seq = [sos, "You are a helpful assistant.<|endofprompt|>", (prompt text) text, spk(+rate), task, (prompt speech) speech, eos] +sos/task live in speech_embedding (CV3), eos = speech_token_size+1. Targets are CV3 speech tokens (s3v3.*). +Flow (DiT) + HiFT = CV3 base, zero-shot prompted with the voice clip (no flow fine-tune). + + python flash_lm.py lm --out ~/flash/runs/flash_lm --max_steps 12000 --minutes 75 + python flash_lm.py gen --lm CKPT --out DIR [--voice anchor|waxal_ibo_5|waxal_yor_5] [--rate normal] [--rows ROWS] + +Release note: in this repo use `infer.py --version flash` (it wraps Synth below). Training needs the Phase 3 data +manifest, which is not shipped; the training code is included for reference. Paths come from COSYVOICE_REPO (CosyVoice +clone with CosyVoice3 support) and CV3_DIR (Fun-CosyVoice3-0.5B-2512 snapshot). +""" +import argparse, json, math, os, random, sys, time, inspect +import numpy as np, torch, torch.nn as nn, torch.nn.functional as F, soundfile as sf, librosa + +HOME = os.path.expanduser("~") +_HERE = os.path.dirname(os.path.abspath(__file__)) +_CV = os.environ.get("COSYVOICE_REPO") or (f"{_HERE}/CosyVoice" if os.path.isdir(f"{_HERE}/CosyVoice") else f"{HOME}/CosyVoice") +sys.path[:0] = [_CV, f"{_CV}/third_party/Matcha-TTS", f"{_HERE}/code", f"{HOME}/da"] +from da.text import segments # noqa: E402 +from da.model import grad_reverse # noqa: E402 + +CV3 = os.environ.get("CV3_DIR") or (f"{_HERE}/pretrained/Fun-CosyVoice3-0.5B-2512" if os.path.isdir(f"{_HERE}/pretrained/Fun-CosyVoice3-0.5B-2512") else f"{HOME}/models/Fun-CosyVoice3-0.5B-2512") +LANGS = ["eng", "ibo", "hau", "yor", "pcm"] +L2I = {l: i for i, l in enumerate(LANGS)} +RATES = [None, "very slow", "slow", "normal", "fast", "very fast"] +R2I = {r: i for i, r in enumerate(RATES)} +TAGS = ("[pause]", "[breath]", "[quick_breath]", "[laughter]") +SIL = {1, 2, 28, 29, 55, 248, 494, 2241, 2242, 2322, 2323} # CV3 FSQ silent/breath tokens (cosyvoice/cli/model.py) +ANCHOR = "waxal_yor_4" +NEW = ["waxal", "igboapi", "open_bible_igbo", "naijas2st_igbo", "igbosyncorp"] +MAN = f"{HOME}/p3t/manifest_p3.jsonl" +PDIR = os.environ.get("DA_PROMPTS") or (f"{_HERE}/prompts" if os.path.isdir(f"{_HERE}/prompts") else f"{HOME}/hf_test/prompts") +VOICES = { + "anchor": dict(wav=f"{PDIR}/clip0_waxal_yor_yor_1258.wav" if os.path.exists(f"{PDIR}/clip0_waxal_yor_yor_1258.wav") + else f"{PDIR}/waxal_yor_yor_1258.wav", spk=ANCHOR), + "waxal_ibo_5": dict(wav=f"{PDIR}/voice_waxal_ibo_5.wav", spk="waxal_ibo_5"), + "waxal_yor_5": dict(wav=f"{PDIR}/voice_waxal_yor_5.wav", spk="waxal_yor_5"), +} +# Flash source re-weighting on top of the Phase 3 manifest weight: less Bible narration and slow phone/dictionary reads +FLASH_W = {"open_bible_igbo": 0.4, "bible_hau": 0.5, "bible_yor": 0.5, "igboapi": 0.6, "naijas2st_igbo": 0.6, + "igbosyn_pseudo": 1.0} + + +def strip_tags(t): + for g in TAGS: + t = t.replace(g, " ") + return " ".join(t.split()).replace(" ,", ",").replace(" .", ".") + + +def _sf_load_wav(wav, target_sr, min_sr=16000): + y, sr = sf.read(wav, dtype="float32", always_2d=True) + y = y.mean(1) + if sr != target_sr: + y = librosa.resample(y, orig_sr=sr, target_sr=target_sr) + return torch.from_numpy(y)[None] + + +def load_cv3(): + import cosyvoice.utils.file_utils as fu + fu.load_wav = _sf_load_wav + import cosyvoice.cli.frontend as cf + cf.load_wav = _sf_load_wav + from cosyvoice.cli.cosyvoice import CosyVoice3 + kw = dict(load_jit=False, load_trt=False, fp16=False) + sig = inspect.signature(CosyVoice3.__init__).parameters + return CosyVoice3(CV3, **{k: v for k, v in kw.items() if k in sig}) + + +class Tok: + def __init__(self, cosy): + self.t = cosy.frontend.tokenizer + self.prefix = self.t.encode("You are a helpful assistant.") + [151646] + self.cache = {} + + def __call__(self, text, default="eng"): + if text in self.cache: + return self.cache[text] + ids, langs = [], [] + for l, s in segments(text, default): + t = self.t.encode(s) + ids += t; langs += [L2I[l]] * len(t) + self.cache[text] = (ids, langs) + return ids, langs + + +class FLM(nn.Module): + def __init__(self, q, n_spk, prefix, d=896): + super().__init__() + self.q = q + self.lang_emb = nn.Embedding(len(LANGS), d); nn.init.zeros_(self.lang_emb.weight) + self.spk_emb = nn.Embedding(n_spk, d); nn.init.normal_(self.spk_emb.weight, std=0.02) + self.rate_emb = nn.Embedding(len(RATES), d, padding_idx=0); nn.init.zeros_(self.rate_emb.weight) + self.adv = nn.Sequential(nn.Linear(d, 256), nn.ReLU(), nn.Linear(256, n_spk)) + self.V = q.speech_token_size + self.EOS = q.eos_token + self.register_buffer("prefix", torch.tensor(prefix, dtype=torch.long), persistent=False) + + def embed_text(self, ids, langs): + return self.q.llm.model.model.embed_tokens(ids) + self.lang_emb(langs) + + def spkvec(self, spk): + return self.spk_emb(spk[0]) + self.rate_emb(spk[1]) + + def build(self, ids, langs, spk, sp=None, pids=None, plangs=None, psp=None): + E = self.q.speech_embedding.weight + if pids is not None: + ids = torch.cat([pids, ids]); langs = torch.cat([plangs, langs]) + pre = self.q.llm.model.model.embed_tokens(self.prefix.to(ids.device)) + parts = [E[self.q.sos][None], pre, self.embed_text(ids, langs), self.spkvec(spk)[None], E[self.q.task_id][None]] + npsp = 0 + if psp is not None: + parts.append(self.q.speech_embedding(psp)); npsp = len(psp) + if sp is not None: + parts.append(self.q.speech_embedding(sp)) + return torch.cat(parts, 0), len(pre), len(ids), npsp + + def hidden(self, x, mask): + return self.q.llm.model.model(inputs_embeds=x, attention_mask=mask, return_dict=True).last_hidden_state + + def pack(self, batch): + seqs, tgts, tpos, spks = [], [], [], [] + for b in batch: + ids, langs, spk, sp = b[:4] + pr = b[4] if len(b) > 4 else None + x, P, nt, npsp = self.build(ids, langs, spk, sp, *(pr if pr else (None, None, None))) + tgt = torch.full((x.shape[0] + 1,), -100, device=x.device, dtype=torch.long) + start = P + nt + 2 + npsp + tgt[start: start + len(sp)] = sp + tgt[start + len(sp)] = self.EOS + seqs.append(x); tgts.append(tgt[:x.shape[0]]); tpos.append((1 + P, 1 + P + nt)); spks.append(int(spk[0])) + Tm = max(s.shape[0] for s in seqs); D = seqs[0].shape[1] + X = seqs[0].new_zeros(len(seqs), Tm, D) + Y = torch.full((len(seqs), Tm), -100, device=X.device, dtype=torch.long) + M = torch.zeros(len(seqs), Tm, device=X.device, dtype=torch.long) + for i, (s, t) in enumerate(zip(seqs, tgts)): + X[i, :len(s)] = s; Y[i, :len(t)] = t; M[i, :len(s)] = 1 + return X, Y, M, tpos, spks + + def forward2(self, batch, adv_lambda=1.0): + X, Y, M, tpos, spks = self.pack(batch) + H = self.hidden(X, M) + logits = self.q.llm_decoder(H) + ce = F.cross_entropy(logits.float().reshape(-1, logits.shape[-1]), Y.reshape(-1), ignore_index=-100) + valid = Y != -100 + acc = (logits.argmax(-1)[valid] == Y[valid]).float().mean() + hs = torch.cat([H[i, a:b] for i, (a, b) in enumerate(tpos)]) + ys = torch.cat([torch.full((b - a,), spks[i], device=X.device) for i, (a, b) in enumerate(tpos)]) + al = self.adv(grad_reverse(hs.float(), adv_lambda)) + return ce, acc, F.cross_entropy(al, ys), (al.argmax(-1) == ys).float().mean() + + def seq_logp(self, batch): + """sum log p(speech tokens + eos) per item (for DPO).""" + X, Y, M, _, _ = self.pack(batch) + logits = self.q.llm_decoder(self.hidden(X, M)).float() + lp = logits.log_softmax(-1) + valid = Y != -100 + g = lp.gather(-1, Y.clamp(min=0)[..., None])[..., 0] * valid + return g.sum(-1), valid.sum(-1) + + @torch.no_grad() + def generate(self, ids, langs, spk, prompt=None, B=1, top_p=0.8, top_k=25, win=10, tau_r=0.1, seed=0, + min_ratio=2.0, max_ratio=30.0, sampler="silexcap", max_tok=900): + g = torch.Generator(device=ids.device).manual_seed(seed) + x, _, _, _ = self.build(ids, langs, spk, None, *(prompt if prompt else (None, None, None))) + inp = x[None].repeat(B, 1, 1) + n = len(ids) + min_len, max_len = int(n * min_ratio), min(int(n * max_ratio), max_tok) + past, outs, done = None, [[] for _ in range(B)], [False] * B + E = self.q.speech_embedding.weight + for i in range(max_len): + o = self.q.llm.model.model(inputs_embeds=inp, past_key_values=past, use_cache=True, return_dict=True) + past = o.past_key_values + logp = self.q.llm_decoder(o.last_hidden_state[:, -1]).float().log_softmax(-1) + eos_lp = logp[:, self.EOS].clone() + logp[:, self.V:] = -float("inf") + if i >= min_len: + logp[:, self.EOS] = eos_lp + P = logp.exp() + nxt = [] + for b in range(B): + if done[b]: + nxt.append(0); continue + p = P[b]; out = outs[b] + sp_, si = p.sort(descending=True) + keep = (sp_.cumsum(0) - sp_) < top_p + keep[top_k:] = False + cp, ci = sp_[keep], si[keep] + t = int(ci[torch.multinomial(cp / cp.sum(), 1, generator=g)]) + exempt = False + if sampler == "silexcap" and t in SIL: + run = 0 + for u in reversed(out): + if u not in SIL: + break + run += 1 + exempt = run < 25 + if not exempt and out and t != self.EOS and sum(1 for u in out[-win:] if u == t) >= win * tau_r: + t = int(torch.multinomial(p, 1, generator=g)) + if t >= self.V: + done[b] = True; nxt.append(0); continue + out.append(t); nxt.append(t) + if all(done): + break + inp = E[torch.tensor(nxt, device=ids.device)][:, None] + return outs + + +def load_model(cosy, ckpt, tok): + ck = torch.load(ckpt, map_location="cuda", weights_only=False) + m = FLM(cosy.model.llm, ck["n_spk"], tok.prefix).cuda().eval() + print("load", ckpt, m.load_state_dict(ck["model"], strict=False), flush=True) + return m, json.load(open(os.path.join(os.path.dirname(ckpt), "spk2id.json"))) + + +# ------------------------------------------------------------------ data +def load_data(splits, max_tok=750, extra_man=None, qual=None, qual_drop=0.1): + rows = [json.loads(l) for l in open(MAN, encoding="utf-8")] + if extra_man and os.path.exists(extra_man): + rows += [json.loads(l) for l in open(extra_man, encoding="utf-8")] + want = {r["id"] for r in rows if r["split"] in splits} + drop = set() + if qual and os.path.exists(qual) and "train" in splits: # drop the bottom qual_drop of UTMOS per source + Q = [json.loads(l) for l in open(qual)] + by = {} + for q in Q: + by.setdefault(q["src"], []).append(q) + for src, L in by.items(): + if src in ("cs_pairs",): + continue + thr = float(np.quantile([q["u22"] for q in L], qual_drop)) + drop |= {q["id"] for q in L if q["u22"] < thr} + print("quality filter drops", len(drop), flush=True) + src_idx, mm = {}, {} + for src in sorted({r["src"] for r in rows}): + if src in NEW: + d = f"{HOME}/p3t/{src}" + elif src == "igbosyn_pseudo": + d = f"{HOME}/flash/data/igbosyn_pseudo" + else: + d = f"{HOME}/data3/{src}" + if not os.path.exists(f"{d}/s3v3.done"): + print("missing v3 tokens", d, flush=True); continue + meta = [json.loads(l) for l in open(f"{d}/meta.jsonl", encoding="utf-8")] + tk = [json.loads(l) for l in open(f"{d}/s3v3.jsonl")] + ix = {} + for k, (m, t) in enumerate(zip(meta, tk)): + assert m["id"] == t["id"] + if m["id"] in want: + ix[m["id"]] = (t["toff"], t["ntok"]) + src_idx[src] = ix + mm[src] = np.memmap(f"{d}/s3v3.i32", dtype=np.int32, mode="r") + items = [] + for r in rows: + if r["split"] not in splits or r["src"] not in src_idx or r["id"] in drop and r["speaker"] != ANCHOR: + continue + ix = src_idx[r["src"]] + if r["id"] not in ix: + continue + toff, ntok = ix[r["id"]] + if not (5 < ntok <= max_tok): + continue + w = float(r.get("weight", 1.0)) * FLASH_W.get(r["src"], 1.0) + items.append(dict(id=r["id"], ds=r["src"], src=r["src"], lang=r["lang"], spk=r["speaker"], split=r["split"], + dur=r["dur"], text=r["text_plain"], tags=r.get("text_tags"), rate=R2I.get(r.get("rate_bin"), 0), + w=w, toff=toff, ntok=ntok)) + return items, mm + + +def tagged(text, lang): + return text if lang == "cs" else f"<{lang}>{text}" + + +def batches(items, budget, rng, wfn, cost): + idx = [] + for i, m in enumerate(items): + w = wfn(m) + idx += [i] * int(w) + ([i] if rng.random() < w - int(w) else []) + idx.sort(key=lambda i: cost(items[i]) + rng.random() * 10) + out, cur, mx = [], [], 0 + for i in idx: + L = cost(items[i]) + if cur and max(mx, L) * (len(cur) + 1) > budget: + out.append(cur); cur, mx = [], 0 + cur.append(i); mx = max(mx, L) + if cur: + out.append(cur) + rng.shuffle(out) + return out + + +VAL_SPLITS = {"val", "validation", "dev"} + + +def train_lm(a): + cosy = load_cv3(); tok = Tok(cosy) + items, mm = load_data({"train"}, a.max_tok, a.extra_man, a.qual, a.qual_drop) + val_all, mmv = load_data(VAL_SPLITS, a.max_tok) + mm.update(mmv) + spk2id = {} + for s in sorted({m["spk"] for m in items}): + spk2id[s] = len(spk2id) + n_spk = len(spk2id) + rng = random.Random(1); rng.shuffle(val_all) + val, per_l, per_s = [], {}, {} + for m in val_all: + if m["spk"] not in spk2id or m["lang"] == "cs": + continue + L = "anchor" if m["spk"] == ANCHOR else m["lang"] + if per_l.get(L, 0) >= 60 or per_s.get(m["spk"], 0) >= 15: + continue + per_l[L] = per_l.get(L, 0) + 1; per_s[m["spk"]] = per_s.get(m["spk"], 0) + 1; val.append(m) + by_spk = {} + for m in items: + if m["ntok"] <= 200 and m["lang"] != "cs": + by_spk.setdefault(m["spk"], []).append(m) + os.makedirs(a.out, exist_ok=True) + json.dump(spk2id, open(f"{a.out}/spk2id.json", "w")); json.dump(vars(a), open(f"{a.out}/args.json", "w")) + hrs = {} + for m in items: + hrs[m["src"]] = hrs.get(m["src"], 0) + m["dur"] / 3600 + print("lm items", len(items), "hours", round(sum(hrs.values()), 1), {k: round(v, 1) for k, v in hrs.items()}, + "spk", n_spk, "val", len(val), per_l, flush=True) + model = FLM(cosy.model.llm, n_spk, tok.prefix).cuda().train() + for p in list(cosy.model.flow.parameters()) + list(cosy.model.hift.parameters()): + p.requires_grad = False + new = list(model.lang_emb.parameters()) + list(model.spk_emb.parameters()) + list(model.adv.parameters()) + list(model.rate_emb.parameters()) + newids = {id(p) for p in new} + old = [p for p in model.parameters() if id(p) not in newids and p.requires_grad] + opt = torch.optim.AdamW([{"params": old, "lr": a.lr}, {"params": new, "lr": a.lr_new}], weight_decay=0.01) + base = [a.lr, a.lr_new] + + def text_of(m, r): + if m["tags"] and r is not None and r.random() >= a.tag_drop: + return m["tags"] + return strip_tags(m["text"]) + + def spk_t(m, r): + rate = m["rate"] if (r is not None and r.random() >= a.rate_drop) else 0 + return torch.tensor([spk2id[m["spk"]], rate]).cuda() + + def enc(m, prng=None, force_prompt=None, val_mode=False): + r = None if val_mode else prng + ids, langs = tok(tagged(text_of(m, r), m["lang"])) + sp = np.asarray(mm[m["ds"]][m["toff"]: m["toff"] + m["ntok"]], dtype=np.int64) + out = [torch.tensor(ids).cuda(), torch.tensor(langs).cuda(), spk_t(m, r), torch.from_numpy(sp).cuda()] + use = force_prompt if force_prompt is not None else (prng is not None and prng.random() < a.p_prompt) + if use: + c = by_spk.get(m["spk"], []) + c = [x for x in (prng or random).sample(c, min(6, len(c))) if x["id"] != m["id"]] if c else [] + if c and c[0]["ntok"] + m["ntok"] <= a.max_tok + 150: + p = c[0] + pids, plangs = tok(tagged(strip_tags(p["text"]), p["lang"])) + psp = np.asarray(mm[p["ds"]][p["toff"]: p["toff"] + p["ntok"]], dtype=np.int64) + out.append((torch.tensor(pids).cuda(), torch.tensor(plangs).cuda(), torch.from_numpy(psp).cuda())) + return out + + def validate(): + model.eval(); res = {} + with torch.no_grad(), torch.autocast("cuda", dtype=torch.bfloat16): + for mode in (False, True): + vr = random.Random(7); L, W = [], [] + for i in range(0, len(val), 16): + bt = val[i:i + 16] + ce, _, _, _ = model.forward2([enc(m, vr, force_prompt=mode, val_mode=True) for m in bt]) + L.append(float(ce) * len(bt)); W.append(len(bt)) + res["p" if mode else "np"] = sum(L) / sum(W) + model.train() + res["mean"] = (res["np"] + res["p"]) / 2 + return res + + wfn = lambda m: m["w"] * (a.anchor_w if m["spk"] == ANCHOR else 1.0) # noqa + cost = lambda m: (lambda L: int(L * (1 + L / a.quad)))(m["ntok"] + len(m["text"]) // 2 + 20 + int(a.p_prompt * 200)) # noqa + rng = random.Random(0); prng = random.Random(3) + step, nskip, noom, t0 = 0, 0, 0, time.time() + logf = open(f"{a.out}/log.jsonl", "a") + v = validate(); best = v["mean"]; bad = 0 + print(json.dumps({"step": 0, "val": v}), flush=True); logf.write(json.dumps({"step": 0, "val": v}) + "\n") + total, done = a.max_steps, False + while not done: + for bi in batches(items, a.tokens, rng, wfn, cost): + lrm = min(1.0, (step + 1) / a.warmup) * (0.1 + 0.9 * 0.5 * (1 + math.cos(math.pi * min(1.0, step / total)))) + for gp, b in zip(opt.param_groups, base): + gp["lr"] = b * lrm + batch = [enc(items[i], prng) for i in bi] + opt.zero_grad(set_to_none=True) + oom = False + try: + with torch.autocast("cuda", dtype=torch.bfloat16): + ce, acc, adv, adv_acc = model.forward2(batch, adv_lambda=min(1.0, step / 1000)) + loss = ce + a.adv_w * adv + if not torch.isfinite(loss): + nskip += 1; continue + loss.backward() + except torch.cuda.OutOfMemoryError: + oom = True + if oom: + batch = ce = acc = adv = adv_acc = loss = None + opt.zero_grad(set_to_none=True); torch.cuda.empty_cache(); noom += 1; continue + gn = torch.nn.utils.clip_grad_norm_([p for p in model.parameters() if p.requires_grad], 1.0) + if not torch.isfinite(gn): + nskip += 1; opt.zero_grad(set_to_none=True); continue + opt.step(); step += 1 + if step % 50 == 0: + r = dict(step=step, ce=round(float(ce), 4), acc=round(float(acc), 4), adv=round(float(adv), 3), + adv_acc=round(float(adv_acc), 3), gn=round(float(gn), 2), B=len(bi), lr=round(lrm * a.lr, 9), + skips=nskip, oom=noom, min=round((time.time() - t0) / 60, 2)) + print(json.dumps(r), flush=True); logf.write(json.dumps(r) + "\n"); logf.flush() + if step % a.val_every == 0: + v = validate(); imp = v["mean"] < best - 1e-4 + rec = {"step": step, "val": v, "best": imp} + print(json.dumps(rec), flush=True); logf.write(json.dumps(rec) + "\n"); logf.flush() + if imp: + best, bad = v["mean"], 0 + torch.save(dict(model={k: t for k, t in model.state_dict().items()}, step=step, n_spk=n_spk, val=v), f"{a.out}/best.pt") + else: + bad += 1 + if bad >= a.patience: + print("EARLY_STOP", step, flush=True); done = True; break + if (time.time() - t0) / 60 > a.minutes or step >= total: + done = True; break + print("TRAIN_LM_DONE", step, "best", best, "skips", nskip, "oom", noom, flush=True) + + +# ------------------------------------------------------------------ synthesis +def prompt_feats(cosy, wav): + fe = cosy.frontend + tokn, _ = fe._extract_speech_token(wav) + feat, _ = fe._extract_speech_feat(wav) + n = min(feat.shape[1] // 2, tokn.shape[1]) + return tokn[:, :n].cuda(), feat[:, :2 * n].cuda(), fe._extract_spk_embedding(wav).cuda() + + +@torch.no_grad() +def token2wav(cosy, tokens, ptok, pfeat, emb): + flow = cosy.model.flow + t = torch.tensor([tokens], dtype=torch.int32).cuda() + mel, _ = flow.inference(token=t, token_len=torch.tensor([t.shape[1]], dtype=torch.int32).cuda(), + prompt_token=ptok.int(), prompt_token_len=torch.tensor([ptok.shape[1]], dtype=torch.int32).cuda(), + prompt_feat=pfeat, prompt_feat_len=torch.tensor([pfeat.shape[1]], dtype=torch.int32).cuda(), + embedding=emb, streaming=False, finalize=True) + w, _ = cosy.model.hift.inference(speech_feat=mel, finalize=True) + return w.squeeze().float().cpu().numpy() + + +def item_text(r): + return r["text"] if r["lang"] in ("cs", "para") else f"<{r['lang']}>{r['text']}" + + +class Synth: + def __init__(self, lm_ckpt): + self.cosy = load_cv3(); self.tok = Tok(self.cosy) + self.m, self.spk2id = load_model(self.cosy, lm_ckpt, self.tok) + self.vcache = {} + vt = f"{PDIR}/flash_voices_text.json" + self.vtext = json.load(open(vt if os.path.exists(vt) else f"{_HERE}/voices_text.json", encoding="utf-8")) + + def voice(self, name): + if name not in self.vcache: + v = VOICES[name] + ptok, pfeat, emb = prompt_feats(self.cosy, v["wav"]) + ids, langs = self.tok(self.vtext[name]) + lmp = (torch.tensor(ids).cuda(), torch.tensor(langs).cuda(), ptok[0].long()) + self.vcache[name] = (ptok, pfeat, emb, lmp, self.spk2id.get(v["spk"], self.spk2id.get(ANCHOR))) + return self.vcache[name] + + def tokens(self, text, voice="anchor", rate="normal", seed=0, B=1, sampler="silexcap"): + ptok, pfeat, emb, lmp, sid = self.voice(voice) + ids, langs = self.tok(text) + spk = torch.tensor([sid, R2I.get(None if rate == "none" else rate, 0)]).cuda() + with torch.autocast("cuda", dtype=torch.bfloat16): + return self.m.generate(torch.tensor(ids).cuda(), torch.tensor(langs).cuda(), spk, prompt=lmp, B=B, seed=seed, + sampler=sampler) + + def wav(self, toks, voice="anchor"): + ptok, pfeat, emb, _, _ = self.voice(voice) + if len(toks) < 3: + toks = list(toks) + [1, 1, 1] + return token2wav(self.cosy, toks, ptok, pfeat, emb) + + +def gen(a): + S = Synth(a.lm) + rows = [json.loads(l) for l in open(a.rows, encoding="utf-8")] + if a.shard: + i, n = map(int, a.shard.split("/")); rows = rows[i::n] + os.makedirs(a.out, exist_ok=True) + json.dump(vars(a), open(f"{a.out}/variant.json", "w")) + for r in rows: + if os.path.exists(f"{a.out}/{r['id']}.wav"): + continue + text = r.get("tts_text") or item_text(r) + toks = S.tokens(text, a.voice, a.rate, a.seed)[0] + w = S.wav(toks, a.voice) + sf.write(f"{a.out}/{r['id']}.wav", w, S.cosy.sample_rate) + json.dump(dict(tokens=[toks], ntok=[len(toks)]), open(f"{a.out}/{r['id']}.tok.json", "w")) + print("GEN_DONE", a.out, len(rows), flush=True) + + +if __name__ == "__main__": + ap = argparse.ArgumentParser() + ap.add_argument("cmd") + ap.add_argument("--out", required=True) + ap.add_argument("--lm") + ap.add_argument("--rows", default=f"{HOME}/flash/score/rows_flash.jsonl") + ap.add_argument("--voice", default="anchor") + ap.add_argument("--rate", default="normal") + ap.add_argument("--seed", type=int, default=0) + ap.add_argument("--shard") + ap.add_argument("--minutes", type=float, default=75) + ap.add_argument("--max_steps", type=int, default=12000) + ap.add_argument("--lr", type=float, default=2e-5) + ap.add_argument("--lr_new", type=float, default=1e-3) + ap.add_argument("--warmup", type=int, default=300) + ap.add_argument("--tokens", type=int, default=16000) + ap.add_argument("--max_tok", type=int, default=750) + ap.add_argument("--adv_w", type=float, default=0.02) + ap.add_argument("--p_prompt", type=float, default=0.5) + ap.add_argument("--tag_drop", type=float, default=0.5) + ap.add_argument("--rate_drop", type=float, default=0.5) + ap.add_argument("--anchor_w", type=float, default=1.0) + ap.add_argument("--val_every", type=int, default=1000) + ap.add_argument("--patience", type=int, default=4) + ap.add_argument("--quad", type=float, default=600.0) + ap.add_argument("--extra_man", default=f"{HOME}/flash/data/igbosyn_pseudo/manifest.jsonl") + ap.add_argument("--qual", default=f"{HOME}/flash/qual/utmos_train.jsonl") + ap.add_argument("--qual_drop", type=float, default=0.1) + a = ap.parse_args() + {"lm": train_lm, "gen": gen}[a.cmd](a) diff --git a/frontend/__init__.py b/frontend/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a11c4d90287e97697ada0b5a9d4f28136b81eaea --- /dev/null +++ b/frontend/__init__.py @@ -0,0 +1,2 @@ +"""Dynamic Accent Flash text front-end (normalisation, Yoruba/Igbo diacritic restoration, experimental MT).""" +from .frontend import FrontEnd # noqa: F401 diff --git a/frontend/frontend.py b/frontend/frontend.py new file mode 100644 index 0000000000000000000000000000000000000000..41c03c6c807cdd2eeb4bf9ded7fcd4e0fa418a2a --- /dev/null +++ b/frontend/frontend.py @@ -0,0 +1,122 @@ +"""Dynamic Accent Flash text front-end. + + from frontend import FrontEnd + fe = FrontEnd(diac_model="models/diacritics", mt=True) + fe("The meeting is at 3:30 on 21/10/2026.", mode="codeswitch") # tags kept, numbers verbalised + fe("Ojo naa dara pupo", mode="native", lang="yor") # adds Yorùbá tone marks/underdots + fe("Please come home early today.", mode="translate", lang="ibo") # MADLAD-400 eng/pcm -> ibo + fe("I go call you tomorrow", mode="accent", lang="ibo") # English/Pidgin words, Igbo accent + +Modes + codeswitch : input already tagged … … (or untagged = one language `lang`); each segment is + normalised in its own language; Yorùbá/Igbo segments that carry no marks are diacritised. + native : the whole text is one language (`lang`); normalise + diacritise. + translate : `lang` is the target (ibo|yor|hau); the input (English or Pidgin) is translated first with + MADLAD-400-3B-MT (Apache-2.0), then normalised + diacritised (MADLAD Yorùbá is often unmarked). + accent : keep the words as written but read them with the `lang` accent (whole text tagged ), i.e. + "English words with a native accent". Numbers are verbalised in English. +Output: a tagged string for infer.py / flash_lm.py. +""" +import re +import unicodedata + +try: + from .normalize import normalize +except ImportError: # run as a script + from normalize import normalize + +SEG = re.compile(r"<(\w{3})>(.*?)", re.S) + + +def _strip(s): + return "".join(c for c in unicodedata.normalize("NFD", s) if unicodedata.category(c) != "Mn") + + +def unmarked(text, thresh=0.05): + w = re.findall(r"\w+", unicodedata.normalize("NFC", text)) + if not w: + return False + return sum(_strip(x) != unicodedata.normalize("NFC", x) for x in w) / len(w) < thresh + + +class FrontEnd: + def __init__(self, diac_model=None, mt=False, device="cuda"): + self.diac_dir, self.device = diac_model, device + self._d = self._mt = None + self.use_mt = mt + + # ---- diacritics (ByT5-small fine-tune, word-guarded) + def diacritise(self, text, lang): + if lang not in ("yor", "ibo") or not self.diac_dir: + return text + import torch + from transformers import AutoTokenizer, T5ForConditionalGeneration + if self._d is None: + self._d = (AutoTokenizer.from_pretrained(self.diac_dir), + T5ForConditionalGeneration.from_pretrained(self.diac_dir).to(self.device).eval()) + tok, m = self._d + src = unicodedata.normalize("NFC", _strip(text)) + x = tok([f"{lang}: {src}"], return_tensors="pt").to(self.device) + with torch.no_grad(): + y = m.generate(**x, max_length=int(x.input_ids.shape[1] * 1.6) + 16) + hyp = unicodedata.normalize("NFC", tok.decode(y[0], skip_special_tokens=True)) + sw, hw = src.split(), hyp.split() + if len(sw) != len(hw): + return text + return " ".join(h if unicodedata.normalize("NFC", _strip(h)) == s else s for s, h in zip(sw, hw)) + + # ---- MT (MADLAD-400-3B-MT) + def translate(self, text, tgt): + import torch + from transformers import T5ForConditionalGeneration, T5Tokenizer + if self._mt is None: + self._mt = (T5Tokenizer.from_pretrained("google/madlad400-3b-mt"), + T5ForConditionalGeneration.from_pretrained("google/madlad400-3b-mt", torch_dtype=torch.bfloat16).to(self.device).eval()) + tok, m = self._mt + code = {"ibo": "ig", "yor": "yo", "hau": "ha", "eng": "en"}[tgt] + out = [] + for sent in re.split(r"(?<=[.!?])\s+", text.strip()): + if not sent: + continue + x = tok([f"<2{code}> {sent}"], return_tensors="pt").to(self.device) + with torch.no_grad(): + y = m.generate(**x, max_new_tokens=256, num_beams=4) + out.append(tok.decode(y[0], skip_special_tokens=True)) + return " ".join(out) + + def _seg(self, lang, s): + s = normalize(s, lang) + if lang in ("yor", "ibo") and unmarked(s): + s = self.diacritise(s, lang) + return f"<{lang}>{s}" + + def __call__(self, text, mode="codeswitch", lang="eng"): + text = unicodedata.normalize("NFC", text).strip() + if mode == "translate": + if lang not in ("ibo", "yor", "hau"): + raise ValueError("translate mode needs lang in ibo|yor|hau") + plain = re.sub(r"", "", text) + return self._seg(lang, self.translate(normalize(plain, "eng"), lang)) + if mode == "accent": + plain = re.sub(r"", "", text) + return f"<{lang}>{normalize(plain, 'eng')}" + if mode == "native" or not SEG.search(text): + return self._seg(lang, re.sub(r"", "", text)) + parts, pos = [], 0 + for m in SEG.finditer(text): + if text[pos:m.start()].strip(): + parts.append(self._seg(lang, text[pos:m.start()])) + parts.append(self._seg(m.group(1), m.group(2))) + pos = m.end() + if text[pos:].strip(): + parts.append(self._seg(lang, text[pos:])) + return " ".join(parts) + + +if __name__ == "__main__": + import argparse + ap = argparse.ArgumentParser() + ap.add_argument("text"); ap.add_argument("--mode", default="codeswitch"); ap.add_argument("--lang", default="eng") + ap.add_argument("--diac_model", default=None); ap.add_argument("--cpu", action="store_true") + a = ap.parse_args() + print(FrontEnd(a.diac_model, device="cpu" if a.cpu else "cuda")(a.text, a.mode, a.lang)) diff --git a/frontend/normalize.py b/frontend/normalize.py new file mode 100644 index 0000000000000000000000000000000000000000..9d95fbb362ed065ef6314d958e81fdd90b0b7b74 --- /dev/null +++ b/frontend/normalize.py @@ -0,0 +1,308 @@ +"""Dynamic Accent Flash text normaliser: numbers, ordinals-as-cardinals, years, dates, times, currency and percent +-> words, per language (eng, pcm, ibo, hau, yor). Pure Python, no dependencies. + + from normalize import normalize + normalize("Ọ bụ ₦5,000 n'ụbọchị 21/10/2026", "ibo") + +Coverage: integers 0 .. 999,999,999 (larger stay as digits), decimals (point-by-digit), ₦/N/NGN/naira, $/USD, +£, kobo, %, dd/mm/yyyy and "21 October 2026" dates, hh:mm times, 1st/2nd... in eng/pcm. +The Igbo and Hausa systems are decimal and regular. Yorùbá uses the traditional vigesimal/subtractive system for +1-99 (with tone marks) and decimal composition above 100 (ọgọ́rùn-ún, ẹgbẹ̀rún, mílíọ̀nù + "ó lé"); the Yorùbá forms and +all date/currency phrasings need a native-speaker check before release (see TEXT_FRONTEND.md). +""" +import re +import unicodedata + +# ------------------------------------------------------------------ English / Pidgin +_E1 = "zero one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen".split() +_E10 = "_ _ twenty thirty forty fifty sixty seventy eighty ninety".split() + + +def eng(n): + if n < 20: + return _E1[n] + if n < 100: + return _E10[n // 10] + ("" if n % 10 == 0 else "-" + _E1[n % 10]) + if n < 1000: + return _E1[n // 100] + " hundred" + ("" if n % 100 == 0 else " and " + eng(n % 100)) + for v, w in ((10 ** 9, "billion"), (10 ** 6, "million"), (1000, "thousand")): + if n >= v: + r = n % v + tail = "" if r == 0 else ((" and " if r < 100 else " ") + eng(r)) + return eng(n // v) + " " + w + tail + return str(n) + + +def eng_year(n): + if 2000 <= n <= 2009: + return eng(n) + if 1100 <= n <= 2099 and n % 100 != 0: + return eng(n // 100) + " " + (("oh " + _E1[n % 100]) if n % 100 < 10 else eng(n % 100)) + return eng(n) + + +_ORD = {"one": "first", "two": "second", "three": "third", "five": "fifth", "eight": "eighth", "nine": "ninth", "twelve": "twelfth"} + + +def eng_ord(n): + w = eng(n) + head, _, last = w.rpartition(" ") if " " in w else ("", "", w) + h2, dash, l2 = last.rpartition("-") + base = l2 if dash else last + o = _ORD.get(base) or (base[:-1] + "ieth" if base.endswith("y") else base + "th") + last = (h2 + "-" + o) if dash else o + return (head + " " + last).strip() + + +# ------------------------------------------------------------------ Igbo (decimal) +_I1 = ["efu", "otu", "abụọ", "atọ", "anọ", "ise", "isii", "asaa", "asatọ", "itoolu"] + + +def ibo(n): + if n < 10: + return _I1[n] + if n < 100: + t, u = divmod(n, 10) + s = "iri" if t == 1 else "iri " + _I1[t] + return s if u == 0 else s + " na " + _I1[u] + for v, w in ((10 ** 9, "ijeri"), (10 ** 6, "nde"), (1000, "puku"), (100, "narị")): + if n >= v: + q, r = divmod(n, v) + s = w if q == 1 else w + " " + ibo(q) + return s if r == 0 else s + " na " + ibo(r) + return str(n) + + +# ------------------------------------------------------------------ Hausa (decimal) +_H1 = ["sifili", "ɗaya", "biyu", "uku", "huɗu", "biyar", "shida", "bakwai", "takwas", "tara"] +_H10 = [None, "goma", "ashirin", "talatin", "arba'in", "hamsin", "sittin", "saba'in", "tamanin", "casa'in"] + + +def hau(n): + if n < 10: + return _H1[n] + if n < 20: + return "goma" if n == 10 else "goma sha " + _H1[n - 10] + if n < 100: + t, u = divmod(n, 10) + return _H10[t] if u == 0 else _H10[t] + " da " + _H1[u] + for v, w in ((10 ** 9, "biliyan"), (10 ** 6, "miliyan"), (1000, "dubu"), (100, "ɗari")): + if n >= v: + q, r = divmod(n, v) + s = w if q == 1 else w + " " + hau(q) + return s if r == 0 else s + " da " + hau(r) + return str(n) + + +# ------------------------------------------------------------------ Yorùbá +# counting forms 1-10, then vigesimal/subtractive 11-99 (standard grammar tables); decimal composition >= 100 +_Y1 = ["òdo", "oókan", "eéjì", "ẹẹ́ta", "ẹẹ́rin", "aárùn-ún", "ẹẹ́fà", "eéje", "ẹẹ́jọ", "ẹẹ́sàn-án", "ẹẹ́wàá"] +_Y11 = {11: "ọ̀kanlá", 12: "èjìlá", 13: "ẹ̀tàlá", 14: "ẹ̀rìnlá"} +# tens: name, locative form used after lé/dín ("added to / less than") +_YT = {20: ("ogún", "lógún"), 30: ("ọgbọ̀n", "lọ́gbọ̀n"), 40: ("ogójì", "lógójì"), 50: ("àádọ́ta", "láàádọ́ta"), + 60: ("ọgọ́ta", "lọ́gọ́ta"), 70: ("àádọ́rin", "láàádọ́rin"), 80: ("ọgọ́rin", "lọ́gọ́rin"), + 90: ("àádọ́rùn-ún", "láàádọ́rùn-ún"), 100: ("ọgọ́rùn-ún", "lọ́gọ́rùn-ún")} +_YLE = {1: "ọ̀kàn", 2: "éjì", 3: "ẹ́tà", 4: "ẹ́rìn"} # +1..+4 +_YDIN = {1: "ọ̀kàn", 2: "éjì", 3: "ẹẹ́tà", 4: "ẹẹ́rìn"} # -1..-4 +_YFIVE = {20: "ẹ̀ẹ́dógún", 30: "ẹ̀ẹ́dọ́gbọ̀n", 40: "ẹ̀ẹ́dógójì", 50: "ẹ̀ẹ́dáàádọ́ta", 60: "ẹ̀ẹ́dọ́gọ́ta", 70: "ẹ̀ẹ́dáàádọ́rin", + 80: "ẹ̀ẹ́dọ́gọ́rin", 90: "ẹ̀ẹ́dáàádọ́rùn-ún", 100: "ẹ̀ẹ́dọ́gọ́rùn-ún"} # (T - 5) +_YADJ = {1: "kan", 2: "méjì", 3: "mẹ́ta", 4: "mẹ́rin", 5: "márùn-ún", 6: "mẹ́fà", 7: "méje", 8: "mẹ́jọ", 9: "mẹ́sàn-án", 10: "mẹ́wàá"} +_YH = {200: "igba", 300: "ọ̀ọ́dúnrún", 400: "irinwó", 500: "ẹ̀ẹ́dẹ́gbẹ̀ta", 600: "ẹgbẹ̀ta", 700: "ẹ̀ẹ́dẹ́gbẹ̀rin", + 800: "ẹgbẹ̀rin", 900: "ẹ̀ẹ́dẹ́gbẹ̀rún"} + + +def _y99(n): + if n <= 10: + return _Y1[n] + if n in _Y11: + return _Y11[n] + if n in _YT: + return _YT[n][0] + T = (n // 10) * 10 + u = n - T + if u <= 4: # T + u (u-lé-T) + return _YLE[u] + "lé" + _YT[T][1] + if u == 5: # (T+10) - 5 + return _YFIVE[T + 10] + return _YDIN[10 - u] + "dín" + _YT[T + 10][1] # (T+10) - (10-u) + + +def yor(n): + if n < 100: + return _y99(n) + if n < 1000: + h, r = divmod(n, 100) + s = _YT[100][0] if h == 1 else _YH[h * 100] + return s if r == 0 else s + " ó lé " + _y99(r) + for v, w in ((10 ** 9, "bílíọ̀nù"), (10 ** 6, "mílíọ̀nù"), (1000, "ẹgbẹ̀rún")): + if n >= v: + q, r = divmod(n, v) + s = w + " " + (_YADJ[q] if q in _YADJ else yor(q)) + return s if r == 0 else s + " ó lé " + yor(r) + return str(n) + + +NUM = {"eng": eng, "pcm": eng, "ibo": ibo, "hau": hau, "yor": yor} + +MONTHS = { + "eng": "January February March April May June July August September October November December".split(), + "ibo": "Jenụwarị Febụwarị Maachị Epreel Mee Juun Julaị Ọgọọst Septemba Ọktoba Novemba Disemba".split(), + "hau": "Janairu Fabrairu Maris Afrilu Mayu Yuni Yuli Agusta Satumba Oktoba Nuwamba Disamba".split(), + "yor": "Ṣẹ́rẹ́ Èrèlé Ẹrẹ̀nà Ìgbé Ẹ̀bìbí Òkúdu Agẹmọ Ògún Owewe Ọ̀wàrà Bélú Ọ̀pẹ̀".split(), +} +MONTHS["pcm"] = MONTHS["eng"] +_MIDX = {m.lower(): i for i, m in enumerate(MONTHS["eng"])} +_MIDX.update({m[:3].lower(): i for i, m in enumerate(MONTHS["eng"])}) + +CUR = { # (major, minor) per language; phrase order handled in money() + "NGN": {"eng": ("naira", "kobo"), "pcm": ("naira", "kobo"), "ibo": ("naịra", "kobo"), "hau": ("naira", "kobo"), "yor": ("náírà", "kọ́bọ̀")}, + "USD": {"eng": ("dollars", "cents"), "pcm": ("dollars", "cents"), "ibo": ("dọla", "senti"), "hau": ("dala", "santi"), "yor": ("dọ́là", "sẹ́ǹtì")}, + "GBP": {"eng": ("pounds", "pence"), "pcm": ("pounds", "pence"), "ibo": ("paụnd", "pens"), "hau": ("fam", "fens"), "yor": ("pọ́n-ùn", "pẹ́ńsì")}, +} + + +def num(n, lang): + return NUM[lang](n) if n < 10 ** 9 else str(n) + + +def money(amount, code, lang): + whole, _, frac = amount.partition(".") + whole = int(whole.replace(",", "") or 0) + major, minor = CUR[code][lang] + if lang in ("eng", "pcm"): + s = f"{num(whole, lang)} {major}" + if code == "USD" and whole == 1: + s = "one dollar" + if frac and int(frac[:2].ljust(2, "0")): + s += f" {num(int(frac[:2].ljust(2, '0')), lang)} {minor}" + return s + s = f"{major} {num(whole, lang)}" # ibo/hau/yor: currency word first ("naịra puku ise") + if lang == "yor": + s = f"{major} {yor_adj(whole)}" + if frac and int(frac[:2].ljust(2, "0")): + k = int(frac[:2].ljust(2, "0")) + s += {"ibo": " na ", "hau": " da ", "yor": " ó lé "}[lang] + f"{minor} {yor_adj(k) if lang == 'yor' else num(k, lang)}" + return s + + +def yor_adj(n): + """Yorùbá number after a noun (adjectival form for 1-10, counting form otherwise).""" + return _YADJ.get(n) or yor(n) + + +def date(d, m, y, lang): + M = MONTHS[lang][m - 1] + if lang in ("eng", "pcm"): + return f"the {eng_ord(d)} of {M}" + (f", {eng_year(y)}" if y else "") + if lang == "ibo": + return f"ụbọchị {ibo(d)} n'ọnwa {M}" + (f", afọ {ibo(y)}" if y else "") + if lang == "hau": + return f"ranar {hau(d)} ga watan {M}" + (f", shekarar {hau(y)}" if y else "") + return f"ọjọ́ {yor(d)} oṣù {M}" + (f", ọdún {yor(y)}" if y else "") + + +def clock(h, mi, lang): + if lang in ("eng", "pcm"): + return eng(h) + ("" if mi == 0 else (" oh " + eng(mi) if mi < 10 else " " + eng(mi))) + (" o'clock" if mi == 0 else "") + if lang == "ibo": + return f"elekere {ibo(h)}" + ("" if mi == 0 else f" na nkeji {ibo(mi)}") + if lang == "hau": + return f"ƙarfe {hau(h)}" + ("" if mi == 0 else f" da minti {hau(mi)}") + return f"agogo {yor_adj(h)}" + ("" if mi == 0 else f" ó lé ìṣẹ́jú {yor_adj(mi)}") + + +def percent(x, lang): + w = decimal(x, lang) + return {"eng": f"{w} percent", "pcm": f"{w} percent", "ibo": f"pasentị {w}", "hau": f"kashi {w} cikin ɗari", + "yor": f"ìdá {w} nínú ọgọ́rùn-ún"}[lang] + + +_POINT = {"eng": "point", "pcm": "point", "ibo": "ntụpọ", "hau": "digo", "yor": "ààmì"} + + +def decimal(x, lang): + a, _, b = x.replace(",", "").partition(".") + s = num(int(a or 0), lang) + if b: + s += f" {_POINT[lang]} " + " ".join(num(int(c), lang) for c in b) + return s + + +_AMT = r"\d{1,3}(?:,\d{3})+(?:\.\d+)?|\d+(?:\.\d+)?" +_RE_MONEY = re.compile(r"(?:(₦|NGN\s?|N(?=\d)|\$|USD\s?|£)\s?(" + _AMT + r")(\s?(?:k|m|bn)\b)?)|(?:(" + _AMT + r")\s?(naira|kobo|dollars?)\b)", re.I) +_RE_DATE = re.compile(r"\b(\d{1,2})[/.-](\d{1,2})[/.-](\d{4})\b") +_RE_DATE2 = re.compile(r"\b(\d{1,2})(?:st|nd|rd|th)?\s+(January|February|March|April|May|June|July|August|September|October|November|December|Jan|Feb|Mar|Apr|Jun|Jul|Aug|Sep|Sept|Oct|Nov|Dec)\.?,?\s*(\d{4})?\b", re.I) +_RE_TIME = re.compile(r"\b(\d{1,2}):(\d{2})(?:\s?(am|pm)\b)?", re.I) +_RE_TIME2 = re.compile(r"\b(\d{1,2})\s?(am|pm)\b", re.I) +_RE_PCT = re.compile(r"(" + _AMT + r")\s?%") +_RE_ORD = re.compile(r"\b(\d+)(st|nd|rd|th)\b", re.I) +_RE_YEAR = re.compile(r"\b(1[5-9]\d\d|20\d\d)\b") +_RE_NUM = re.compile(_AMT) +_DUP = re.compile(r"\b(n'elekere|elekere|karfe|ƙarfe|ranar|agogo|ọjọ́|ụbọchị)\s+(elekere|ƙarfe|ranar|agogo|ọjọ́|ụbọchị)\b", re.I) +_SCALE = {"k": 1000, "m": 10 ** 6, "bn": 10 ** 9} + + +def normalize(text, lang): + """Verbalise numbers etc. for one language segment (no tags inside).""" + lang = lang if lang in NUM else "eng" + t = unicodedata.normalize("NFC", text) + + def _money(m): + if m.group(1): + sym, amt, sc = m.group(1).strip().upper(), m.group(2), (m.group(3) or "").strip().lower() + code = {"₦": "NGN", "N": "NGN", "NGN": "NGN", "$": "USD", "USD": "USD", "£": "GBP"}[sym] + else: + amt, word, sc = m.group(4), m.group(5).lower(), "" + if word == "kobo": + k = int(amt.replace(",", "")) + return f"{num(k, lang)} kobo" if lang in ("eng", "pcm") else f"{CUR['NGN'][lang][1]} {yor_adj(k) if lang == 'yor' else num(k, lang)}" + code = "NGN" if word == "naira" else "USD" + if sc: + amt = str(int(round(float(amt.replace(",", "")) * _SCALE[sc]))) + return money(amt, code, lang) + + t = _RE_MONEY.sub(_money, t) + t = _RE_PCT.sub(lambda m: percent(m.group(1), lang), t) + t = _RE_DATE.sub(lambda m: date(int(m.group(1)), int(m.group(2)), int(m.group(3)), lang) + if 1 <= int(m.group(1)) <= 31 and 1 <= int(m.group(2)) <= 12 else m.group(0), t) + t = _RE_DATE2.sub(lambda m: date(int(m.group(1)), _MIDX[m.group(2).lower()[:3]] + 1, + int(m.group(3)) if m.group(3) else None, lang), t) + + def _time(m): + h, mi, ap = int(m.group(1)), int(m.group(2)), (m.group(3) or "").lower() + s = clock(h, mi, lang) + if ap and lang in ("eng", "pcm"): + s += " " + {"am": "a m", "pm": "p m"}[ap] + return s + + t = _RE_TIME.sub(_time, t) + t = _RE_TIME2.sub(lambda m: (eng(int(m.group(1))) + " " + {"am": "a m", "pm": "p m"}[m.group(2).lower()]) + if lang in ("eng", "pcm") else clock(int(m.group(1)), 0, lang), t) + if lang in ("eng", "pcm"): + t = _RE_ORD.sub(lambda m: eng_ord(int(m.group(1))), t) + t = _RE_YEAR.sub(lambda m: eng_year(int(m.group(1))), t) + else: + t = _RE_ORD.sub(lambda m: num(int(m.group(1)), lang), t) + t = _RE_NUM.sub(lambda m: decimal(m.group(0), lang) if "." in m.group(0) else num(int(m.group(0).replace(",", "")), lang), t) + t = _DUP.sub(lambda m: m.group(1) if m.group(1).lower().endswith(m.group(2).lower()) or m.group(1).lower() == "karfe" else m.group(0), t) + return re.sub(r"\s+", " ", t).strip() + + +def normalize_tagged(text, default="eng"): + """Apply normalize() to every … segment of a tagged string (untagged text uses `default`).""" + out, pos = [], 0 + for m in re.finditer(r"<(\w{3})>(.*?)", text, re.S): + if m.start() > pos: + out.append(normalize(text[pos:m.start()], default) if text[pos:m.start()].strip() else text[pos:m.start()]) + out.append(f"<{m.group(1)}>{normalize(m.group(2), m.group(1))}") + pos = m.end() + if pos < len(text): + rest = text[pos:] + out.append(normalize(rest, default) if rest.strip() else rest) + return "".join(out) + + +if __name__ == "__main__": + import sys + for L in ["eng", "pcm", "ibo", "hau", "yor"]: + print(L, "|", normalize("₦5,000 on 21/10/2026 at 7:30, 45% of 1,235 people, 3.5 km", L)) diff --git a/infer.py b/infer.py new file mode 100644 index 0000000000000000000000000000000000000000..1c28b727bbb482086bb9c72a5ccca15b0645f77d --- /dev/null +++ b/infer.py @@ -0,0 +1,801 @@ +#!/usr/bin/env python +"""Dynamic Accent Pro v1.1: experimental prompt-controlled, code-switching Nigerian TTS. + +New in Pro v1.1: + --instruct "..." natural-language style instruction read by the LM. Reliable: pace (slow / fast); + pauses (plenty of / hardly any) via a text step. Tone words (calm/lively) have no + measurable effect; no emotion control. e.g. --instruct "Speak slowly with plenty of pauses." + Without --instruct the LM gets its default prompt (tuned toward native pace and pauses). + --codeswitch_prompt "..." OPTIONAL text pipeline step BEFORE synthesis: a text LLM (Qwen3, Apache-2.0) rewrites plain + text as tagged code-switched text, e.g. "mostly English, Igbo greetings". This is a + pipeline step, not a native model capability; outputs are validated (tags, length, + repetition loops, back-translation meaning check) and fall back to the input text. + +Versions (`--version`): + pro (used automatically with --instruct) Flash-Instruct LM `checkpoints/pro/flash_instruct` = Flash-DPO + instruction SFT (licensed data only). + phase2/phase3 checkpoints are not in this repo (see Modularcomputing/dynamic-accent-preview). + flash (default) LM `checkpoints/flash/flash_dpo` on Fun-CosyVoice3-0.5B-2512 (Apache-2.0): Flash SFT + DPO preference + tuning (naturalness + CER + pace + pause reward), licensed data only. CV3 base flow/vocoder, zero-shot from the + voice clip. Text front-end: numbers verbalised, Yoruba/Igbo diacritics restored when missing (ByT5 model in + checkpoints/flash/diacritics_byt5). One pass per paragraph (<= ~60 words), model pauses, no post-processing. + Needs a CUDA GPU and a CosyVoice clone with CosyVoice3 support. + phase2 LM `p2_lm_B` + anchor-SFT flow `p2_flow_sft`. Phase 2 sampler, sentence stitching, edge trim, internal + silences capped at 0.6 s, 0.6 s between sentences (exactly the Phase 2 final samples). + phase3 LM `p3_lm_full` (re-cut long-form data with natural pauses, ~102 h of Igbo) + the shared Phase 2 flow (see + settings.json and checkpoints/phase3/p3_lm_full/config.json). Silence-aware sampler (silence tokens exempt from the repetition check up + to a 1 s run), the whole paragraph in ONE pass (up to ~60 words per pass), no audio post-processing: every + pause and the pace come from the model. + +Voices (`--voice`): `anchor_male` (default; WAXAL Yoruba speaker 4), `waxal_ibo_5_female`, `waxal_yor_5_female` +(see --list_voices; recommended: phase3 with a female voice, phase2 with anchor_male). A non-anchor voice is zero-shot: its clip is the flow prompt and the LM prompt, and its +speaker token is used when the LM was trained on that speaker. + + python infer.py --text "this is dynamic accent, e dey sound nice abi?" --version phase3 --out out.wav + python infer.py --text "..." --compare --out cmp.wav # -> cmp_phase2/phase3/flash.wav, same text, seed, voice + python infer.py --list_voices + python infer.py --auto_tag --tag_only --text "Omo, this traffic no be here o. Ẹ ṣé púpọ̀." # inspect / edit the tags + +Text: wrap each language span in , , , or tags. Untagged text is treated as English, unless +--auto_tag (automatic word/span language ID, see tagger.py), --force_lang or --lang_map is given. +`--pause_mode clause` / `--speed` (prosody v2: fixed gaps + WSOLA) are kept for phase2 only as an experiment; a +listening test preferred the model's own pauses. +""" + +import argparse, copy, inspect, json, os, re, sys, unicodedata + +import numpy as np +import soundfile as sf +import librosa +import torch +import torch.nn as nn +import contextlib + +HERE = os.path.dirname(os.path.abspath(__file__)) +LANGS = ["eng", "ibo", "hau", "yor", "pcm"] +L2I = {l: i for i, l in enumerate(LANGS)} +ANCHOR = "waxal_yor_4" +DEV = torch.device("cuda" if torch.cuda.is_available() else "cpu") # set_device() / --device to override +DEFAULT_SPEED = 1.0 # user listening test 2026-10-21: original model pauses preferred; clause/1.05 kept as experimental + + +def set_device(d): + global DEV + DEV = torch.device(d if d != "auto" else ("cuda" if torch.cuda.is_available() else "cpu")) + return DEV + + +def autocast(dtype): + if DEV.type == "cuda" and dtype is not None: + return torch.autocast("cuda", dtype=dtype) + return contextlib.nullcontext() # CPU: plain fp32 + + +# ------------------------------------------------------------------ text front-end (tags -> spans) +_TAG = re.compile(r"<(eng|ibo|hau|yor|pcm)>(.*?)", re.S) +_SENT = re.compile(r"(?<=[.?!])\s+") + + +def normalize(t): + t = unicodedata.normalize("NFC", t) + t = t.replace("\u2019", "'").replace("\u2018", "'").replace("\u201c", '"').replace("\u201d", '"') + return re.sub(r"\s+", " ", t).strip() + + +def segments(text, default_lang="eng"): + out, pos = [], 0 + for m in _TAG.finditer(text): + pre = text[pos:m.start()] + if pre.strip(): + out.append((default_lang, pre)) + out.append((m.group(1), m.group(2))) + pos = m.end() + if text[pos:].strip(): + out.append((default_lang, text[pos:])) + res = [] + for l, s in out: + s = normalize(s) + if not s: + continue + if res: + s = " " + s + res.append((l, s)) + return res + + +def text_tokens(tok, text, default="eng"): + ids, langs = [], [] + for l, s in segments(text, default): + t = tok(s, add_special_tokens=False)["input_ids"] + ids += t; langs += [L2I[l]] * len(t) + return ids, langs + + +def split_sentences(text): + out, cur = [], [] + for l, s in segments(text): + parts = _SENT.split(s.strip()) + for k, p in enumerate(parts): + p = p.strip() + if p: + cur.append(f"<{l}>{p}") + if (k < len(parts) - 1 or p[-1:] in ".?!") and cur: + out.append(" ".join(cur)); cur = [] + if cur: + out.append(" ".join(cur)) + return [o for o in out if o.strip()] + + +# ------------------------------------------------------------------ silence post-processing +def frame_db(y, sr, hop_s=0.01): + hop = int(sr * hop_s) + n = max(1, len(y) // hop) + fr = y[: n * hop].reshape(n, hop) + return 10 * np.log10((fr.astype(np.float64) ** 2).mean(1) + 1e-10) + + +def silence_mask(y, sr, rel_db=-40.0, hop_s=0.01): + e = frame_db(y, sr, hop_s) + return e < np.percentile(e, 98) + rel_db + + +def runs(mask): + out, i, n = [], 0, len(mask) + while i < n: + if mask[i]: + j = i + while j < n and mask[j]: + j += 1 + out.append((i, j)); i = j + else: + i += 1 + return out + + +def trim_edges(w, sr, keep_lead=0.12, keep_tail=0.18): + sil = silence_mask(w, sr) + sp = np.where(~sil)[0] + if not len(sp): + return w + a, b = sp[0] * 0.01, (sp[-1] + 1) * 0.01 + out = w[int(max(0, a - keep_lead) * sr): int(min(len(w) / sr, b + keep_tail) * sr)] + lead = a - max(0, a - keep_lead) + if lead < keep_lead: + out = np.concatenate([np.zeros(int((keep_lead - lead) * sr), out.dtype), out]) + return out + + +def cap_internal(w, sr, cap=0.6): + sil = silence_mask(w, sr) + hop = int(0.01 * sr) + R = [(a, b) for a, b in runs(sil) if a > 0 and b < len(sil) and (b - a) * 0.01 > cap] + if not R: + return w + keep, cur = [], 0 + for a, b in R: + keep.append(w[cur: a * hop + int(cap / 2 * sr)]); cur = b * hop - int(cap / 2 * sr) + keep.append(w[cur:]) + return np.concatenate(keep) + + + +# ------------------------------------------------------------------ prosody v2: clause chunks, set gaps, pitch-preserving speed +_PUNCT = re.compile(r"(?<=[,;:.?!])\s+") +GAPS = {"comma": 0.40, "sent": 0.60, "switch": 0.12} +JITTER = {"comma": 0.05, "sent": 0.03, "switch": 0.02} + + +def clause_chunks(text): + """tagged text -> [(lang, chunk_text, boundary_after)] with boundary in comma|sent|switch|None (None = end). + Chunks are cut after , ; : . ? ! and at language switches; a one-word chunk is merged into the next chunk of the + same language (one-word LM calls are unstable), keeping its comma inside.""" + raw = [] + for l, s in segments(text): + for p in _PUNCT.split(s.strip()): + if p.strip(): + raw.append([l, p.strip()]) + merged = [] + for c in raw: + if merged and merged[-1][0] == c[0] and len(merged[-1][1].split()) < 2: + merged[-1][1] = merged[-1][1] + " " + c[1] + else: + merged.append(c) + out = [] + for i, (l, t) in enumerate(merged): + if i == len(merged) - 1: + b = None + elif t[-1] in ".?!": + b = "sent" + elif t[-1] in ",;:": + b = "comma" + else: + b = "switch" + out.append((l, t, b)) + return out + + +def trim_tight(w, sr, keep=0.03): + sil = silence_mask(w, sr) + sp = np.where(~sil)[0] + if not len(sp): + return w + a, b = sp[0] * 0.01, (sp[-1] + 1) * 0.01 + return w[int(max(0.0, a - keep) * sr): int(min(len(w) / sr, b + keep) * sr)] + + +def time_stretch(w, sr, speed): + """pitch-preserving tempo change (speed > 1 = faster). WSOLA via audiotsm, else librosa's phase vocoder.""" + if abs(speed - 1.0) < 1e-3: + return w + try: + from audiotsm import wsola + from audiotsm.io.array import ArrayReader, ArrayWriter + r, wr = ArrayReader(w[None].astype(np.float32)), ArrayWriter(1) + wsola(1, speed=speed, frame_length=int(0.040 * sr), synthesis_hop=int(0.010 * sr)).run(r, wr) + return wr.data[0].astype(np.float32) + except ImportError: + return librosa.effects.time_stretch(w.astype(np.float32), rate=speed) + + +def fade(w, sr, ms=10): + n = min(len(w) // 2, int(sr * ms / 1000)) + if n > 0: + w = w.copy(); r = np.linspace(0, 1, n, dtype=np.float32) + w[:n] *= r; w[-n:] *= r[::-1] + return w + +# ------------------------------------------------------------------ CosyVoice2 base +def _sf_load_wav(wav, target_sr, min_sr=16000): + y, sr = sf.read(wav, dtype="float32", always_2d=True) + y = y.mean(1) + if sr != target_sr: + y = librosa.resample(y, orig_sr=sr, target_sr=target_sr) + return torch.from_numpy(y)[None] + + +def load_cosy(model_dir, cosyvoice_dir): + sys.path[:0] = [cosyvoice_dir, os.path.join(cosyvoice_dir, "third_party", "Matcha-TTS")] + try: # pyworld is only needed for CosyVoice training (F0 features); it often fails + import pyworld # noqa: F401 # to build, so inference runs without it + except ImportError: + import types + sys.modules["pyworld"] = types.ModuleType("pyworld") + import cosyvoice.utils.file_utils as fu + fu.load_wav = _sf_load_wav + import cosyvoice.cli.frontend as cf + cf.load_wav = _sf_load_wav + from cosyvoice.cli.cosyvoice import CosyVoice2 + kw = dict(load_jit=False, load_trt=False, fp16=False) + sig = inspect.signature(CosyVoice2.__init__).parameters + return CosyVoice2(model_dir, **{k: v for k, v in kw.items() if k in sig}) + + +def prompt_feats(cosy, wav_path): + fe = cosy.frontend + tokn, _ = fe._extract_speech_token(wav_path) + feat, _ = fe._extract_speech_feat(wav_path) + n = min(feat.shape[1] // 2, tokn.shape[1]) + feat, tokn = feat[:, :2 * n], tokn[:, :n] + emb = fe._extract_spk_embedding(wav_path) + return tokn.to(DEV), feat.to(DEV), emb.to(DEV) + + +@torch.no_grad() +def token2wav(cosy, tokens, ptok, pfeat, emb): + flow = cosy.model.flow + t = torch.tensor([tokens], dtype=torch.int32).to(DEV) + kw = dict(token=t, token_len=torch.tensor([t.shape[1]], dtype=torch.int32).to(DEV), + prompt_token=ptok, prompt_token_len=torch.tensor([ptok.shape[1]], dtype=torch.int32).to(DEV), + prompt_feat=pfeat, prompt_feat_len=torch.tensor([pfeat.shape[1]], dtype=torch.int32).to(DEV), + embedding=emb) + sig = inspect.signature(flow.inference).parameters + if "streaming" in sig: + kw["streaming"] = False + if "finalize" in sig: + kw["finalize"] = True + if "flow_cache" in sig: + kw["flow_cache"] = torch.zeros(1, 80, 0, 2).to(DEV) + mel = flow.inference(**kw) + mel = mel[0] if isinstance(mel, tuple) else mel + hs = inspect.signature(cosy.model.hift.inference).parameters + w = cosy.model.hift.inference(speech_feat=mel, **({"cache_source": torch.zeros(1, 1, 0).to(DEV)} if "cache_source" in hs else {})) + w = w[0] if isinstance(w, tuple) else w + return w.squeeze().float().cpu().numpy() + + +# ------------------------------------------------------------------ Dynamic Accent LM (Qwen2LM + language + speaker embeddings) +# S3 tokens that fill silent frames in the anchor's recordings (Phase 3 Stage 0); used by the silence-aware sampler +SIL_TOKENS = {4137, 4218, 1950, 2031, 3894, 6324, 1707, 3677, 1701, 6405, 1947, 3645, 1944, 1704, 3891, 4134, 3680, 5838, 3651} +RATES = [None, "very slow", "slow", "normal", "fast", "very fast"] + + +class DALM2(nn.Module): + def __init__(self, qlm, n_spk, d=896): + super().__init__() + self.q = qlm + self.lang_emb = nn.Embedding(len(LANGS), d) + self.spk_emb = nn.Embedding(n_spk, d) + self.rate_emb = nn.Embedding(len(RATES), d, padding_idx=0) # phase3 speed instruct; zero = none (phase2) + nn.init.zeros_(self.rate_emb.weight) + self.adv = nn.Sequential(nn.Linear(d, 256), nn.ReLU(), nn.Linear(256, n_spk)) # training-only head + self.V = qlm.speech_token_size + + def embed_text(self, ids, langs): + return self.q.llm.model.model.embed_tokens(ids) + self.lang_emb(langs) + + def build2(self, ids, langs, spk, rate=0, pids=None, plangs=None, psp=None): + sos = self.q.llm_embedding.weight[self.q.sos][None] + task = self.q.llm_embedding.weight[self.q.task_id][None] + if pids is not None: + ids = torch.cat([pids, ids]); langs = torch.cat([plangs, langs]) + sv = self.spk_emb(spk) + if rate: + sv = sv + self.rate_emb(torch.tensor(rate, device=sv.device)) + parts = [sos, self.embed_text(ids, langs), sv[None], task] + if psp is not None: + parts.append(self.q.speech_embedding(psp)) + return torch.cat(parts, 0) + + @torch.no_grad() + def generate2(self, ids, langs, spk, prompt=None, top_p=0.8, top_k=25, win=10, tau_r=0.1, seed=0, + min_ratio=2.0, max_ratio=20.0, sampler="ras", rate=0, sil_cap=25): + """sampler 'ras' = Phase 2 repetition-aware sampling; 'silexcap' = silence tokens exempt from the repetition + check while the current silence run is shorter than sil_cap frames (25 = 1.0 s), then the normal check.""" + g = torch.Generator(device=ids.device).manual_seed(seed) + x = self.build2(ids, langs, spk, rate, *(prompt if prompt else (None, None, None)))[None] + n = len(ids) + min_len, max_len = int(n * min_ratio), int(n * max_ratio) + past, out, inp = None, [], x + for i in range(max_len): + o = self.q.llm.model.model(inputs_embeds=inp, past_key_values=past, use_cache=True, return_dict=True) + past = o.past_key_values + logp = self.q.llm_decoder(o.last_hidden_state[:, -1]).float().log_softmax(-1)[0] + if i < min_len: + logp[self.V] = -float("inf") + logp[self.V + 1:] = -float("inf") + p = logp.exp() + sp, si = p.sort(descending=True) + keep = (sp.cumsum(0) - sp) < top_p + keep[top_k:] = False + cp, ci = sp[keep], si[keep] + tokid = int(ci[torch.multinomial(cp / cp.sum(), 1, generator=g)]) + exempt = False + if sampler == "silexcap" and tokid in SIL_TOKENS: + run = 0 + for t in reversed(out): + if t not in SIL_TOKENS: + break + run += 1 + exempt = run < sil_cap + if not exempt and out and sum(1 for t in out[-win:] if t == tokid) >= win * tau_r and tokid != self.V: # RAS + tokid = int(torch.multinomial(p, 1, generator=g)) + if tokid >= self.V: + break + out.append(tokid) + inp = self.q.speech_embedding.weight[tokid][None, None] + return out + + +SETTINGS_PATH = os.path.join(HERE, "settings.json") +SETTINGS = json.load(open(SETTINGS_PATH)) if os.path.exists(SETTINGS_PATH) else {} +VERSIONS = {k: {kk: vv for kk, vv in v.items() if kk != "desc"} for k, v in SETTINGS.get("versions", {}).items()} or { + "phase2": dict(lm="checkpoints/phase2/p2_lm_B/best.pt", flow="checkpoints/phase2/p2_flow_sft/best.pt", sampler="ras", + one_pass=False, postprocess=True, rate=None), + "phase3": dict(lm="checkpoints/phase3/p3_lm_full/best.pt", flow="checkpoints/phase2/p2_flow_sft/best.pt", + sampler="silexcap", one_pass=True, postprocess=False, rate="normal"), +} +RECOMMENDED = SETTINGS.get("recommended", {}) + + +def recommendation(version, voice): + """one-line hint when a version/voice pairing is not the recommended one (None when it is).""" + rec = RECOMMENDED.get(version) + if rec and voice not in rec: + return f"note: {version} is recommended with {' or '.join(rec)} ({RECOMMENDED.get('note', '')})" + return None + + +def version_cfg(name): + cfg = dict(VERSIONS[name]) + cj = os.path.join(HERE, os.path.dirname(cfg["lm"]), "config.json") + if os.path.exists(cj): # a checkpoint may override flow / rate / sampler + cfg.update({k: v for k, v in json.load(open(cj)).items() if k in cfg}) + for k in ("lm", "flow", "diacritics"): + if cfg.get(k): + cfg[k] = os.path.join(HERE, cfg[k]) + return cfg + + +# ------------------------------------------------------------------ Flash (CosyVoice3 engine) +FLASH_VOICE = {"anchor_male": "anchor", "waxal_ibo_5_female": "waxal_ibo_5", "waxal_yor_5_female": "waxal_yor_5"} + + +def canonical_voice(voice, voices_json=None): + try: + al = json.load(open(voices_json, encoding="utf-8")).get("aliases", {}) if voices_json else {} + except OSError: + al = {} + return al.get(voice, voice) + + +class FlashTTS: + """--version flash: CosyVoice3 base + Flash-DPO LM (flash_lm.Synth) behind the same say()/sr interface.""" + + def __init__(self, a, cfg): + if not torch.cuda.is_available(): + raise SystemExit("--version flash needs a CUDA GPU (phase2/phase3 also run on CPU)") + cv3 = getattr(a, "cv3_dir", "") or os.environ.get("CV3_DIR") or os.path.join(HERE, "pretrained", "Fun-CosyVoice3-0.5B-2512") + if not os.path.isdir(cv3): + from huggingface_hub import snapshot_download + snapshot_download("FunAudioLLM/Fun-CosyVoice3-0.5B-2512", local_dir=cv3) + os.environ["CV3_DIR"] = cv3 + os.environ.setdefault("COSYVOICE_REPO", a.cosyvoice_dir) + os.environ.setdefault("DA_PROMPTS", os.path.join(HERE, "prompts")) + self.instruct = getattr(a, "instruct", None) if cfg.get("instruct") else None + if cfg.get("instruct"): + import flash_instruct + g = a.guidance if getattr(a, "guidance", None) is not None else cfg.get("cfg", 2.0) + self.S = flash_instruct.ISynth(a.lm or cfg["lm"], cfg=g) + self.S.text_pauses = cfg.get("text_pauses", True) and not getattr(a, "no_text_pauses", False) + else: + if getattr(a, "instruct", None): + print("note: --instruct is ignored by this version (use --version pro)") + import flash_lm + self.S = flash_lm.Synth(a.lm or cfg["lm"]) + self.cosy = self.tok = None # not shareable with the CosyVoice2 versions + self.m = self.S.m + self.sr = self.S.cosy.sample_rate + r = a.rate if a.rate != "default" else cfg.get("rate", "normal") + self.rate = RATES.index(None if r == "none" else r) if (None if r == "none" else r) in RATES else 0 + self.fe_mode = getattr(a, "frontend", "codeswitch") + self.fe_lang = getattr(a, "frontend_lang", "eng") + if self.fe_mode == "translate" and not getattr(a, "experimental_translate", False): + raise SystemExit("--frontend translate is EXPERIMENTAL and off by default (MADLAD-400 loops on long inputs and is " + "weak on Pidgin). Add --experimental_translate to use it anyway.") + from frontend import FrontEnd + diac = cfg.get("diacritics") if not getattr(a, "no_diacritics", False) else None + self.fe = FrontEnd(diac if diac and os.path.isdir(diac) else None, mt=self.fe_mode == "translate") + self.tail_pad = getattr(a, "tail_pad", 0) + self.set_voice(a.voice, a.voices) + + def set_voice(self, voice, voices_json=None): + v = canonical_voice(voice, voices_json) + if v not in FLASH_VOICE: + raise SystemExit(f"unknown voice '{voice}' for flash; available: {', '.join(FLASH_VOICE)}") + self.voice = FLASH_VOICE[v] + + def frontend(self, text): + return self.fe(text, self.fe_mode, self.fe_lang) + + def say(self, tagged_text, seed=0, return_info=False, **_): + t = self.frontend(tagged_text) + print(f"[flash front-end] {t}") + rate = RATES[self.rate] + ws = [] + for i, ch in enumerate(word_chunks(t)): + if self.instruct: + toks = self.S.tokens(ch, self.voice, rate or "none", seed + 1000 * i, instruct=self.instruct)[0] + else: + toks = self.S.tokens(ch, self.voice, rate or "none", seed + 1000 * i)[0] + pad = int(getattr(self, "tail_pad", 0) or 0) + if pad > 0: # opt-in: trailing silence tokens so the last phone is not cut + toks = list(toks) + [1] * pad + ws.append(self.S.wav(toks, self.voice)) + w = np.concatenate(ws).astype(np.float32) + return (w, None) if return_info else w + + +def word_chunks(text, max_words=60): + """one pass per <= max_words, split only at sentence ends (a whole paragraph usually fits in one pass).""" + sents = split_sentences(text) + out, cur, n = [], [], 0 + for s in sents: + w = len(re.sub(r"<[^>]+>", " ", s).split()) + if cur and n + w > max_words: + out.append(" ".join(cur)); cur, n = [], 0 + cur.append(s); n += w + if cur: + out.append(" ".join(cur)) + return out or [text] + + +class DynamicAccent: + def __init__(self, model_dir, cosyvoice_dir, lm_ckpt, flow_ckpt, prompts_json, dtype="auto", sent_pause=0.6, cap=0.6, + sampler="ras", one_pass=False, postprocess=True, rate=None, cosy=None, tok=None, copy_llm=False): + from transformers import AutoTokenizer + self.tok = tok or AutoTokenizer.from_pretrained(os.path.join(model_dir, "CosyVoice-BlankEN")) + self.cosy = cosy or load_cosy(model_dir, cosyvoice_dir) + self.sr = self.cosy.sample_rate + self.flow_sd = torch.load(flow_ckpt, map_location=DEV, weights_only=False)["flow"] + self.cosy.model.flow.load_state_dict(self.flow_sd) + ck = torch.load(lm_ckpt, map_location=DEV, weights_only=False) + self.spk2id = json.load(open(os.path.join(os.path.dirname(lm_ckpt), "spk2id.json"))) + # two versions resident at once (app.py) must not share LM weights: copy_llm=True gives each its own copy. + # The CLI runs versions one after the other and reloads the full LM state in place (saves ~2 GB RAM). + qlm = copy.deepcopy(self.cosy.model.llm) if copy_llm else self.cosy.model.llm + self.m = DALM2(qlm, ck["n_spk"]).to(DEV).eval() + miss = self.m.load_state_dict(ck["model"], strict=False) + assert not [k for k in miss.missing_keys if not k.startswith("rate_emb")], miss + del ck + if DEV.type != "cuda": + dtype = "fp32" + elif dtype == "auto": + dtype = "bf16" if torch.cuda.is_bf16_supported() else "fp16" + self.dtype = {"bf16": torch.bfloat16, "fp16": torch.float16, "fp32": None}[dtype] + self.sent_pause, self.cap = sent_pause, cap + self.sampler, self.one_pass, self.postprocess = sampler, one_pass, postprocess + self.rate = RATES.index(rate) if rate in RATES else 0 + meta = json.load(open(prompts_json, encoding="utf-8")) + root = os.path.dirname(os.path.dirname(os.path.abspath(prompts_json))) + self.prompts = [] + for p in meta["prompts"]: + ids, langs = text_tokens(self.tok, p["tagged"]) + lmp = (torch.tensor(ids).to(DEV), torch.tensor(langs).to(DEV), torch.tensor(p["s3_tokens"], dtype=torch.int64).to(DEV)) + self.prompts.append(dict(id=p["id"], lm=lmp, flow=prompt_feats(self.cosy, os.path.join(root, p["wav"])), spk=ANCHOR)) + self.voice = None + + def use_flow(self): + self.cosy.model.flow.load_state_dict(self.flow_sd) # two versions may share one CosyVoice2 instance + + def set_voice(self, voice, voices_json=None): + """'anchor' or an id from voices.json: its clip is the flow + LM prompt (zero-shot).""" + meta = json.load(open(voices_json, encoding="utf-8")) if voices_json else {"voices": {}} + voice = meta.get("aliases", {}).get(voice, voice) + if voice in (None, "anchor", "anchor_male"): + self.voice = None; return + if voice not in meta["voices"]: + raise SystemExit(f"unknown voice '{voice}'; available: {', '.join(meta['voices'])}") + V = meta["voices"][voice] + root = os.path.dirname(os.path.dirname(os.path.abspath(voices_json))) + ptok, pfeat, pemb = prompt_feats(self.cosy, os.path.join(root, V["wav"])) + ids, langs = text_tokens(self.tok, V["tagged"]) + lmp = (torch.tensor(ids).to(DEV), torch.tensor(langs).to(DEV), ptok[0].long()) + self.voice = dict(id=voice, lm=lmp, flow=(ptok, pfeat, pemb), spk=V["spk"] if V["spk"] in self.spk2id else ANCHOR) + + def _prompt(self, prompt_k): + return self.voice or self.prompts[prompt_k % len(self.prompts)] + + def one(self, tagged_text, seed, prompt_k, use_lm_prompt=True, lm_prompt=None): + ids, langs = text_tokens(self.tok, tagged_text) + P = self._prompt(prompt_k) + with autocast(self.dtype): + sp = self.m.generate2(torch.tensor(ids).to(DEV), torch.tensor(langs).to(DEV), torch.tensor(self.spk2id[P["spk"]]).to(DEV), + prompt=(lm_prompt or P["lm"]) if use_lm_prompt else None, seed=seed, sampler=self.sampler, + rate=self.rate, max_ratio=30.0 if self.one_pass else 20.0) + if len(sp) < 3: + sp = sp + [0] * 3 + self.last_tokens = sp + return token2wav(self.cosy, sp, *P["flow"]) + + def say(self, tagged_text, seed=0, prompt_k=0, stitch=True, pause_mode="model", speed=1.0, return_info=False): + self.use_flow() + if self.one_pass: # phase3: the model makes every pause; nothing is trimmed or inserted + ws, prev = [], None + for i, ch in enumerate(word_chunks(tagged_text)): + w = self.one(ch, seed + 1000 * i, prompt_k, lm_prompt=prev) + ws.append(w) + ids, langs = text_tokens(self.tok, ch) # next chunk continues from this one (model sets the gap) + if len(self.last_tokens) <= 400: + prev = (torch.tensor(ids).to(DEV), torch.tensor(langs).to(DEV), torch.tensor(self.last_tokens).to(DEV)) + w = np.concatenate(ws) + return (w, None) if return_info else w + if pause_mode == "clause": + return self.say_clause(tagged_text, seed, prompt_k, speed, return_info) + w = self.say_model(tagged_text, seed, prompt_k, stitch) + w = time_stretch(w, self.sr, speed) + return (w, None) if return_info else w + + def say_clause(self, tagged_text, seed=0, prompt_k=0, speed=1.10, return_info=False): + rng = np.random.default_rng(seed) + chunks = clause_chunks(tagged_text) + out, info, lead = [], [], 0.03 + for i, (l, t, b) in enumerate(chunks): + w = self.one(f"<{l}>{t}", seed + 1000 * i, prompt_k) + w = cap_internal(trim_tight(w, self.sr), self.sr, self.cap) + w = fade(time_stretch(w, self.sr, speed), self.sr) + if i == 0: + out.append(np.zeros(int(0.12 * self.sr), np.float32)) + out.append(w) + if b is not None: + g = GAPS[b] + rng.uniform(-JITTER[b], JITTER[b]) + out.append(np.zeros(max(0, int((g - 2 * lead) * self.sr)), np.float32)) + else: + out.append(np.zeros(int(0.15 * self.sr), np.float32)) + info.append(dict(lang=l, text=t, boundary=b)) + w = np.concatenate(out) + return (w, info) if return_info else w + + def say_model(self, tagged_text, seed=0, prompt_k=0, stitch=True): + sents = split_sentences(tagged_text) if stitch else [tagged_text] + ws = [cap_internal(trim_edges(self.one(s, seed + 1000 * i, prompt_k), self.sr), self.sr, self.cap) + for i, s in enumerate(sents)] + if len(ws) == 1: + return ws[0] + out = [] + for i, w in enumerate(ws): + if i: + out.append(np.zeros(max(0, int((self.sent_pause - 0.30) * self.sr)), np.float32)) + out.append(w) + return np.concatenate(out) + + +def build(version, a, cosy=None, tok=None, copy_llm=False): + cfg = version_cfg(version) + if cfg.get("engine") == "cosyvoice3": + return FlashTTS(a, cfg) + lm = a.lm or cfg["lm"]; flow = a.flow or cfg["flow"] + S = DynamicAccent(a.model_dir, a.cosyvoice_dir, lm, flow, a.prompts, a.dtype, sampler=cfg["sampler"], + one_pass=cfg["one_pass"] and not a.stitch, postprocess=cfg["postprocess"], + rate=a.rate if a.rate != "default" else cfg["rate"], cosy=cosy, tok=tok, copy_llm=copy_llm) + S.set_voice(a.voice, a.voices) + return S + + +def list_voices(voices_json): + V = json.load(open(voices_json, encoding="utf-8"))["voices"] if os.path.exists(voices_json) else {} + print("voices (--voice):") + for k, v in V.items(): + print(f" {k:20s} {v.get('desc', '')}") + print("\nversions (--version):") + for k, v in SETTINGS.get("versions", {}).items(): + print(f" {k:20s} {v.get('desc', '')}") + if RECOMMENDED: + print("\nrecommended pairing: " + "; ".join(f"{k} -> {', '.join(v)}" for k, v in RECOMMENDED.items() if isinstance(v, list))) + print(" " + RECOMMENDED.get("note", "")) + + +def instruct_lang(instr): + """single language named in an instruction (None when none or a mix is asked for).""" + t = instr.lower() + names = {"igbo": "ibo", "yoruba": "yor", "hausa": "hau", "pidgin": "pcm"} + hit = [v for k, v in names.items() if k in t] + if len(hit) == 1 and not any(w in t for w in ("mix", "english", "code-switch", "switch")): + return hit[0] + return None + + +def instruct_gender(instr): + t = " " + instr.lower() + " " + if any(w in t for w in (" female", " woman", " woman's", " women")): + return "female" + if any(w in t for w in (" male ", " male,", " man ", " man's")): + return "male" + return None + + +def prepare_text(a): + """apply --auto_tag / --force_lang / --lang_map; returns the tagged text that will be synthesised.""" + from tagger import Tagger, parse_map, TAGGED + text = a.text + if a.force_lang: + from tagger import strip_tags + return f"<{a.force_lang}>{strip_tags(text)}" + if a.auto_tag or a.lang_map: + if TAGGED.search(text) and not a.auto_tag: + return text + from tagger import strip_tags + T = Tagger(None if a.lid == "none" else a.lid, parse_map(a.lang_map)) + return T.tag(strip_tags(text)) + return text + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--text", help="text; tagged ('Good morning, how body?') or plain with --auto_tag") + ap.add_argument("--version", default=SETTINGS.get("default_version", "phase3"), choices=sorted(VERSIONS), + help=f"model version (default {SETTINGS.get('default_version', 'phase3')})") + ap.add_argument("--compare", action="store_true", + help="render the same text, seed and voice with every version in --compare_versions -> _.wav") + ap.add_argument("--compare_versions", default="flash,pro", help="versions for --compare (comma-separated)") + ap.add_argument("--instruct", default=None, + help='pro only: natural-language style instruction, e.g. "Speak quickly with few pauses, lively tone."') + ap.add_argument("--codeswitch_prompt", default=None, + help='optional text LLM step: rewrite plain --text as code-switched text, e.g. "mostly English, Igbo greetings"') + ap.add_argument("--guidance", type=float, default=None, + help="pro: classifier-free guidance strength on --instruct (default 2.0 from settings.json; 0 = off)") + ap.add_argument("--no_text_pauses", action="store_true", + help="pro: do not edit the text for few/many-pause instructions (model-only pause control is weak)") + ap.add_argument("--cs_llm", default=os.environ.get("CS_LLM", "Qwen/Qwen3-32B"), + help="text LLM for --codeswitch_prompt (Apache-2.0 Qwen3; Qwen/Qwen3-14B needs less GPU memory)") + ap.add_argument("--voice", default=SETTINGS.get("default_voice", "anchor_male"), + help="anchor_male | waxal_ibo_5_female | waxal_yor_5_female (see --list_voices)") + ap.add_argument("--list_voices", action="store_true", help="list voices, versions and the recommended pairing") + ap.add_argument("--rate", default="default", choices=["default", "none", "very slow", "slow", "normal", "fast", "very fast"], + help="phase3/flash speed instruction to the LM (default: the version's setting, 'normal'; 'none' = no instruction)") + # flash text front-end + ap.add_argument("--frontend", default="codeswitch", choices=["codeswitch", "native", "accent", "translate"], + help="flash only: codeswitch (tagged/one-language text), native (whole text in --frontend_lang), accent " + "(words as written, read with the --frontend_lang accent), translate (EXPERIMENTAL, off by default)") + ap.add_argument("--frontend_lang", default="eng", choices=LANGS, help="flash front-end language for untagged text / native / accent / translate target") + ap.add_argument("--no_diacritics", action="store_true", help="flash: do not restore missing Yoruba/Igbo diacritics") + ap.add_argument("--experimental_translate", action="store_true", + help="flash: allow --frontend translate (MADLAD-400-3B-MT, ~12 GB download; loops on long inputs, weak on Pidgin)") + ap.add_argument("--cv3_dir", default=os.environ.get("CV3_DIR", os.path.join(HERE, "pretrained", "Fun-CosyVoice3-0.5B-2512")), + help="flash: stock FunAudioLLM/Fun-CosyVoice3-0.5B-2512 snapshot (downloaded on first run)") + # code-switching control + ap.add_argument("--auto_tag", action="store_true", + help="tag untagged mixed text automatically (word/span language ID), print it, then synthesise") + ap.add_argument("--tag_only", action="store_true", help="with --auto_tag: print the tagged text and exit (edit it, then pass it back as --text)") + ap.add_argument("--force_lang", choices=LANGS, help="read the whole text in one language (tags are ignored)") + ap.add_argument("--lang_map", default="", help='per-word language overrides for the tagger, e.g. "omo=pcm,sha=pcm"') + ap.add_argument("--lid", default="none", choices=["none", "glotlid"], + help="optional LID model for --auto_tag (glotlid: cis-lmu/glotlid, Apache-2.0, 1.7 GB, pip install fasttext)") + ap.add_argument("--seed", type=int, default=0) + ap.add_argument("--tail_pad", type=int, default=0, + help="flash/pro: append N silence speech tokens (25 per second) before the vocoder so the final syllable " + "is not clipped; 6 removed measured clipped endings in testing (opt-in, default 0)") + ap.add_argument("--out", default="out.wav") + ap.add_argument("--prompt_clip", type=int, default=0, help="anchor prompt clip 0-3 (final systems: 0)") + ap.add_argument("--model_dir", default=os.environ.get("COSYVOICE2_DIR", os.path.join(HERE, "pretrained", "CosyVoice2-0.5B")), + help="stock FunAudioLLM/CosyVoice2-0.5B snapshot (downloaded on first run)") + ap.add_argument("--cosyvoice_dir", default=os.environ.get("COSYVOICE_REPO", os.path.join(HERE, "CosyVoice")), + help="clone of github.com/FunAudioLLM/CosyVoice (with submodules)") + ap.add_argument("--lm", default="", help="override the version's LM checkpoint") + ap.add_argument("--flow", default="", help="override the version's flow checkpoint") + ap.add_argument("--prompts", default=os.path.join(HERE, "prompts", "prompts.json")) + ap.add_argument("--voices", default=os.path.join(HERE, "prompts", "voices.json")) + ap.add_argument("--dtype", default="auto", choices=["auto", "bf16", "fp16", "fp32"], + help="LM autocast dtype on GPU (samples: bf16; auto picks fp16 on GPUs without bf16). CPU always runs fp32") + ap.add_argument("--stitch", action="store_true", help="phase3: generate sentence by sentence instead of one pass") + ap.add_argument("--no_stitch", action="store_true", help="phase2: generate multi-sentence text in one pass") + ap.add_argument("--pause_mode", default="model", choices=["clause", "model"], help="phase2 only; clause = experimental prosody v2 (not recommended)") + ap.add_argument("--speed", type=float, default=DEFAULT_SPEED, help="phase2 only; pitch-preserving tempo factor (1.0 = as trained; not recommended)") + a = ap.parse_args() + a.voice_set = any(x == "--voice" or x.startswith("--voice=") for x in sys.argv) + a.version_set = any(x == "--version" or x.startswith("--version=") for x in sys.argv) + if a.instruct and not a.version_set and not a.compare and "pro" in VERSIONS: + a.version = "pro"; print("[instruct] using --version pro (instruction-controlled)") + if a.list_voices: + list_voices(a.voices); return + if not a.text: + ap.error("--text is required") + if a.codeswitch_prompt: + from cs_frontend import CodeSwitchFrontEnd, TAG + from tagger import strip_tags + fe = CodeSwitchFrontEnd(model=a.cs_llm) + cs_text, info = fe(strip_tags(a.text), a.codeswitch_prompt) + print(f"[code-switch front end] {cs_text}" + ("" if info["ok"] else f" ({info['warning']})")) + del fe; torch.cuda.empty_cache() + a.text = cs_text + elif a.instruct and not a.force_lang: + il = instruct_lang(a.instruct) + from tagger import TAGGED + if il and not TAGGED.search(a.text) and not a.auto_tag: + a.force_lang = il # "Speak in Igbo ..." on untagged text -> read it as Igbo + if a.instruct and not a.voice_set: + g = instruct_gender(a.instruct) + if g == "female": + a.voice = "waxal_ibo_5_female"; print("[instruct] female voice requested -> --voice waxal_ibo_5_female") + text = prepare_text(a) + if text != a.text or a.auto_tag: + print(f"[tagged] {text}") + if a.tag_only: + return + versions = [v.strip() for v in a.compare_versions.split(",") if v.strip()] if a.compare else [a.version] + bad = [v for v in versions if v not in VERSIONS] + if bad: + ap.error(f"unknown version(s) {bad}; available: {sorted(VERSIONS)}") + if any(VERSIONS[v].get("engine") != "cosyvoice3" for v in versions) and not os.path.isdir(a.model_dir): + from huggingface_hub import snapshot_download + snapshot_download("FunAudioLLM/CosyVoice2-0.5B", local_dir=a.model_dir) + cosy = tok = None + base, ext = os.path.splitext(a.out) + for v in versions: + hint = recommendation(v, json.load(open(a.voices)).get("aliases", {}).get(a.voice, a.voice) if os.path.exists(a.voices) else a.voice) + if hint: + print(hint) + S = build(v, a, cosy, tok) + if S.cosy is not None: + cosy, tok = S.cosy, S.tok + w = S.say(text, seed=a.seed, prompt_k=a.prompt_clip, stitch=not a.no_stitch, pause_mode=a.pause_mode, + speed=a.speed if v == "phase2" else 1.0) + out = f"{base}_{v}{ext or '.wav'}" if a.compare else a.out + sf.write(out, w, S.sr) + print(f"[{v}] wrote {out} ({len(w) / S.sr:.2f} s, {S.sr} Hz, voice={a.voice}, device={DEV.type})") + del S.m + if isinstance(S, FlashTTS): + del S.S + del S; torch.cuda.empty_cache() + + +if __name__ == "__main__": + main() diff --git a/instruct_text.py b/instruct_text.py new file mode 100644 index 0000000000000000000000000000000000000000..6053dfb6ab68fde724ca90b4940197241d53aad2 --- /dev/null +++ b/instruct_text.py @@ -0,0 +1,130 @@ +"""Natural-language instruction templates for Flash-Instruct. +labels: dict(lang=+>, rate=slow|normal|fast|None, pause=few|normal|many|None, + energy=calm|neutral|lively|None, gender=male|female|None) +make(labels, rng) -> one instruction string (many surface forms per label combination). +DEFAULT = the original CosyVoice3 system prompt; ~18% of training items keep it. +""" +import random + +DEFAULT = "You are a helpful assistant." +LN = {"ibo": "Igbo", "yor": "Yoruba", "hau": "Hausa", "pcm": "Nigerian Pidgin", "eng": "Nigerian English"} + +RATE = { + "slow": ["slowly", "at an unhurried pace", "at a slow, relaxed pace", "taking your time", "at a gentle, slow pace", + "a little slower than usual", "slowly and deliberately"], + "normal": ["at a natural pace", "at a normal speaking pace", "at a steady, everyday pace", "at a comfortable pace"], + "fast": ["quickly", "at a brisk pace", "fast, like quick everyday conversation", "at a quick pace", + "a little faster than usual", "briskly"], +} +PAUSE = { + "few": ["with few pauses", "in one smooth flow with hardly any pauses", "without stopping much", + "with minimal pausing", "running the phrases together"], + "normal": ["with natural pauses", "with normal phrasing", "pausing where it feels natural"], + "many": ["with plenty of pauses", "pausing often between phrases", "with frequent pauses", + "with lots of breaks between phrases", "with deliberate pauses"], +} +ENERGY = { + "calm": ["calm", "soft and calm", "gentle, even-toned", "relaxed and low-key", "quiet and steady"], + "neutral": ["neutral", "plain, matter-of-fact", "even", "straightforward"], + "lively": ["lively", "animated", "expressive and energetic", "bright and lively", "upbeat, expressive"], +} +GENDER = {"male": ["male", "man's"], "female": ["female", "woman's"]} + + +def art(w): + return ("an " if w[:1].lower() in "aeiou" else "a ") + w + + +def lang_phrase(lang, rng): + if lang.startswith("cs:"): + a, b = lang[3:].split("+") + A, B = LN.get(a, a), LN.get(b, b).replace("Nigerian English", "English") + return rng.choice([f"an {A}-{B} mix" if A[0] in "AEIOU" else f"a {A}-{B} mix", f"mixed {A} and {B}", + f"{A} with some {B} mixed in", f"code-switched {A} and {B}"]) + return LN.get(lang, lang) + + +def make(lab, rng=random): + parts = {} + if lab.get("rate"): + parts["rate"] = rng.choice(RATE[lab["rate"]]) + if lab.get("pause"): + parts["pause"] = rng.choice(PAUSE[lab["pause"]]) + if lab.get("energy"): + parts["energy"] = rng.choice(ENERGY[lab["energy"]]) + lang = lang_phrase(lab["lang"], rng) if lab.get("lang") else None + g = rng.choice(GENDER[lab["gender"]]) if lab.get("gender") else None + tone = parts.get("energy") + adv = [parts[k] for k in ("rate", "pause") if k in parts] + advs = ", ".join(adv) + T = [] + # template family 1: imperative sentence + s = "Speak" + if lang: + s += f" in {lang}" + (f", sounding {tone}," if tone else "") + elif tone: + s += f" in {art(tone)} tone" + if advs: + s += f" {advs}" + T.append(s + ".") + # family 2: read-this style + s = "Read this" + if advs: + s += f" {advs}" + if tone: + s += f", in {art(tone)} voice" + if lang: + s += f". The language is {lang}" + T.append(s + ".") + # family 3: descriptive/caption style + d = [] + if g: + d.append(f"A {g} voice") + else: + d.append("A speaker") + if lang: + d.append(f"speaking {lang}") + s = " ".join(d) + if advs: + s += f", {advs}" + if tone: + s += f"; the delivery is {tone}" if (lang or advs) else f" with {art(tone)} delivery" + T.append(s + ".") + # family 4: tag list + tags = [] + if lang: tags.append(f"language: {lang}") + if "rate" in parts: tags.append(f"pace: {lab['rate']}") + if "pause" in parts: tags.append(f"pauses: {lab['pause']}") + if tone: tags.append(f"tone: {lab['energy']}") + if g: tags.append(f"voice: {lab['gender']}") + if tags: + T.append("Style - " + "; ".join(tags) + ".") + # family 5: polite request + s = "Please say this" + if lang: + s += f" in {lang}" + if tone: + s += f" with {art(tone)} delivery" + if advs: + s += f", {advs}" + T.append(s + ".") + out = rng.choice(T) + return out[0].upper() + out[1:] + + +def sample_labels(full, rng, p=None): + """drop each control independently so instructions mention subsets; keep at least one.""" + p = p or dict(lang=0.55, rate=0.75, pause=0.7, energy=0.6, gender=0.3) + while True: + lab = {k: (v if (v and rng.random() < p.get(k, 0.5)) else None) for k, v in full.items()} + if any(lab.values()): + return lab + + +if __name__ == "__main__": + r = random.Random(0) + full = dict(lang="cs:ibo+eng", rate="slow", pause="many", energy="calm", gender="female") + for _ in range(12): + print(make(sample_labels(full, r), r)) + for l in ["ibo", "yor", "hau", "pcm", "eng"]: + print(make(dict(lang=l, rate="fast", pause="few", energy="lively"), r)) diff --git a/pause_text.py b/pause_text.py new file mode 100644 index 0000000000000000000000000000000000000000..725da1e841d0e44cd2d2a288c80ab6fae05bcedd --- /dev/null +++ b/pause_text.py @@ -0,0 +1,42 @@ +"""Text-side pause control (pipeline step): 'many pauses' -> insert the model's trained [pause] tag at commas and +every ~5 words inside long phrases; 'few pauses' -> drop commas/semicolons and [pause] tags inside sentences.""" +import re +TAGRE = re.compile(r"(<\w+>)(.*?)()", re.S) + + +def _many(s, every=5): + out = [] + for sent in re.split(r"(?<=[.!?])\s+", s.strip()): + parts = re.split(r"(,|;|:)", sent) + buf = [] + for p in parts: + if p in (",", ";", ":"): + buf.append(p + " [pause]"); continue + w = p.split(); chunk = [] + for i, x in enumerate(w): + chunk.append(x) + if (i + 1) % every == 0 and i + 1 < len(w) - 1: + chunk.append("[pause]") + buf.append(" ".join(chunk)) + out.append(" ".join(b for b in buf if b).replace(" ,", ",").replace(" ;", ";")) + return " [pause] ".join(out) if len(out) > 1 else out[0] + + +def _few(s): + s = s.replace("[pause]", " ").replace("[breath]", " ") + s = re.sub(r"\s*[,;:]\s*", " ", s) + return " ".join(s.split()) + + +def apply(text, mode): + f = _many if mode == "many" else _few if mode == "few" else None + if f is None: + return text + if TAGRE.search(text): + return TAGRE.sub(lambda m: m.group(1) + f(m.group(2)) + m.group(3), text) + return f(text) + + +if __name__ == "__main__": + t = "Ndewo nụ, ụmụnna m. Tonight we will talk about the new market road and the school fees, ma ọ bụrụ na ego ezughị." + print(apply(t, "many")); print(apply(t, "few")) diff --git a/prompts/clip0_waxal_yor_yor_1258.wav b/prompts/clip0_waxal_yor_yor_1258.wav new file mode 100644 index 0000000000000000000000000000000000000000..00097eb3fe91c1aa00c6f2a3d693c578e4e3450c --- /dev/null +++ b/prompts/clip0_waxal_yor_yor_1258.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e7e368595eb4c267f124164c7d8a83b03462d549c5efd235dd8ec6eb557dc418 +size 285164 diff --git a/prompts/clip1_waxal_yor_yor_1205.wav b/prompts/clip1_waxal_yor_yor_1205.wav new file mode 100644 index 0000000000000000000000000000000000000000..43265158581498ebcc18678159cb3896c647beb0 --- /dev/null +++ b/prompts/clip1_waxal_yor_yor_1205.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8731a9032a1e84a88a2494b65a576794f732cf5c8555c1b94af1a5d370ad7c9 +size 314924 diff --git a/prompts/clip2_waxal_yor_yor_1263.wav b/prompts/clip2_waxal_yor_yor_1263.wav new file mode 100644 index 0000000000000000000000000000000000000000..989d062e3b14c00c7acf9118270e56c4bfd9a925 --- /dev/null +++ b/prompts/clip2_waxal_yor_yor_1263.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0cf10737379673b123cee008f78006f4fa2fdabc779563d466f85f0772b9cec5 +size 194924 diff --git a/prompts/clip3_waxal_yor_yor_1218.wav b/prompts/clip3_waxal_yor_yor_1218.wav new file mode 100644 index 0000000000000000000000000000000000000000..696e30f2fbf39ce4340f3bf28bd4c3f6d2efb01c --- /dev/null +++ b/prompts/clip3_waxal_yor_yor_1218.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5a0e00a437713a35429fe9423c3acf4ec668038e418fc7b85391bec53cb2368a +size 262124 diff --git a/prompts/flash_voices_text.json b/prompts/flash_voices_text.json new file mode 100644 index 0000000000000000000000000000000000000000..0c83a797c7573602569cb8a1a598b774d1dd8fda --- /dev/null +++ b/prompts/flash_voices_text.json @@ -0,0 +1,3 @@ +{"anchor": "Púpọ̀ jùlọ àwọn olókìkí ní ìlú Èkó ń gbé ní erékùṣù náà.", + "waxal_ibo_5": "I nwere ike itinye mmemme ọbụla n'izu a ma gị atụla ụjọ maka oké mmiri ozuzo.", + "waxal_yor_5": "Nínú oṣù Ọpẹ́ ni ó ra mọ́tò tí wọ́n jí gbé."} diff --git a/prompts/prompts.json b/prompts/prompts.json new file mode 100644 index 0000000000000000000000000000000000000000..b950ae38449467f4a39efeffa26909d4fd298af6 --- /dev/null +++ b/prompts/prompts.json @@ -0,0 +1,602 @@ +{ + "anchor_speaker": "waxal_yor_4", + "source": "WAXAL (CC-BY-SA-4.0), anchor val clips, pause profile B (~/data3)", + "prompts": [ + { + "index": 0, + "id": "waxal_yor_yor_1258", + "wav": "prompts/clip0_waxal_yor_yor_1258.wav", + "lang": "yor", + "text": "Púpọ̀ jùlọ àwọn olókìkí ní ìlú Èkó ń gbé ní erékùṣù náà.", + "tagged": "Púpọ̀ jùlọ àwọn olókìkí ní ìlú Èkó ń gbé ní erékùṣù náà.", + "dur": 5.94, + "s3_tokens": [ + 3677, + 4137, + 4218, + 2166, + 1452, + 5830, + 2175, + 2112, + 696, + 5027, + 4834, + 2344, + 1860, + 5973, + 1939, + 1862, + 1558, + 3880, + 316, + 2621, + 2864, + 2864, + 2891, + 5051, + 3412, + 3888, + 3888, + 1458, + 1701, + 1950, + 1973, + 1946, + 3890, + 4133, + 692, + 3617, + 4346, + 3617, + 5030, + 4955, + 2768, + 4955, + 2789, + 2006, + 2903, + 3395, + 3771, + 6013, + 3853, + 5096, + 5095, + 1350, + 978, + 2298, + 5586, + 1122, + 1788, + 2598, + 295, + 566, + 51, + 3669, + 3748, + 2938, + 4751, + 4750, + 4741, + 2006, + 1349, + 1835, + 1510, + 1588, + 2180, + 5581, + 4606, + 2330, + 1127, + 573, + 660, + 1364, + 617, + 2290, + 5209, + 2077, + 2076, + 2184, + 967, + 4616, + 4607, + 4526, + 4769, + 2591, + 4526, + 4579, + 2446, + 4137, + 4137, + 4137, + 1959, + 3670, + 3671, + 3751, + 1479, + 4769, + 5021, + 4994, + 4751, + 5561, + 5561, + 5236, + 3255, + 4218, + 4218, + 4218, + 4137, + 5352, + 6081, + 5379, + 1006, + 1034, + 4679, + 2960, + 2411, + 1207, + 860, + 4760, + 233, + 1407, + 654, + 3567, + 5826, + 6310, + 5481, + 5563, + 3348, + 6556, + 2102, + 886, + 1563, + 1479, + 2300, + 4547, + 4546, + 2522, + 263, + 263, + 1225, + 1953, + 1947 + ] + }, + { + "index": 1, + "id": "waxal_yor_yor_1205", + "wav": "prompts/clip1_waxal_yor_yor_1205.wav", + "lang": "yor", + "text": "Mejìlá ní ìsọdipúpọ̀ nípasẹ̀ mẹ́rin dọ́gba mejì-mejìlá.", + "tagged": "Mejìlá ní ìsọdipúpọ̀ nípasẹ̀ mẹ́rin dọ́gba mejì-mejìlá.", + "dur": 6.56, + "s3_tokens": [ + 3677, + 1968, + 1804, + 1884, + 1883, + 2312, + 4579, + 3021, + 1041, + 5250, + 4858, + 80, + 3799, + 1532, + 731, + 6158, + 4486, + 3669, + 5938, + 3748, + 4742, + 4751, + 4751, + 4751, + 4714, + 5605, + 1951, + 2031, + 1978, + 3481, + 1268, + 3721, + 4672, + 4999, + 4752, + 586, + 2894, + 2413, + 1608, + 2347, + 1940, + 1410, + 2139, + 6558, + 5829, + 2148, + 2112, + 1410, + 5099, + 5081, + 2894, + 707, + 518, + 1308, + 1950, + 4131, + 3645, + 1707, + 1959, + 1987, + 1753, + 1484, + 5857, + 1482, + 4571, + 3637, + 4299, + 1383, + 3574, + 4214, + 3647, + 56, + 5401, + 6374, + 1035, + 32, + 2303, + 2303, + 227, + 2127, + 6502, + 1882, + 4742, + 4490, + 4579, + 4550, + 3016, + 1724, + 377, + 377, + 2456, + 2456, + 25, + 3903, + 3975, + 4137, + 4218, + 4137, + 4137, + 6324, + 6324, + 6324, + 1968, + 3913, + 1727, + 1807, + 1110, + 2666, + 5078, + 5078, + 1125, + 2157, + 1452, + 389, + 5186, + 2999, + 2513, + 2270, + 3242, + 5429, + 5105, + 5103, + 2922, + 1464, + 1950, + 4218, + 1987, + 1987, + 1987, + 1804, + 6256, + 1639, + 2573, + 4849, + 4696, + 1509, + 1779, + 4440, + 5911, + 4857, + 4777, + 323, + 323, + 2445, + 978, + 1950, + 1968, + 1996, + 1885, + 1882, + 4760, + 4696, + 3966, + 5658, + 4431, + 5506, + 78, + 5986, + 74, + 1460, + 1460, + 982, + 1701, + 1947 + ] + }, + { + "index": 2, + "id": "waxal_yor_yor_1263", + "wav": "prompts/clip2_waxal_yor_yor_1263.wav", + "lang": "yor", + "text": "Ọmọ ọdún méji ni Nóla ní oṣù kẹwàá.", + "tagged": "Ọmọ ọdún méji ni Nóla ní oṣù kẹwàá.", + "dur": 4.06, + "s3_tokens": [ + 3677, + 4218, + 2057, + 4217, + 2762, + 2127, + 4314, + 2852, + 2771, + 572, + 4835, + 963, + 1914, + 954, + 6554, + 5041, + 6502, + 4068, + 2420, + 4858, + 2265, + 1995, + 2022, + 4440, + 5262, + 5506, + 323, + 753, + 1563, + 750, + 323, + 2267, + 2591, + 2321, + 2240, + 4670, + 2455, + 4389, + 2922, + 3648, + 4137, + 1950, + 1987, + 1753, + 1481, + 1480, + 3745, + 6012, + 1073, + 4949, + 5054, + 2297, + 5905, + 5986, + 2216, + 5915, + 5186, + 5186, + 4469, + 2916, + 3645, + 1458, + 1464, + 4218, + 4137, + 1725, + 3670, + 3831, + 2941, + 5102, + 5561, + 6074, + 5234, + 2012, + 2006, + 2096, + 2912, + 5828, + 2990, + 6051, + 6054, + 1588, + 2072, + 2139, + 1734, + 2355, + 2528, + 4571, + 2384, + 881, + 644, + 1456, + 2185, + 707, + 1946, + 812, + 3242, + 5914, + 4545, + 1711, + 2031, + 6405 + ] + }, + { + "index": 3, + "id": "waxal_yor_yor_1218", + "wav": "prompts/clip3_waxal_yor_yor_1218.wav", + "lang": "yor", + "text": "Ìdajì àti ọ̀kan kẹ́fà dọ́gba méji kẹ́ta.", + "tagged": "Ìdajì àti ọ̀kan kẹ́fà dọ́gba méji kẹ́ta.", + "dur": 5.46, + "s3_tokens": [ + 3707, + 2031, + 566, + 566, + 4778, + 861, + 1770, + 68, + 3083, + 5996, + 4570, + 777, + 3939, + 4683, + 5169, + 5911, + 3239, + 1052, + 286, + 259, + 5109, + 2922, + 2922, + 3894, + 3651, + 2031, + 1976, + 3161, + 3890, + 3890, + 2219, + 1527, + 2085, + 6380, + 6131, + 5401, + 1038, + 2267, + 4697, + 4697, + 4778, + 4697, + 4778, + 5507, + 5507, + 5507, + 3050, + 3255, + 3894, + 4218, + 4137, + 1979, + 1304, + 1385, + 2897, + 601, + 4299, + 2031, + 2754, + 4869, + 5030, + 5030, + 665, + 497, + 1723, + 2112, + 762, + 2271, + 2213, + 4523, + 4853, + 6372, + 6534, + 6534, + 688, + 650, + 1055, + 1784, + 3161, + 2917, + 2916, + 1458, + 1458, + 1461, + 2031, + 1807, + 1726, + 1806, + 1806, + 873, + 2657, + 5081, + 4852, + 1941, + 1888, + 2181, + 218, + 3242, + 2999, + 2999, + 3242, + 3170, + 2926, + 3645, + 3645, + 1701, + 2031, + 1959, + 1727, + 1727, + 3994, + 6256, + 1883, + 4841, + 4848, + 2346, + 1779, + 1284, + 4509, + 5181, + 1943, + 1923, + 1707, + 168, + 4751, + 2330, + 62, + 67, + 1707, + 2058, + 54, + 2189, + 3242, + 2927, + 2925, + 1944, + 4218 + ] + } + ] +} \ No newline at end of file diff --git a/prompts/voice_waxal_ibo_5.wav b/prompts/voice_waxal_ibo_5.wav new file mode 100644 index 0000000000000000000000000000000000000000..1eb0cd9115eb0c6cc1cbb0051d84c8ec2fa4ca30 --- /dev/null +++ b/prompts/voice_waxal_ibo_5.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d537c83dbe167ccf0b43f03304e456fb05e59dfda6b4e359d941900ef629924f +size 318044 diff --git a/prompts/voice_waxal_yor_5.wav b/prompts/voice_waxal_yor_5.wav new file mode 100644 index 0000000000000000000000000000000000000000..9734e61e1c03ea5b7c1fb219cee96ee6b966cb31 --- /dev/null +++ b/prompts/voice_waxal_yor_5.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ce75b93cf5166d0dc2f53cc943330b4d304f2e060f831d38deb2e664fff49ca5 +size 182924 diff --git a/prompts/voices.json b/prompts/voices.json new file mode 100644 index 0000000000000000000000000000000000000000..e7181e77310f718025078149d36b67a4c99a2031 --- /dev/null +++ b/prompts/voices.json @@ -0,0 +1,41 @@ +{ + "note": "zero-shot voices: the clip is the flow + LM prompt; the speaker token is used when the LM saw that speaker", + "voices": { + "anchor_male": { + "wav": null, + "lang": "yor", + "spk": "waxal_yor_4", + "gender": "male", + "source": "WAXAL Media Trust TTS, Yoruba speaker 4 (prompts/clip0..3)", + "licence": "CC-BY-SA-4.0 (Media Trust TTS subset)", + "desc": "male, WAXAL Yoruba speaker 4: the anchor voice both versions were trained around (uses prompts/prompts.json clip 0)" + }, + "waxal_ibo_5_female": { + "wav": "prompts/voice_waxal_ibo_5.wav", + "tagged": "I nwere ike itinye mmemme ọbụla n'izu a ma gị atụla ụjọ maka oké mmiri ozuzo.", + "text": "I nwere ike itinye mmemme ọbụla n'izu a ma gị atụla ụjọ maka oké mmiri ozuzo.", + "lang": "ibo", + "spk": "waxal_ibo_5", + "gender": "female", + "source_clip": "waxal_ibo_test_ibo_502_c003909", + "licence": "CC-BY-SA-4.0 (Media Trust TTS subset)", + "desc": "female native Igbo speaker (WAXAL ibo speaker 5); prompt clip waxal_ibo_test_ibo_502_c003909 (6.625 s)" + }, + "waxal_yor_5_female": { + "wav": "prompts/voice_waxal_yor_5.wav", + "tagged": "Nínú oṣù Ọpẹ́ ni ó ra mọ́tò tí wọ́n jí gbé.", + "text": "Nínú oṣù Ọpẹ́ ni ó ra mọ́tò tí wọ́n jí gbé.", + "lang": "yor", + "spk": "waxal_yor_5", + "gender": "female", + "source_clip": "waxal_yor_test_yor_686", + "licence": "CC-BY-SA-4.0 (Media Trust TTS subset)", + "desc": "female native Yoruba speaker (WAXAL yor speaker 5); prompt clip waxal_yor_test_yor_686 (3.81 s)" + } + }, + "aliases": { + "anchor": "anchor_male", + "waxal_ibo_5": "waxal_ibo_5_female", + "waxal_yor_5": "waxal_yor_5_female" + } +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..7123228fa3b32b3a30d959dea5edef3128739855 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,35 @@ +# Python 3.10. Install torch/torchaudio for your CUDA first (Colab already has them), then: +# pip install -r requirements.txt +# and clone CosyVoice (see README). Versions below are the ones the samples were rendered with +# (pins loosened where Colab's preinstalled versions also work). +torch>=2.3 +torchaudio>=2.3 +transformers==4.51.3 +huggingface_hub>=0.23 +numpy==1.26.4 +soundfile>=0.12 +librosa>=0.10 +onnxruntime-gpu>=1.18 +HyperPyYAML>=1.2.2 +modelscope>=1.20 +conformer==0.3.2 +diffusers==0.29.0 +lightning>=2.2.4 +omegaconf>=2.3.0 +hydra-core==1.3.2 +inflect>=7.3 +x-transformers>=2.11 +openai-whisper>=20231117 +einops>=0.8 +# pyworld>=0.3.4 # CosyVoice training only; infer.py runs without it +pyarrow>=18 +wget==3.2 +gdown>=5.1 +rich>=13.7 +matplotlib>=3.7 +protobuf>=4.25 +pydantic>=2.7 +wetext==0.0.4 +audiotsm==0.1.2 # WSOLA time-stretch for --speed (falls back to librosa if missing) +gradio>=4.44,<6 # app.py +# optional, for --lid glotlid / the GlotLID checkbox: fasttext (or fasttext-wheel) diff --git a/samples/codeswitch_frontend/cs_frontend_texts.json b/samples/codeswitch_frontend/cs_frontend_texts.json new file mode 100644 index 0000000000000000000000000000000000000000..bbaf7b03915b4494e540edbfd2f25851c1f446ef --- /dev/null +++ b/samples/codeswitch_frontend/cs_frontend_texts.json @@ -0,0 +1,78 @@ +[ + { + "text": "Good morning everyone, welcome to the market. Prices are good today, come and buy.", + "request": "mostly English, Igbo greetings", + "voice": "anchor", + "tagged": "Ụtụtụ ọma, unu niile. Welcome to the market. Prices are good today, come and buy.", + "ok": true, + "log": [ + { + "try_": 0, + "ok": true, + "why": "ok", + "out": "Ụtụtụ ọma, unu niile. Welcome to the market. Prices are good today, come and buy.", + "roundtrip": { + "chrf": 91.8, + "back": "Good morning, everyone. Welcome to the market. Prices are good today, come and buy." + } + } + ] + }, + { + "text": "Thank you for coming. We will start the program in five minutes.", + "request": "English with a Yoruba closing", + "voice": "waxal_yor_5", + "tagged": "Thank you for coming. We will start the program in five minutes. Ẹ ṣé.", + "ok": true, + "log": [ + { + "try_": 0, + "ok": true, + "why": "ok", + "out": "Thank you for coming. We will start the program in five minutes. Ẹ ṣé.", + "roundtrip": { + "chrf": 89.5, + "back": "Thank you for coming. We will start the program in five minutes. You did." + } + } + ] + }, + { + "text": "Good evening. The rain will be heavy tomorrow, so carry an umbrella.", + "request": "Hausa greeting, the rest in English", + "voice": "anchor", + "tagged": "Barka da yamma The rain will be heavy tomorrow, so carry an umbrella.", + "ok": true, + "log": [ + { + "try_": 0, + "ok": true, + "why": "ok", + "out": "Barka da yamma The rain will be heavy tomorrow, so carry an umbrella.", + "roundtrip": { + "chrf": 74.0, + "back": "Welcome to the village The rain will be heavy tomorrow, so carry an umbrella." + } + } + ] + }, + { + "text": "My brother, the work is too much today, but we will finish it.", + "request": "Nigerian Pidgin with an Igbo greeting", + "voice": "waxal_ibo_5", + "tagged": "Ndewo My bro, di work be too much for today, but we go finish am.", + "ok": true, + "log": [ + { + "try_": 0, + "ok": true, + "why": "ok", + "out": "Ndewo My bro, di work be too much for today, but we go finish am.", + "roundtrip": { + "chrf": 85.6, + "back": "Hello My brother, the work is too much for today, but we will finish it." + } + } + ] + } +] \ No newline at end of file diff --git a/samples/codeswitch_frontend/flash_csfe1_anchor.mp3 b/samples/codeswitch_frontend/flash_csfe1_anchor.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..2c22d49bc0edd38ffdf737ecb8a890f3b00fe91b Binary files /dev/null and b/samples/codeswitch_frontend/flash_csfe1_anchor.mp3 differ diff --git a/samples/codeswitch_frontend/flash_csfe2_waxal_yor_5.mp3 b/samples/codeswitch_frontend/flash_csfe2_waxal_yor_5.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..2db5b7f7613a324bd4ceefa638d982535e54f32a Binary files /dev/null and b/samples/codeswitch_frontend/flash_csfe2_waxal_yor_5.mp3 differ diff --git a/samples/codeswitch_frontend/flash_csfe3_anchor.mp3 b/samples/codeswitch_frontend/flash_csfe3_anchor.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..a84844563083ff9a2adf01907e56690b2ab80ad5 Binary files /dev/null and b/samples/codeswitch_frontend/flash_csfe3_anchor.mp3 differ diff --git a/samples/codeswitch_frontend/flash_csfe4_waxal_ibo_5.mp3 b/samples/codeswitch_frontend/flash_csfe4_waxal_ibo_5.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..7e7f30377f0e3ed268f6ba008f4e5c4b47b6d41d Binary files /dev/null and b/samples/codeswitch_frontend/flash_csfe4_waxal_ibo_5.mp3 differ diff --git a/samples/codeswitch_frontend/pro_csfe1_anchor.mp3 b/samples/codeswitch_frontend/pro_csfe1_anchor.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..2025ac0d725a9c5cc47be1110ebbdecbd3b1ae80 Binary files /dev/null and b/samples/codeswitch_frontend/pro_csfe1_anchor.mp3 differ diff --git a/samples/codeswitch_frontend/pro_csfe2_waxal_yor_5.mp3 b/samples/codeswitch_frontend/pro_csfe2_waxal_yor_5.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..3091ad9d71e243fad000b535cb28b46cbb1ac303 Binary files /dev/null and b/samples/codeswitch_frontend/pro_csfe2_waxal_yor_5.mp3 differ diff --git a/samples/codeswitch_frontend/pro_csfe3_anchor.mp3 b/samples/codeswitch_frontend/pro_csfe3_anchor.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..0fa30c6e30720d4e834d3718c123270e1445a959 Binary files /dev/null and b/samples/codeswitch_frontend/pro_csfe3_anchor.mp3 differ diff --git a/samples/codeswitch_frontend/pro_csfe4_waxal_ibo_5.mp3 b/samples/codeswitch_frontend/pro_csfe4_waxal_ibo_5.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..66bff168b9a4d6852a8f78ca248ebadf4c67ad71 Binary files /dev/null and b/samples/codeswitch_frontend/pro_csfe4_waxal_ibo_5.mp3 differ diff --git a/samples/flash_cs_anchor_default.mp3 b/samples/flash_cs_anchor_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..7dffa6ea169d54b5462bbd0cf3851649780efb00 Binary files /dev/null and b/samples/flash_cs_anchor_default.mp3 differ diff --git a/samples/flash_cs_waxal_ibo_5_default.mp3 b/samples/flash_cs_waxal_ibo_5_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..2501e2d86918e4399de281ee33fb3f529f68f73e Binary files /dev/null and b/samples/flash_cs_waxal_ibo_5_default.mp3 differ diff --git a/samples/flash_eng_anchor_default.mp3 b/samples/flash_eng_anchor_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..6e7d308ed0d491c31dd73f795513b1d5eb3663ee Binary files /dev/null and b/samples/flash_eng_anchor_default.mp3 differ diff --git a/samples/flash_eng_waxal_ibo_5_default.mp3 b/samples/flash_eng_waxal_ibo_5_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..94d9a143205e8cd0579736f3dd5745a903f6fa58 Binary files /dev/null and b/samples/flash_eng_waxal_ibo_5_default.mp3 differ diff --git a/samples/flash_hau_anchor_default.mp3 b/samples/flash_hau_anchor_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..efc57045493eeb1af9cb3c18fe5a7fed35da94bc Binary files /dev/null and b/samples/flash_hau_anchor_default.mp3 differ diff --git a/samples/flash_hau_waxal_ibo_5_default.mp3 b/samples/flash_hau_waxal_ibo_5_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..aeae8232d57c150f057e127f3acc0b6946c59d93 Binary files /dev/null and b/samples/flash_hau_waxal_ibo_5_default.mp3 differ diff --git a/samples/flash_ibo_anchor_default.mp3 b/samples/flash_ibo_anchor_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..aafc61eedddbbc883939d932d8fe82f28afd13b9 Binary files /dev/null and b/samples/flash_ibo_anchor_default.mp3 differ diff --git a/samples/flash_ibo_waxal_ibo_5_default.mp3 b/samples/flash_ibo_waxal_ibo_5_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..ae23c3e4bc6bd0e8125ebdd506483574bf6ee873 --- /dev/null +++ b/samples/flash_ibo_waxal_ibo_5_default.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:11453c1369a60d270232ea1c51e07125e3cc83c57f77b352eb40ed4361ccda4d +size 104804 diff --git a/samples/flash_pcm_anchor_default.mp3 b/samples/flash_pcm_anchor_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..5d1de172aeb2cef7c75e3ea867a368b802470e86 Binary files /dev/null and b/samples/flash_pcm_anchor_default.mp3 differ diff --git a/samples/flash_pcm_waxal_ibo_5_default.mp3 b/samples/flash_pcm_waxal_ibo_5_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..4acda52bb20bf49f7bb8714fe62a87ef28cafb2b Binary files /dev/null and b/samples/flash_pcm_waxal_ibo_5_default.mp3 differ diff --git a/samples/flash_yor_anchor_default.mp3 b/samples/flash_yor_anchor_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..b2b3833b4d1aa8ad6b7b60bceea8ae39f714408c Binary files /dev/null and b/samples/flash_yor_anchor_default.mp3 differ diff --git a/samples/flash_yor_waxal_ibo_5_default.mp3 b/samples/flash_yor_waxal_ibo_5_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..399ae25d59af093185a7268f83b7ebfb34e4cbf6 Binary files /dev/null and b/samples/flash_yor_waxal_ibo_5_default.mp3 differ diff --git a/samples/meta.json b/samples/meta.json new file mode 100644 index 0000000000000000000000000000000000000000..269241d08ca1d9c258f09a44fd10520f64b5f64a --- /dev/null +++ b/samples/meta.json @@ -0,0 +1,866 @@ +[ + { + "file": "pro_ibo_anchor_default.mp3", + "system": "Pro v1.1", + "lang": "ibo", + "voice": "anchor_male", + "instruct": null, + "condition": "default", + "text": "Ndewo nụ, ụmụnna m. Anyị ga-ekwu maka ụzọ ahịa ọhụrụ taa, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_ibo_anchor_slow.mp3", + "system": "Pro v1.1", + "lang": "ibo", + "voice": "anchor_male", + "instruct": "Please speak slowly and take your time.", + "condition": "slow", + "text": "Ndewo nụ, ụmụnna m. Anyị ga-ekwu maka ụzọ ahịa ọhụrụ taa, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_ibo_anchor_fast.mp3", + "system": "Pro v1.1", + "lang": "ibo", + "voice": "anchor_male", + "instruct": "Speak fast, like a quick everyday conversation.", + "condition": "fast", + "text": "Ndewo nụ, ụmụnna m. Anyị ga-ekwu maka ụzọ ahịa ọhụrụ taa, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_ibo_anchor_few_pauses.mp3", + "system": "Pro v1.1", + "lang": "ibo", + "voice": "anchor_male", + "instruct": "Say it in one smooth flow with hardly any pauses.", + "condition": "few_pauses", + "text": "Ndewo nụ, ụmụnna m. Anyị ga-ekwu maka ụzọ ahịa ọhụrụ taa, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_ibo_anchor_many_pauses.mp3", + "system": "Pro v1.1", + "lang": "ibo", + "voice": "anchor_male", + "instruct": "Speak with plenty of pauses between phrases.", + "condition": "many_pauses", + "text": "Ndewo nụ, ụmụnna m. Anyị ga-ekwu maka ụzọ ahịa ọhụrụ taa, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_ibo_waxal_ibo_5_default.mp3", + "system": "Pro v1.1", + "lang": "ibo", + "voice": "waxal_ibo_5_female", + "instruct": null, + "condition": "default", + "text": "Ndewo nụ, ụmụnna m. Anyị ga-ekwu maka ụzọ ahịa ọhụrụ taa, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_ibo_waxal_ibo_5_slow.mp3", + "system": "Pro v1.1", + "lang": "ibo", + "voice": "waxal_ibo_5_female", + "instruct": "Please speak slowly and take your time.", + "condition": "slow", + "text": "Ndewo nụ, ụmụnna m. Anyị ga-ekwu maka ụzọ ahịa ọhụrụ taa, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_ibo_waxal_ibo_5_fast.mp3", + "system": "Pro v1.1", + "lang": "ibo", + "voice": "waxal_ibo_5_female", + "instruct": "Speak fast, like a quick everyday conversation.", + "condition": "fast", + "text": "Ndewo nụ, ụmụnna m. Anyị ga-ekwu maka ụzọ ahịa ọhụrụ taa, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_ibo_waxal_ibo_5_few_pauses.mp3", + "system": "Pro v1.1", + "lang": "ibo", + "voice": "waxal_ibo_5_female", + "instruct": "Say it in one smooth flow with hardly any pauses.", + "condition": "few_pauses", + "text": "Ndewo nụ, ụmụnna m. Anyị ga-ekwu maka ụzọ ahịa ọhụrụ taa, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_ibo_waxal_ibo_5_many_pauses.mp3", + "system": "Pro v1.1", + "lang": "ibo", + "voice": "waxal_ibo_5_female", + "instruct": "Speak with plenty of pauses between phrases.", + "condition": "many_pauses", + "text": "Ndewo nụ, ụmụnna m. Anyị ga-ekwu maka ụzọ ahịa ọhụrụ taa, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_yor_anchor_default.mp3", + "system": "Pro v1.1", + "lang": "yor", + "voice": "anchor_male", + "instruct": null, + "condition": "default", + "text": "Ẹ káàárọ̀ o. Òjò ńlá máa rọ̀ lọ́la, nítorí náà ẹ gbé agboòrùn dání tí ẹ bá ń jáde, kí ẹ sì tètè padà sílé.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_yor_anchor_slow.mp3", + "system": "Pro v1.1", + "lang": "yor", + "voice": "anchor_male", + "instruct": "Please speak slowly and take your time.", + "condition": "slow", + "text": "Ẹ káàárọ̀ o. Òjò ńlá máa rọ̀ lọ́la, nítorí náà ẹ gbé agboòrùn dání tí ẹ bá ń jáde, kí ẹ sì tètè padà sílé.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_yor_anchor_fast.mp3", + "system": "Pro v1.1", + "lang": "yor", + "voice": "anchor_male", + "instruct": "Speak fast, like a quick everyday conversation.", + "condition": "fast", + "text": "Ẹ káàárọ̀ o. Òjò ńlá máa rọ̀ lọ́la, nítorí náà ẹ gbé agboòrùn dání tí ẹ bá ń jáde, kí ẹ sì tètè padà sílé.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_yor_anchor_few_pauses.mp3", + "system": "Pro v1.1", + "lang": "yor", + "voice": "anchor_male", + "instruct": "Say it in one smooth flow with hardly any pauses.", + "condition": "few_pauses", + "text": "Ẹ káàárọ̀ o. Òjò ńlá máa rọ̀ lọ́la, nítorí náà ẹ gbé agboòrùn dání tí ẹ bá ń jáde, kí ẹ sì tètè padà sílé.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_yor_anchor_many_pauses.mp3", + "system": "Pro v1.1", + "lang": "yor", + "voice": "anchor_male", + "instruct": "Speak with plenty of pauses between phrases.", + "condition": "many_pauses", + "text": "Ẹ káàárọ̀ o. Òjò ńlá máa rọ̀ lọ́la, nítorí náà ẹ gbé agboòrùn dání tí ẹ bá ń jáde, kí ẹ sì tètè padà sílé.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_yor_waxal_ibo_5_default.mp3", + "system": "Pro v1.1", + "lang": "yor", + "voice": "waxal_ibo_5_female", + "instruct": null, + "condition": "default", + "text": "Ẹ káàárọ̀ o. Òjò ńlá máa rọ̀ lọ́la, nítorí náà ẹ gbé agboòrùn dání tí ẹ bá ń jáde, kí ẹ sì tètè padà sílé.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_yor_waxal_ibo_5_slow.mp3", + "system": "Pro v1.1", + "lang": "yor", + "voice": "waxal_ibo_5_female", + "instruct": "Please speak slowly and take your time.", + "condition": "slow", + "text": "Ẹ káàárọ̀ o. Òjò ńlá máa rọ̀ lọ́la, nítorí náà ẹ gbé agboòrùn dání tí ẹ bá ń jáde, kí ẹ sì tètè padà sílé.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_yor_waxal_ibo_5_fast.mp3", + "system": "Pro v1.1", + "lang": "yor", + "voice": "waxal_ibo_5_female", + "instruct": "Speak fast, like a quick everyday conversation.", + "condition": "fast", + "text": "Ẹ káàárọ̀ o. Òjò ńlá máa rọ̀ lọ́la, nítorí náà ẹ gbé agboòrùn dání tí ẹ bá ń jáde, kí ẹ sì tètè padà sílé.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_yor_waxal_ibo_5_few_pauses.mp3", + "system": "Pro v1.1", + "lang": "yor", + "voice": "waxal_ibo_5_female", + "instruct": "Say it in one smooth flow with hardly any pauses.", + "condition": "few_pauses", + "text": "Ẹ káàárọ̀ o. Òjò ńlá máa rọ̀ lọ́la, nítorí náà ẹ gbé agboòrùn dání tí ẹ bá ń jáde, kí ẹ sì tètè padà sílé.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_yor_waxal_ibo_5_many_pauses.mp3", + "system": "Pro v1.1", + "lang": "yor", + "voice": "waxal_ibo_5_female", + "instruct": "Speak with plenty of pauses between phrases.", + "condition": "many_pauses", + "text": "Ẹ káàárọ̀ o. Òjò ńlá máa rọ̀ lọ́la, nítorí náà ẹ gbé agboòrùn dání tí ẹ bá ń jáde, kí ẹ sì tètè padà sílé.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_hau_anchor_default.mp3", + "system": "Pro v1.1", + "lang": "hau", + "voice": "anchor_male", + "instruct": null, + "condition": "default", + "text": "Barka da safiya. Yau za mu tattauna game da sabuwar hanyar kasuwa, kuma muna son kowa ya halarta.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_hau_anchor_slow.mp3", + "system": "Pro v1.1", + "lang": "hau", + "voice": "anchor_male", + "instruct": "Please speak slowly and take your time.", + "condition": "slow", + "text": "Barka da safiya. Yau za mu tattauna game da sabuwar hanyar kasuwa, kuma muna son kowa ya halarta.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_hau_anchor_fast.mp3", + "system": "Pro v1.1", + "lang": "hau", + "voice": "anchor_male", + "instruct": "Speak fast, like a quick everyday conversation.", + "condition": "fast", + "text": "Barka da safiya. Yau za mu tattauna game da sabuwar hanyar kasuwa, kuma muna son kowa ya halarta.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_hau_anchor_few_pauses.mp3", + "system": "Pro v1.1", + "lang": "hau", + "voice": "anchor_male", + "instruct": "Say it in one smooth flow with hardly any pauses.", + "condition": "few_pauses", + "text": "Barka da safiya. Yau za mu tattauna game da sabuwar hanyar kasuwa, kuma muna son kowa ya halarta.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_hau_anchor_many_pauses.mp3", + "system": "Pro v1.1", + "lang": "hau", + "voice": "anchor_male", + "instruct": "Speak with plenty of pauses between phrases.", + "condition": "many_pauses", + "text": "Barka da safiya. Yau za mu tattauna game da sabuwar hanyar kasuwa, kuma muna son kowa ya halarta.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_hau_waxal_ibo_5_default.mp3", + "system": "Pro v1.1", + "lang": "hau", + "voice": "waxal_ibo_5_female", + "instruct": null, + "condition": "default", + "text": "Barka da safiya. Yau za mu tattauna game da sabuwar hanyar kasuwa, kuma muna son kowa ya halarta.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_hau_waxal_ibo_5_slow.mp3", + "system": "Pro v1.1", + "lang": "hau", + "voice": "waxal_ibo_5_female", + "instruct": "Please speak slowly and take your time.", + "condition": "slow", + "text": "Barka da safiya. Yau za mu tattauna game da sabuwar hanyar kasuwa, kuma muna son kowa ya halarta.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_hau_waxal_ibo_5_fast.mp3", + "system": "Pro v1.1", + "lang": "hau", + "voice": "waxal_ibo_5_female", + "instruct": "Speak fast, like a quick everyday conversation.", + "condition": "fast", + "text": "Barka da safiya. Yau za mu tattauna game da sabuwar hanyar kasuwa, kuma muna son kowa ya halarta.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_hau_waxal_ibo_5_few_pauses.mp3", + "system": "Pro v1.1", + "lang": "hau", + "voice": "waxal_ibo_5_female", + "instruct": "Say it in one smooth flow with hardly any pauses.", + "condition": "few_pauses", + "text": "Barka da safiya. Yau za mu tattauna game da sabuwar hanyar kasuwa, kuma muna son kowa ya halarta.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_hau_waxal_ibo_5_many_pauses.mp3", + "system": "Pro v1.1", + "lang": "hau", + "voice": "waxal_ibo_5_female", + "instruct": "Speak with plenty of pauses between phrases.", + "condition": "many_pauses", + "text": "Barka da safiya. Yau za mu tattauna game da sabuwar hanyar kasuwa, kuma muna son kowa ya halarta.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_pcm_anchor_default.mp3", + "system": "Pro v1.1", + "lang": "pcm", + "voice": "anchor_male", + "instruct": null, + "condition": "default", + "text": "Abeg make una hear me well well. Di market road go open next week, so make everybody prepare.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_pcm_anchor_slow.mp3", + "system": "Pro v1.1", + "lang": "pcm", + "voice": "anchor_male", + "instruct": "Please speak slowly and take your time.", + "condition": "slow", + "text": "Abeg make una hear me well well. Di market road go open next week, so make everybody prepare.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_pcm_anchor_fast.mp3", + "system": "Pro v1.1", + "lang": "pcm", + "voice": "anchor_male", + "instruct": "Speak fast, like a quick everyday conversation.", + "condition": "fast", + "text": "Abeg make una hear me well well. Di market road go open next week, so make everybody prepare.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_pcm_anchor_few_pauses.mp3", + "system": "Pro v1.1", + "lang": "pcm", + "voice": "anchor_male", + "instruct": "Say it in one smooth flow with hardly any pauses.", + "condition": "few_pauses", + "text": "Abeg make una hear me well well. Di market road go open next week, so make everybody prepare.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_pcm_anchor_many_pauses.mp3", + "system": "Pro v1.1", + "lang": "pcm", + "voice": "anchor_male", + "instruct": "Speak with plenty of pauses between phrases.", + "condition": "many_pauses", + "text": "Abeg make una hear me well well. Di market road go open next week, so make everybody prepare.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_pcm_waxal_ibo_5_default.mp3", + "system": "Pro v1.1", + "lang": "pcm", + "voice": "waxal_ibo_5_female", + "instruct": null, + "condition": "default", + "text": "Abeg make una hear me well well. Di market road go open next week, so make everybody prepare.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_pcm_waxal_ibo_5_slow.mp3", + "system": "Pro v1.1", + "lang": "pcm", + "voice": "waxal_ibo_5_female", + "instruct": "Please speak slowly and take your time.", + "condition": "slow", + "text": "Abeg make una hear me well well. Di market road go open next week, so make everybody prepare.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_pcm_waxal_ibo_5_fast.mp3", + "system": "Pro v1.1", + "lang": "pcm", + "voice": "waxal_ibo_5_female", + "instruct": "Speak fast, like a quick everyday conversation.", + "condition": "fast", + "text": "Abeg make una hear me well well. Di market road go open next week, so make everybody prepare.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_pcm_waxal_ibo_5_few_pauses.mp3", + "system": "Pro v1.1", + "lang": "pcm", + "voice": "waxal_ibo_5_female", + "instruct": "Say it in one smooth flow with hardly any pauses.", + "condition": "few_pauses", + "text": "Abeg make una hear me well well. Di market road go open next week, so make everybody prepare.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_pcm_waxal_ibo_5_many_pauses.mp3", + "system": "Pro v1.1", + "lang": "pcm", + "voice": "waxal_ibo_5_female", + "instruct": "Speak with plenty of pauses between phrases.", + "condition": "many_pauses", + "text": "Abeg make una hear me well well. Di market road go open next week, so make everybody prepare.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_eng_anchor_default.mp3", + "system": "Pro v1.1", + "lang": "eng", + "voice": "anchor_male", + "instruct": null, + "condition": "default", + "text": "Good evening, everyone. Tonight we will talk about the new market road, the school fees, and the plan for next month.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_eng_anchor_slow.mp3", + "system": "Pro v1.1", + "lang": "eng", + "voice": "anchor_male", + "instruct": "Please speak slowly and take your time.", + "condition": "slow", + "text": "Good evening, everyone. Tonight we will talk about the new market road, the school fees, and the plan for next month.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_eng_anchor_fast.mp3", + "system": "Pro v1.1", + "lang": "eng", + "voice": "anchor_male", + "instruct": "Speak fast, like a quick everyday conversation.", + "condition": "fast", + "text": "Good evening, everyone. Tonight we will talk about the new market road, the school fees, and the plan for next month.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_eng_anchor_few_pauses.mp3", + "system": "Pro v1.1", + "lang": "eng", + "voice": "anchor_male", + "instruct": "Say it in one smooth flow with hardly any pauses.", + "condition": "few_pauses", + "text": "Good evening, everyone. Tonight we will talk about the new market road, the school fees, and the plan for next month.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_eng_anchor_many_pauses.mp3", + "system": "Pro v1.1", + "lang": "eng", + "voice": "anchor_male", + "instruct": "Speak with plenty of pauses between phrases.", + "condition": "many_pauses", + "text": "Good evening, everyone. Tonight we will talk about the new market road, the school fees, and the plan for next month.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_eng_waxal_ibo_5_default.mp3", + "system": "Pro v1.1", + "lang": "eng", + "voice": "waxal_ibo_5_female", + "instruct": null, + "condition": "default", + "text": "Good evening, everyone. Tonight we will talk about the new market road, the school fees, and the plan for next month.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_eng_waxal_ibo_5_slow.mp3", + "system": "Pro v1.1", + "lang": "eng", + "voice": "waxal_ibo_5_female", + "instruct": "Please speak slowly and take your time.", + "condition": "slow", + "text": "Good evening, everyone. Tonight we will talk about the new market road, the school fees, and the plan for next month.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_eng_waxal_ibo_5_fast.mp3", + "system": "Pro v1.1", + "lang": "eng", + "voice": "waxal_ibo_5_female", + "instruct": "Speak fast, like a quick everyday conversation.", + "condition": "fast", + "text": "Good evening, everyone. Tonight we will talk about the new market road, the school fees, and the plan for next month.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_eng_waxal_ibo_5_few_pauses.mp3", + "system": "Pro v1.1", + "lang": "eng", + "voice": "waxal_ibo_5_female", + "instruct": "Say it in one smooth flow with hardly any pauses.", + "condition": "few_pauses", + "text": "Good evening, everyone. Tonight we will talk about the new market road, the school fees, and the plan for next month.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_eng_waxal_ibo_5_many_pauses.mp3", + "system": "Pro v1.1", + "lang": "eng", + "voice": "waxal_ibo_5_female", + "instruct": "Speak with plenty of pauses between phrases.", + "condition": "many_pauses", + "text": "Good evening, everyone. Tonight we will talk about the new market road, the school fees, and the plan for next month.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_cs_anchor_default.mp3", + "system": "Pro v1.1", + "lang": "cs", + "voice": "anchor_male", + "instruct": null, + "condition": "default", + "text": "Ndewo nụ, ụmụnna m. Tonight we will talk about the new market road, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_cs_anchor_slow.mp3", + "system": "Pro v1.1", + "lang": "cs", + "voice": "anchor_male", + "instruct": "Please speak slowly and take your time.", + "condition": "slow", + "text": "Ndewo nụ, ụmụnna m. Tonight we will talk about the new market road, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_cs_anchor_fast.mp3", + "system": "Pro v1.1", + "lang": "cs", + "voice": "anchor_male", + "instruct": "Speak fast, like a quick everyday conversation.", + "condition": "fast", + "text": "Ndewo nụ, ụmụnna m. Tonight we will talk about the new market road, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_cs_anchor_few_pauses.mp3", + "system": "Pro v1.1", + "lang": "cs", + "voice": "anchor_male", + "instruct": "Say it in one smooth flow with hardly any pauses.", + "condition": "few_pauses", + "text": "Ndewo nụ, ụmụnna m. Tonight we will talk about the new market road, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_cs_anchor_many_pauses.mp3", + "system": "Pro v1.1", + "lang": "cs", + "voice": "anchor_male", + "instruct": "Speak with plenty of pauses between phrases.", + "condition": "many_pauses", + "text": "Ndewo nụ, ụmụnna m. Tonight we will talk about the new market road, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_cs_waxal_ibo_5_default.mp3", + "system": "Pro v1.1", + "lang": "cs", + "voice": "waxal_ibo_5_female", + "instruct": null, + "condition": "default", + "text": "Ndewo nụ, ụmụnna m. Tonight we will talk about the new market road, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_cs_waxal_ibo_5_slow.mp3", + "system": "Pro v1.1", + "lang": "cs", + "voice": "waxal_ibo_5_female", + "instruct": "Please speak slowly and take your time.", + "condition": "slow", + "text": "Ndewo nụ, ụmụnna m. Tonight we will talk about the new market road, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_cs_waxal_ibo_5_fast.mp3", + "system": "Pro v1.1", + "lang": "cs", + "voice": "waxal_ibo_5_female", + "instruct": "Speak fast, like a quick everyday conversation.", + "condition": "fast", + "text": "Ndewo nụ, ụmụnna m. Tonight we will talk about the new market road, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "pro_cs_waxal_ibo_5_few_pauses.mp3", + "system": "Pro v1.1", + "lang": "cs", + "voice": "waxal_ibo_5_female", + "instruct": "Say it in one smooth flow with hardly any pauses.", + "condition": "few_pauses", + "text": "Ndewo nụ, ụmụnna m. Tonight we will talk about the new market road, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "pro_cs_waxal_ibo_5_many_pauses.mp3", + "system": "Pro v1.1", + "lang": "cs", + "voice": "waxal_ibo_5_female", + "instruct": "Speak with plenty of pauses between phrases.", + "condition": "many_pauses", + "text": "Ndewo nụ, ụmụnna m. Tonight we will talk about the new market road, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": 2.0, + "text_pause_step": true, + "seed": 0 + }, + { + "file": "flash_ibo_anchor_default.mp3", + "system": "Flash-DPO", + "lang": "ibo", + "voice": "anchor_male", + "instruct": null, + "condition": "default", + "text": "Ndewo nụ, ụmụnna m. Anyị ga-ekwu maka ụzọ ahịa ọhụrụ taa, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": null, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "flash_ibo_waxal_ibo_5_default.mp3", + "system": "Flash-DPO", + "lang": "ibo", + "voice": "waxal_ibo_5_female", + "instruct": null, + "condition": "default", + "text": "Ndewo nụ, ụmụnna m. Anyị ga-ekwu maka ụzọ ahịa ọhụrụ taa, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": null, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "flash_yor_anchor_default.mp3", + "system": "Flash-DPO", + "lang": "yor", + "voice": "anchor_male", + "instruct": null, + "condition": "default", + "text": "Ẹ káàárọ̀ o. Òjò ńlá máa rọ̀ lọ́la, nítorí náà ẹ gbé agboòrùn dání tí ẹ bá ń jáde, kí ẹ sì tètè padà sílé.", + "guidance": null, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "flash_yor_waxal_ibo_5_default.mp3", + "system": "Flash-DPO", + "lang": "yor", + "voice": "waxal_ibo_5_female", + "instruct": null, + "condition": "default", + "text": "Ẹ káàárọ̀ o. Òjò ńlá máa rọ̀ lọ́la, nítorí náà ẹ gbé agboòrùn dání tí ẹ bá ń jáde, kí ẹ sì tètè padà sílé.", + "guidance": null, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "flash_hau_anchor_default.mp3", + "system": "Flash-DPO", + "lang": "hau", + "voice": "anchor_male", + "instruct": null, + "condition": "default", + "text": "Barka da safiya. Yau za mu tattauna game da sabuwar hanyar kasuwa, kuma muna son kowa ya halarta.", + "guidance": null, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "flash_hau_waxal_ibo_5_default.mp3", + "system": "Flash-DPO", + "lang": "hau", + "voice": "waxal_ibo_5_female", + "instruct": null, + "condition": "default", + "text": "Barka da safiya. Yau za mu tattauna game da sabuwar hanyar kasuwa, kuma muna son kowa ya halarta.", + "guidance": null, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "flash_pcm_anchor_default.mp3", + "system": "Flash-DPO", + "lang": "pcm", + "voice": "anchor_male", + "instruct": null, + "condition": "default", + "text": "Abeg make una hear me well well. Di market road go open next week, so make everybody prepare.", + "guidance": null, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "flash_pcm_waxal_ibo_5_default.mp3", + "system": "Flash-DPO", + "lang": "pcm", + "voice": "waxal_ibo_5_female", + "instruct": null, + "condition": "default", + "text": "Abeg make una hear me well well. Di market road go open next week, so make everybody prepare.", + "guidance": null, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "flash_eng_anchor_default.mp3", + "system": "Flash-DPO", + "lang": "eng", + "voice": "anchor_male", + "instruct": null, + "condition": "default", + "text": "Good evening, everyone. Tonight we will talk about the new market road, the school fees, and the plan for next month.", + "guidance": null, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "flash_eng_waxal_ibo_5_default.mp3", + "system": "Flash-DPO", + "lang": "eng", + "voice": "waxal_ibo_5_female", + "instruct": null, + "condition": "default", + "text": "Good evening, everyone. Tonight we will talk about the new market road, the school fees, and the plan for next month.", + "guidance": null, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "flash_cs_anchor_default.mp3", + "system": "Flash-DPO", + "lang": "cs", + "voice": "anchor_male", + "instruct": null, + "condition": "default", + "text": "Ndewo nụ, ụmụnna m. Tonight we will talk about the new market road, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": null, + "text_pause_step": false, + "seed": 0 + }, + { + "file": "flash_cs_waxal_ibo_5_default.mp3", + "system": "Flash-DPO", + "lang": "cs", + "voice": "waxal_ibo_5_female", + "instruct": null, + "condition": "default", + "text": "Ndewo nụ, ụmụnna m. Tonight we will talk about the new market road, ma ọ bụrụ na ego ezughị, anyị ga-akwụsị ọrụ ahụ ruo ọnwa ọzọ.", + "guidance": null, + "text_pause_step": false, + "seed": 0 + } +] \ No newline at end of file diff --git a/samples/pro_cs_anchor_default.mp3 b/samples/pro_cs_anchor_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..c6ee71c1abd123e7c078767f6a169e1dbe2f5b8a --- /dev/null +++ b/samples/pro_cs_anchor_default.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7eb418d02d7d816aa926016a9bdc2433a3a7023fbcf0cc1a2caf3fa68c2ad967 +size 102980 diff --git a/samples/pro_cs_anchor_fast.mp3 b/samples/pro_cs_anchor_fast.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..1d51624d8c8bd7cba2c3c3ec9277fd414da21fe3 Binary files /dev/null and b/samples/pro_cs_anchor_fast.mp3 differ diff --git a/samples/pro_cs_anchor_few_pauses.mp3 b/samples/pro_cs_anchor_few_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..58ff7ac07a36c0364774542cc05c74658324656c Binary files /dev/null and b/samples/pro_cs_anchor_few_pauses.mp3 differ diff --git a/samples/pro_cs_anchor_many_pauses.mp3 b/samples/pro_cs_anchor_many_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..a9436b988058feb3e19e1e900bcddc17200e322a --- /dev/null +++ b/samples/pro_cs_anchor_many_pauses.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f418ed7bc674a348155e4837e06e41b30ca8cc7d1d13270b053a3de3af801ceb +size 109340 diff --git a/samples/pro_cs_anchor_slow.mp3 b/samples/pro_cs_anchor_slow.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..59e5c8a2e6e6bfe44b435b6749a981f08c035e54 --- /dev/null +++ b/samples/pro_cs_anchor_slow.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:831622ce832b882d52b5a455d706f1ffa834f7e7ce19535d12f36b9dfae9ca5c +size 103868 diff --git a/samples/pro_cs_waxal_ibo_5_default.mp3 b/samples/pro_cs_waxal_ibo_5_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..32d8d01e38a433b187c4ca9c3f8312df23f4e141 Binary files /dev/null and b/samples/pro_cs_waxal_ibo_5_default.mp3 differ diff --git a/samples/pro_cs_waxal_ibo_5_fast.mp3 b/samples/pro_cs_waxal_ibo_5_fast.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..3ca27a9211fe3b67407bfd36442b1ee716960ce3 Binary files /dev/null and b/samples/pro_cs_waxal_ibo_5_fast.mp3 differ diff --git a/samples/pro_cs_waxal_ibo_5_few_pauses.mp3 b/samples/pro_cs_waxal_ibo_5_few_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..ba4ffbaae544cf3b269a43d84c43979e331a68d6 Binary files /dev/null and b/samples/pro_cs_waxal_ibo_5_few_pauses.mp3 differ diff --git a/samples/pro_cs_waxal_ibo_5_many_pauses.mp3 b/samples/pro_cs_waxal_ibo_5_many_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..184a245a0a238a06de92b82d8002b984dff45a16 --- /dev/null +++ b/samples/pro_cs_waxal_ibo_5_many_pauses.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f65943c0e78b67c0dc1eec8019e116b5a4e3099e2bb4d869eb71dfc46b6b817b +size 115652 diff --git a/samples/pro_cs_waxal_ibo_5_slow.mp3 b/samples/pro_cs_waxal_ibo_5_slow.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..a338e10e8d3affe92c99c9eeff7b861c4b5690ca Binary files /dev/null and b/samples/pro_cs_waxal_ibo_5_slow.mp3 differ diff --git a/samples/pro_eng_anchor_default.mp3 b/samples/pro_eng_anchor_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..78c27b5a91260b8e927ffbab14316f7ec0f6d06b Binary files /dev/null and b/samples/pro_eng_anchor_default.mp3 differ diff --git a/samples/pro_eng_anchor_fast.mp3 b/samples/pro_eng_anchor_fast.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..51d7ef602a47de45614952d86cac54b430abea72 Binary files /dev/null and b/samples/pro_eng_anchor_fast.mp3 differ diff --git a/samples/pro_eng_anchor_few_pauses.mp3 b/samples/pro_eng_anchor_few_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..2006aede9c682e32715e17d1ff08e9aea9e18f99 Binary files /dev/null and b/samples/pro_eng_anchor_few_pauses.mp3 differ diff --git a/samples/pro_eng_anchor_many_pauses.mp3 b/samples/pro_eng_anchor_many_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..97e856ef21e2a91e768187c6e30aee72a4886d21 Binary files /dev/null and b/samples/pro_eng_anchor_many_pauses.mp3 differ diff --git a/samples/pro_eng_anchor_slow.mp3 b/samples/pro_eng_anchor_slow.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..eef84177de629a766f722c4bf7d695cd5994665d Binary files /dev/null and b/samples/pro_eng_anchor_slow.mp3 differ diff --git a/samples/pro_eng_waxal_ibo_5_default.mp3 b/samples/pro_eng_waxal_ibo_5_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..fbcb2a15309f212438d827243aaef164a38818dd Binary files /dev/null and b/samples/pro_eng_waxal_ibo_5_default.mp3 differ diff --git a/samples/pro_eng_waxal_ibo_5_fast.mp3 b/samples/pro_eng_waxal_ibo_5_fast.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..42f8374af3dce35171b96f67241153223dc3ce6f Binary files /dev/null and b/samples/pro_eng_waxal_ibo_5_fast.mp3 differ diff --git a/samples/pro_eng_waxal_ibo_5_few_pauses.mp3 b/samples/pro_eng_waxal_ibo_5_few_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f42854f18766c33e9e89410b8aa3fd9209cbbf44 Binary files /dev/null and b/samples/pro_eng_waxal_ibo_5_few_pauses.mp3 differ diff --git a/samples/pro_eng_waxal_ibo_5_many_pauses.mp3 b/samples/pro_eng_waxal_ibo_5_many_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..ee7c6f34ed28bc358ced2185da589ddd5ac8dfa8 Binary files /dev/null and b/samples/pro_eng_waxal_ibo_5_many_pauses.mp3 differ diff --git a/samples/pro_eng_waxal_ibo_5_slow.mp3 b/samples/pro_eng_waxal_ibo_5_slow.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..b9ae92abb8f7ed2f84f9962e5c65ee4e465b8746 Binary files /dev/null and b/samples/pro_eng_waxal_ibo_5_slow.mp3 differ diff --git a/samples/pro_hau_anchor_default.mp3 b/samples/pro_hau_anchor_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..11d874c7a822ef5b8a85d1d76b9a9e080f5576bb Binary files /dev/null and b/samples/pro_hau_anchor_default.mp3 differ diff --git a/samples/pro_hau_anchor_fast.mp3 b/samples/pro_hau_anchor_fast.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..e6aaf6e49e10f01fc265600e79fbae1aeb829227 Binary files /dev/null and b/samples/pro_hau_anchor_fast.mp3 differ diff --git a/samples/pro_hau_anchor_few_pauses.mp3 b/samples/pro_hau_anchor_few_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..88ddf4267e2b5fab3034d5863c1fbcd6c4d10d6a Binary files /dev/null and b/samples/pro_hau_anchor_few_pauses.mp3 differ diff --git a/samples/pro_hau_anchor_many_pauses.mp3 b/samples/pro_hau_anchor_many_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..02eed060fa54cfaba68604f4fdf5435c11f056b7 Binary files /dev/null and b/samples/pro_hau_anchor_many_pauses.mp3 differ diff --git a/samples/pro_hau_anchor_slow.mp3 b/samples/pro_hau_anchor_slow.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..208a353a8333931c74fd053d6667f73c8079c3fa Binary files /dev/null and b/samples/pro_hau_anchor_slow.mp3 differ diff --git a/samples/pro_hau_waxal_ibo_5_default.mp3 b/samples/pro_hau_waxal_ibo_5_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..67727de043f01b799fd9d7f20251cac48c134462 Binary files /dev/null and b/samples/pro_hau_waxal_ibo_5_default.mp3 differ diff --git a/samples/pro_hau_waxal_ibo_5_fast.mp3 b/samples/pro_hau_waxal_ibo_5_fast.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..491708c0ac8a8c83e6ffd9e23e79238a00c4eedd Binary files /dev/null and b/samples/pro_hau_waxal_ibo_5_fast.mp3 differ diff --git a/samples/pro_hau_waxal_ibo_5_few_pauses.mp3 b/samples/pro_hau_waxal_ibo_5_few_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..e79127598d99364337cd23af6d97d9a5d6bc7529 Binary files /dev/null and b/samples/pro_hau_waxal_ibo_5_few_pauses.mp3 differ diff --git a/samples/pro_hau_waxal_ibo_5_many_pauses.mp3 b/samples/pro_hau_waxal_ibo_5_many_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..3392cc58a4144c9a9db374ac38eb85d7076aeaad Binary files /dev/null and b/samples/pro_hau_waxal_ibo_5_many_pauses.mp3 differ diff --git a/samples/pro_hau_waxal_ibo_5_slow.mp3 b/samples/pro_hau_waxal_ibo_5_slow.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..40e295622e92304994ab38c3839bffea8b88f8b4 Binary files /dev/null and b/samples/pro_hau_waxal_ibo_5_slow.mp3 differ diff --git a/samples/pro_ibo_anchor_default.mp3 b/samples/pro_ibo_anchor_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..802e48fd616dd82bf655f293878c0790ba3b8694 Binary files /dev/null and b/samples/pro_ibo_anchor_default.mp3 differ diff --git a/samples/pro_ibo_anchor_fast.mp3 b/samples/pro_ibo_anchor_fast.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..5a64cde7dc815cb51b545ca6a76b33acfcdeb50d Binary files /dev/null and b/samples/pro_ibo_anchor_fast.mp3 differ diff --git a/samples/pro_ibo_anchor_few_pauses.mp3 b/samples/pro_ibo_anchor_few_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..18d4bb8c6701adf93f2608a8c394a6369c613a50 Binary files /dev/null and b/samples/pro_ibo_anchor_few_pauses.mp3 differ diff --git a/samples/pro_ibo_anchor_many_pauses.mp3 b/samples/pro_ibo_anchor_many_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..4d91da83131eb5cdb97ee4832de783b6023d80a8 --- /dev/null +++ b/samples/pro_ibo_anchor_many_pauses.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c91bf51c1887fbcff16fadd74c27cca92fb789af22f8c466473fcfbd803dca9d +size 109604 diff --git a/samples/pro_ibo_anchor_slow.mp3 b/samples/pro_ibo_anchor_slow.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..0cf87917b86e23c4381de27dc651a8832eef3d6e --- /dev/null +++ b/samples/pro_ibo_anchor_slow.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:997f23312a25a4383653ec73d0a6b554fe23c7bc3031cee97531dcb6370527cd +size 115748 diff --git a/samples/pro_ibo_waxal_ibo_5_default.mp3 b/samples/pro_ibo_waxal_ibo_5_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f41d3ac9a00dd1a056d174e9f790eeca31fa95c6 --- /dev/null +++ b/samples/pro_ibo_waxal_ibo_5_default.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:010fdcc503bd5dfb41c4158042dccbbe7fa7c390dd942bef412aabfa4d3be699 +size 104084 diff --git a/samples/pro_ibo_waxal_ibo_5_fast.mp3 b/samples/pro_ibo_waxal_ibo_5_fast.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..87c48af4ef2436ae981eec169629296fbf5cae10 Binary files /dev/null and b/samples/pro_ibo_waxal_ibo_5_fast.mp3 differ diff --git a/samples/pro_ibo_waxal_ibo_5_few_pauses.mp3 b/samples/pro_ibo_waxal_ibo_5_few_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..83edd3cc403cfbf94d7c5a5285d3cb42457b5bee Binary files /dev/null and b/samples/pro_ibo_waxal_ibo_5_few_pauses.mp3 differ diff --git a/samples/pro_ibo_waxal_ibo_5_many_pauses.mp3 b/samples/pro_ibo_waxal_ibo_5_many_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..7ace2d5c9180803fdad0692eb8c6ee35056de3e5 --- /dev/null +++ b/samples/pro_ibo_waxal_ibo_5_many_pauses.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:33e4556bf4c7f2d6304cdca635ac2640d204fb2bd7a41f978bc8a21be1982d20 +size 101828 diff --git a/samples/pro_ibo_waxal_ibo_5_slow.mp3 b/samples/pro_ibo_waxal_ibo_5_slow.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..e82b80eedd954cc65f0ea5d07deca64898f5ed52 Binary files /dev/null and b/samples/pro_ibo_waxal_ibo_5_slow.mp3 differ diff --git a/samples/pro_pcm_anchor_default.mp3 b/samples/pro_pcm_anchor_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..3baa7b65edbfaca949eacc4d4e9f25eb03bb32e4 Binary files /dev/null and b/samples/pro_pcm_anchor_default.mp3 differ diff --git a/samples/pro_pcm_anchor_fast.mp3 b/samples/pro_pcm_anchor_fast.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..19fea20e170e0bb994f5173f9daf47ed6f04cfa9 Binary files /dev/null and b/samples/pro_pcm_anchor_fast.mp3 differ diff --git a/samples/pro_pcm_anchor_few_pauses.mp3 b/samples/pro_pcm_anchor_few_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..9cacfc2151b4cfe4ee4bf40d315732a1ded6b67f Binary files /dev/null and b/samples/pro_pcm_anchor_few_pauses.mp3 differ diff --git a/samples/pro_pcm_anchor_many_pauses.mp3 b/samples/pro_pcm_anchor_many_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..646f2302b2b5540cb6ac02887167772bc54878e6 Binary files /dev/null and b/samples/pro_pcm_anchor_many_pauses.mp3 differ diff --git a/samples/pro_pcm_anchor_slow.mp3 b/samples/pro_pcm_anchor_slow.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..ebd114539c8518f987b96190d618fb805d33c129 Binary files /dev/null and b/samples/pro_pcm_anchor_slow.mp3 differ diff --git a/samples/pro_pcm_waxal_ibo_5_default.mp3 b/samples/pro_pcm_waxal_ibo_5_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..806d563f9dac50782a2d15633a77e91542e3dbce Binary files /dev/null and b/samples/pro_pcm_waxal_ibo_5_default.mp3 differ diff --git a/samples/pro_pcm_waxal_ibo_5_fast.mp3 b/samples/pro_pcm_waxal_ibo_5_fast.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..abff74b7a6bed19d84800615dae1b75f725b4777 Binary files /dev/null and b/samples/pro_pcm_waxal_ibo_5_fast.mp3 differ diff --git a/samples/pro_pcm_waxal_ibo_5_few_pauses.mp3 b/samples/pro_pcm_waxal_ibo_5_few_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..b80453e7725cc5f07991652ef67aaffdf0f7b02b Binary files /dev/null and b/samples/pro_pcm_waxal_ibo_5_few_pauses.mp3 differ diff --git a/samples/pro_pcm_waxal_ibo_5_many_pauses.mp3 b/samples/pro_pcm_waxal_ibo_5_many_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..4670d10bf427e4ba9fad4056223941dad7d2edd7 Binary files /dev/null and b/samples/pro_pcm_waxal_ibo_5_many_pauses.mp3 differ diff --git a/samples/pro_pcm_waxal_ibo_5_slow.mp3 b/samples/pro_pcm_waxal_ibo_5_slow.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..2543b7edd62bfa5087c6e88ece154e64f5ce801d Binary files /dev/null and b/samples/pro_pcm_waxal_ibo_5_slow.mp3 differ diff --git a/samples/pro_yor_anchor_default.mp3 b/samples/pro_yor_anchor_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..1f77fd7fa5d89af28c8ca396dd524fb3dc5511aa Binary files /dev/null and b/samples/pro_yor_anchor_default.mp3 differ diff --git a/samples/pro_yor_anchor_fast.mp3 b/samples/pro_yor_anchor_fast.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..dbf535133862504f506026901d607a9a5115c16d Binary files /dev/null and b/samples/pro_yor_anchor_fast.mp3 differ diff --git a/samples/pro_yor_anchor_few_pauses.mp3 b/samples/pro_yor_anchor_few_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..a48ca55b025f7f0b42cb9c83d48202013fa5398c Binary files /dev/null and b/samples/pro_yor_anchor_few_pauses.mp3 differ diff --git a/samples/pro_yor_anchor_many_pauses.mp3 b/samples/pro_yor_anchor_many_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..d1b46f4461e97ed43cc6c2273973d7161ea391de Binary files /dev/null and b/samples/pro_yor_anchor_many_pauses.mp3 differ diff --git a/samples/pro_yor_anchor_slow.mp3 b/samples/pro_yor_anchor_slow.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..4c6ae1b55cb940cdc012e26075fa2c70867eeaec Binary files /dev/null and b/samples/pro_yor_anchor_slow.mp3 differ diff --git a/samples/pro_yor_waxal_ibo_5_default.mp3 b/samples/pro_yor_waxal_ibo_5_default.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..5409474f1c606fcbc8b7e8f274497fdd64e1148c Binary files /dev/null and b/samples/pro_yor_waxal_ibo_5_default.mp3 differ diff --git a/samples/pro_yor_waxal_ibo_5_fast.mp3 b/samples/pro_yor_waxal_ibo_5_fast.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..5423dd15d2d39e438f9e51ce7628f084c1e69699 Binary files /dev/null and b/samples/pro_yor_waxal_ibo_5_fast.mp3 differ diff --git a/samples/pro_yor_waxal_ibo_5_few_pauses.mp3 b/samples/pro_yor_waxal_ibo_5_few_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..e065db08ad96a56fe9b911431c30e07244d27d5f Binary files /dev/null and b/samples/pro_yor_waxal_ibo_5_few_pauses.mp3 differ diff --git a/samples/pro_yor_waxal_ibo_5_many_pauses.mp3 b/samples/pro_yor_waxal_ibo_5_many_pauses.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..43ac91cd3b2c2d3a0da0ac17fa6f5d69aaa9eeec Binary files /dev/null and b/samples/pro_yor_waxal_ibo_5_many_pauses.mp3 differ diff --git a/samples/pro_yor_waxal_ibo_5_slow.mp3 b/samples/pro_yor_waxal_ibo_5_slow.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..1e52aa0487b8a0f9f7c423eaa8c5bf21008cddd9 Binary files /dev/null and b/samples/pro_yor_waxal_ibo_5_slow.mp3 differ diff --git a/settings.json b/settings.json new file mode 100644 index 0000000000000000000000000000000000000000..8cb44d8687fbf4fbfbe0ba4a0e2210803603bb84 --- /dev/null +++ b/settings.json @@ -0,0 +1,62 @@ +{ + "default_version": "flash", + "default_voice": "anchor_male", + "versions": { + "pro": { + "engine": "cosyvoice3", + "base": "FunAudioLLM/Fun-CosyVoice3-0.5B-2512", + "lm": "checkpoints/pro/flash_instruct/best.pt", + "diacritics": "checkpoints/flash/diacritics_byt5", + "sampler": "silexcap", + "one_pass": true, + "postprocess": false, + "rate": "normal", + "frontend": "codeswitch", + "translate": "experimental, off by default (--frontend translate --experimental_translate)", + "desc": "Pro v1.1 (experimental; used automatically when --instruct is given): Flash-Instruct LM = Flash-DPO + instruction SFT (natural-language instruction in the CosyVoice3 system-prompt slot, audio-derived pace/pause labels, default prompt trained on native-like clips) + DPO polish (CER, pace/pause match to native, clipped-ending penalty); classifier-free guidance 2.0 on the instruction; few/many-pause instructions also edit the text ([pause] tags). Pace control works; pauses work through the text step; calm/lively and emotion are not supported. Licensed data only.", + "instruct": true, + "cfg": 2.0, + "text_pauses": true, + "tail_pad": 0 + }, + "flash": { + "engine": "cosyvoice3", + "base": "FunAudioLLM/Fun-CosyVoice3-0.5B-2512", + "lm": "checkpoints/flash/flash_dpo/best.pt", + "diacritics": "checkpoints/flash/diacritics_byt5", + "sampler": "silexcap", + "one_pass": true, + "postprocess": false, + "rate": "normal", + "frontend": "codeswitch", + "translate": "experimental, off by default (--frontend translate --experimental_translate)", + "desc": "Flash (default without --instruct): Flash-DPO LM on Fun-CosyVoice3-0.5B-2512 (SFT on licensed data + DPO with a naturalness/CER/pace/pause reward), CV3 flow+vocoder zero-shot from the voice clip; text front-end restores Yoruba/Igbo diacritics; one pass per paragraph, model pauses, no post-processing" + } + }, + "voices": [ + "anchor_male", + "waxal_ibo_5_female", + "waxal_yor_5_female" + ], + "recommended": { + "pro": [ + "anchor_male", + "waxal_ibo_5_female", + "waxal_yor_5_female" + ], + "flash": [ + "anchor_male", + "waxal_ibo_5_female", + "waxal_yor_5_female" + ], + "note": "Pro v1.1 is experimental. Pace instructions (slow/fast) work; pause instructions work through a text step; calm/lively has no measurable effect and is not advertised. --tail_pad 6 removes clipped endings (opt-in). Compare with --compare (flash vs pro) before use." + }, + "rates": [ + "none", + "very slow", + "slow", + "normal", + "fast", + "very fast" + ] +} \ No newline at end of file diff --git a/tagger.py b/tagger.py new file mode 100644 index 0000000000000000000000000000000000000000..e6789fa6c45868e1941c87a8ab6202a8334d5edd --- /dev/null +++ b/tagger.py @@ -0,0 +1,348 @@ +#!/usr/bin/env python +"""Dynamic Accent auto-tagger: word/span-level language ID for untagged mixed Nigerian text. + +Labels: eng (English), pcm (Nigerian Pidgin), ibo (Igbo), yor (Yoruba), hau (Hausa). +Output is the tagged text infer.py expects, e.g. + "omo, I'm very excited" -> "omo, I'm very excited" + +How it decides (rule-based, no model needed): + 1. letters: Yoruba-only (ẹ ṣ, tone-marked n/m), Igbo-only (ị ụ ṅ), Hausa-only (ɓ ɗ ƙ ƴ), shared ọ; + 2. lexicons of frequent function words / greetings per language (diacritics folded for lookup); + 3. Pidgin markers ("dey", "abi", "omo", "wetin", "abeg", "una", "no go", "e don", "know say", pronoun + "go" + verb ...): + a clause of plain-ASCII words that contains a Pidgin marker becomes Pidgin as a whole; + 4. clause context: ambiguous words ("na", "o", "ka", "ya", "ma", "ni" ...) and unknown words take the language of the + clause they sit in, then of the neighbouring clause; plain ASCII with no evidence falls back to English. + 5. optional: --lid glotlid adds clause-level scores from GlotLID v3 (cis-lmu/glotlid, fastText, Apache-2.0 + notices, + 1.7 GB download, needs `pip install fasttext`), mainly to separate untagged Igbo/Yoruba/Hausa written without + diacritics from English. + +Overrides: lang_map {"omo": "pcm"} forces a word; force_lang renders everything in one language. +Always print the tagged text and let the user fix it: the tagger is a convenience, not ground truth. + + python tagger.py "Omo, this traffic no be here o. I go call you later." + python tagger.py --lang_map "omo=pcm,sha=pcm" "..." +""" +import argparse, json, re, sys, unicodedata + +LANGS = ["eng", "pcm", "ibo", "yor", "hau"] +NG = ("ibo", "yor", "hau") +TAGGED = re.compile(r"<(eng|ibo|hau|yor|pcm)>") + + +def fold(w): + """lowercase, NFD, drop every combining mark (tones, dot below, dot above), map hooked letters.""" + w = unicodedata.normalize("NFD", w.lower()) + w = "".join(c for c in w if not unicodedata.combining(c)) + return w.translate(str.maketrans({"ɓ": "b", "ɗ": "d", "ƙ": "k", "ƴ": "y", "’": "'", "ʼ": "'", "‘": "'"})) + + +def _set(s): + return {fold(x) for x in s.split()} + + +def _exact(s): + return {unicodedata.normalize("NFC", x.lower()) for x in s.split()} + + +# --- lexicons (frequent, high-precision words; folded). Words shared across languages are listed in each. +_RAW = { + "ibo": """ + bụ nke ndị ihe ụlọ mmiri kedu ndewo nụ ụmụ ụmụnna nna nne anyị unu gị ha ọ ya ka ma + nwere ga ji dị ọma biko daalụ dalu ụtọ obi ike nri ego akwụkwọ ọrụ maka ebe otu ọzọ ugbu + a m n' na mere meela kwa kwesịrị ịga ịbịa bịa gaa nyere aka mmadụ obodo nwanne nwa nwoke nwanyị + ụbọchị echi taa ụnyaahụ abalị ututu ehihie mgbede oge afọ ọnwa izu ukwuu nnukwu ntakịrị + mana ọbụna ọbụla ruo site gbasara ebee onye gịnị olee ole kpọmkwem eziokwu nso anọ abụọ atọ + ise isii asaa asatọ itoolu iri mbụ zuru ezu ahụ ahụla ịhụ hụ chere chọrọ chọ mara amaghị + nọ nọrọ bịara gara kwuru kwuo sị siri esi na-esi na-eme eme mee eji ogologo ọkụ ụzọ ahịa ụka + ọnụ ụtụtụ ka ọ dị mma mma nnọọ kpọọ zụta re ree rie riri ụmụaka ezinụlọ chi chukwu ndụ + """, + "yor": """ + ni ti si sí àti àwọn mo o wa wọn kò ṣé jẹ́ ọjọ́ ẹ káàbọ̀ ilé ìlú ọmọ púpọ̀ dáadáa bẹ́ẹ̀ ni + náà yìí kan mi wa yín rẹ̀ rẹ tirẹ̀ fún láti pé ń máa yóò ó a jẹ ṣe lọ wá rí fẹ́ mọ̀ sọ gbọ́ + ẹ̀kọ́ iṣẹ́ owó ọjà òní ọ̀la àná ìpàdé padà ilé-ìwé ọjọ́ ajé ìsinmi alẹ́ àárọ̀ káàárọ̀ káàsán + káalẹ́ ẹ kú ṣé gan an pàdé ìyá bàbá ẹ̀gbọ́n àbúrò ọ̀rẹ́ ènìyàn èèyàn àgbà ọba ìjọba + nítorí ṣùgbọ́n tàbí bí kí kì ìgbà ibi ibo kílódé báwo kíni ta tani mélòó ọ̀kan méjì mẹ́ta + mẹ́rin márùn-ún ẹ̀ẹ̀kan dúpẹ́ ẹ ṣeun ẹ̀ jọ̀ọ́ jọ̀ọ́ oúnjẹ omi ilẹ̀ ojú ọwọ́ ẹsẹ̀ + """, + "hau": """ + da ba ya ta ka ki su mu na ne ce ina yaya barka kuke don haka za idan mai wannan wancan yana + tana suna muna kuna sannu lafiya kwana aiki gobe yau jiya ruwa sai amma kuma shi ita nan + wani wata abin zan zai zata zamu mutane gida kudi gode sosai tafi fita zuwa laima yamma safe + rana dare mako shekara wata yanzu kai ke ku mun sun ban bai bata sani so iya yi yin + farashin tumatur tashi kasuwa abinci ruwan sha babu akwai kamar saboda domin tare har tun + daga cikin waje gaba baya sama kasa me wane wace ina yaushe nawa daya biyu uku hudu biyar + """, + "pcm": """ + dey abi omo wetin abeg una wahala sabi oga sef sha oya jare ehn ehen shey kpele pikin + chop comot waka belle palava wan wey dem don am siddon gist gbam yawa kampe sharp sharp + nawa wahala kolo japa ginger vex dash ogbonge padi paddy shine eye + """, + "eng": """ + the a an and or but if then so because of to in on at for with from by about as into over + after before between through during without under again further here there when where why how + all any both each few more most other some such no nor not only own same than too very can + will just should now i me my we our you your he him his she her it its they them their what + which who whom this that these those am is are was were be been being have has had having do + does did doing would could might must shall may please thank thanks yes hello hi good morning + evening afternoon night today tomorrow yesterday week year time day people man woman child + children team meeting report school market road weather rain safe everyone everybody + new old big small great right left back home work job money food water house car family + friend friends know think say said tell told make made take took get got give gave go went + come came see saw look want need like love help call send sent finish start stop keep let put + read write talk speak discuss three two one four five six seven eight nine ten first second + last next soon late early ready sure also still really very much many lot thing things way + out up down off well done going coming doing nothing something everything anything model + """, +} +LEX = {l: _set(v) for l, v in _RAW.items()} # folded (diacritics dropped) for lookup +LEX_EXACT = {l: _exact(v) for l, v in _RAW.items()} # as written +_N_LEX = {} +for _l, _v in LEX.items(): + for _w in _v: + _N_LEX[_w] = _N_LEX.get(_w, 0) + 1 # >1 = word shared by several languages (na, o, ka, ya ...) +# words that are Pidgin markers on their own (strong evidence) +PCM_STRONG = _set("dey abi omo wetin abeg una wahala sabi sef oya jare shey pikin chop comot waka palava wan wey dem siddon vex japa ehn ehen") +PCM_BIGRAMS = {("no", "go"), ("no", "be"), ("no", "fit"), ("no", "dey"), ("e", "don"), ("e", "dey"), ("e", "no"), + ("know", "say"), ("tell", "am"), ("well", "well"), ("how", "far"), ("how", "body"), ("na", "so"), + ("make", "we"), ("make", "i"), ("make", "una"), ("make", "dem"), ("i", "no"), ("we", "no"), + ("you", "no"), ("dem", "no"), ("no", "easy"), ("no", "forget"), ("no", "vex"), ("fit", "heavy"), + ("don", "come"), ("don", "go"), ("for", "inside"), ("una", "well"), ("e", "go"), ("na", "you"), + ("na", "me"), ("na", "im"), ("na", "dem"), ("na", "una"), ("give", "you"), ("lie", "give")} +PRON = _set("i we you dem una e im him she he they") +NOT_VERB_AFTER_GO = _set("to home there back out away on in up down off ahead") +PCM_AMBIG_END = _set("o") # clause-final particle "o" in an ASCII clause -> Pidgin evidence + +YOR_CH = set("ẹṣ") # after NFC lowercase; also e/s + U+0323 +IBO_CH = set("ịụṅ") +HAU_CH = set("ɓɗƙƴ") +WORD = re.compile(r"[^\W\d_](?:[^\W\d_]|['’ʼ\-\u0300-\u036f])*", re.U) +CLAUSE_END = re.compile(r"[,;:.?!]") + + +def char_scores(w): + s = dict.fromkeys(LANGS, 0.0) + nfd = unicodedata.normalize("NFD", w.lower()) + base = None + for c in nfd: + if unicodedata.combining(c): + if c == "\u0323": # dot below + if base in "es": + s["yor"] += 3 + elif base in "iu": + s["ibo"] += 3 + elif base == "o": + s["yor"] += 1; s["ibo"] += 1 + elif c == "\u0307" and base == "n": + s["ibo"] += 3 + elif c in "\u0300\u0301\u0304": # tones + if base in "nm": + s["yor"] += 2 + else: + s["yor"] += 0.8; s["ibo"] += 0.3 + continue + base = c + if c in HAU_CH: + s["hau"] += 4 + if not any(s.values()) and re.fullmatch(r"[a-z'\-]+", fold(w)): + s["ascii"] = 1 + return s + + +def word_scores(w): + s = char_scores(w) + ascii_ = s.pop("ascii", 0) + f = fold(w) + parts = [f] + (f.split("-") if "-" in f else []) + if f.startswith("n'") or re.match(r"^na-[a-z]", f): + s["ibo"] += 3 + ex = unicodedata.normalize("NFC", w.lower()) + for p in parts: + amb = 0.5 if _N_LEX.get(p, 0) > 1 else 1.0 # shared words: let the clause decide + for l in LANGS: + if p in LEX[l]: + w_ = 1.5 if (p == f and ex in LEX_EXACT[l]) or ascii_ and p in LEX_EXACT[l] else 0.7 + s[l] += amb * (w_ if l != "pcm" else 1.0) + if f in PCM_STRONG and ascii_: # Pidgin markers are plain ASCII ("ọmọ" is Yoruba) + s["pcm"] += 4 + # orthography hints (weak) + if ascii_: + if re.search(r"(th|wh|ck|tion|ing$|ould$|ght)", f): + s["eng"] += 1 + if re.match(r"^(nw|kw|gw|mm|nn|nk|ng[a-z])", f) and f not in LEX["eng"]: + s["ibo"] += 0.7 + if re.search(r"(ts|^ky|^'y|aa|ii|uu)", f) and f not in LEX["eng"]: + s["hau"] += 0.5 + return s, bool(ascii_) + + +def tokenize(text): + """-> list of (start, end, word) for words; everything else is glue.""" + return [(m.start(), m.end(), m.group(0)) for m in WORD.finditer(text)] + + +def clauses(text, toks): + """clause index for each word: a new clause starts after , ; : . ? !""" + ends = [m.start() for m in CLAUSE_END.finditer(text)] + out, k = [], 0 + for a, b, w in toks: + while k < len(ends) and ends[k] < a: + k += 1 + out.append(k) + return out + + +def _norm(d): + t = sum(max(0.0, v) for v in d.values()) + return {k: (max(0.0, v) / t if t else 0.0) for k, v in d.items()} + + +class Tagger: + def __init__(self, lid=None, lang_map=None): + self.lang_map = {fold(k): v for k, v in (lang_map or {}).items()} + self.lid = None + if lid == "glotlid": + import fasttext + from huggingface_hub import hf_hub_download + self.lid = fasttext.load_model(hf_hub_download("cis-lmu/glotlid", "model.bin")) + + def _lid_scores(self, text): + labs, ps = self.lid.predict(text.replace("\n", " "), k=20) + m = {"__label__eng_Latn": "eng", "__label__pcm_Latn": "pcm", "__label__ibo_Latn": "ibo", + "__label__yor_Latn": "yor", "__label__hau_Latn": "hau"} + s = dict.fromkeys(LANGS, 0.0) + for l, p in zip(labs, ps): + if l in m: + s[m[l]] += float(p) + return s + + def word_langs(self, text): + toks = tokenize(text) + if not toks: + return toks, [] + cl = clauses(text, toks) + n = len(toks) + sc, asc = zip(*[word_scores(w) for _, _, w in toks]) + sc = [dict(s) for s in sc] + f = [fold(w) for _, _, w in toks] + # Pidgin bigrams and "pronoun go verb" + for i in range(n - 1): + if cl[i] != cl[i + 1]: + continue + if (f[i], f[i + 1]) in PCM_BIGRAMS: + sc[i]["pcm"] += 2.5; sc[i + 1]["pcm"] += 2.5 + if i + 2 < n and f[i] in PRON and f[i + 1] == "go" and cl[i + 2] == cl[i] and f[i + 2] not in NOT_VERB_AFTER_GO \ + and f[i + 2] in LEX["eng"] | LEX["pcm"]: + for j in (i, i + 1, i + 2): + sc[j]["pcm"] += 2.5 + # clause-level evidence + C = sorted(set(cl)) + cs = {c: dict.fromkeys(LANGS, 0.0) for c in C} + for i in range(n): + for l in LANGS: + cs[cl[i]][l] += sc[i][l] + # clause-final "o" in an ASCII clause = Pidgin particle (Yoruba "o" clauses carry other Yoruba evidence) + for i in range(n): + last = i == n - 1 or cl[i + 1] != cl[i] + if last and f[i] in PCM_AMBIG_END and asc[i]: + others = [j for j in range(n) if cl[j] == cl[i] and j != i] + if others and all(asc[j] for j in others) and cs[cl[i]]["yor"] < 2: + sc[i]["pcm"] += 3; cs[cl[i]]["pcm"] += 3 + if self.lid is not None: + for c in C: + ws = [toks[i][2] for i in range(n) if cl[i] == c] + for l, v in self._lid_scores(" ".join(ws)).items(): + cs[c][l] += 2.0 * v + out = [] + for i in range(n): + c = cl[i] + ctx = _norm(cs[c]) + nb = dict.fromkeys(LANGS, 0.0) # neighbouring clauses, weaker + for d in (-1, 1): + if c + d in cs: + for l, v in _norm(cs[c + d]).items(): + nb[l] += 0.3 * v + tot = {l: sc[i][l] + 1.5 * ctx[l] + nb[l] for l in LANGS} + # plain-ASCII words in a clause without Nigerian-language evidence: English or Pidgin + if asc[i] and max(sc[i][l] for l in NG) < 1.0: + ng_ctx = max(cs[c][l] for l in NG) + if ng_ctx < 1.5 * max(cs[c]["eng"], cs[c]["pcm"], 0.1): + pc = cs[c]["pcm"] >= 2.5 + out.append("pcm" if pc else ("eng" if sc[i]["pcm"] < 4 else "pcm")) + continue + best = max(tot, key=tot.get) + if tot[best] <= 0: + best = "eng" + out.append(best) + for i in range(n): # user overrides + if f[i] in self.lang_map: + out[i] = self.lang_map[f[i]] + return toks, out + + def tag(self, text, force_lang=None): + text = unicodedata.normalize("NFC", text.strip()) + if TAGGED.search(text): + return text # already tagged: leave alone + if force_lang: + return f"<{force_lang}>{text}" + toks, langs = self.word_langs(text) + if not toks: + return f"{text}" + # spans: consecutive same-language words; glue (spaces/punct) stays with the preceding word + spans, cur_l, a = [], langs[0], 0 + for i in range(1, len(toks)): + if langs[i] != cur_l: + b = toks[i][0] + seg = text[a:b] + spans.append((cur_l, seg)) + cur_l, a = langs[i], b + spans.append((cur_l, text[a:])) + lead = text[:toks[0][0]] + out = [] + for k, (l, s) in enumerate(spans): + s2 = (lead + s) if k == 0 else s + core = s2.rstrip() + out.append(f"<{l}>{core}") + return " ".join(out) + + +def parse_map(s): + m = {} + for kv in (s or "").split(","): + if "=" in kv: + k, v = kv.split("=", 1) + v = v.strip() + if v not in LANGS: + raise ValueError(f"--lang_map: unknown language '{v}' (use {LANGS})") + m[k.strip()] = v + return m + + +def spans_of(tagged): + return [(m.group(1), m.group(2)) for m in re.finditer(r"<(eng|ibo|hau|yor|pcm)>(.*?)", tagged, re.S)] + + +def word_labels(tagged): + """gold/pred word-level labels from a tagged string.""" + out = [] + for l, s in spans_of(tagged): + out += [(fold(w), l) for _, _, w in tokenize(s)] + return out + + +def strip_tags(tagged): + return re.sub(r"\s+", " ", re.sub(r"", "", tagged)).strip() + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("text") + ap.add_argument("--lang_map", default="", help='per-word overrides, e.g. "omo=pcm,sha=pcm"') + ap.add_argument("--force_lang", choices=LANGS) + ap.add_argument("--lid", choices=["none", "glotlid"], default="none") + a = ap.parse_args() + t = Tagger(None if a.lid == "none" else a.lid, parse_map(a.lang_map)) + print(t.tag(a.text, a.force_lang)) + + +if __name__ == "__main__": + main()