├── LICENSE ├── README.md ├── conf ├── __init__.py ├── __init__.pyc ├── hyparam.py └── hyparam.pyc ├── data └── aishell │ ├── aishell.py │ ├── manifest.dev │ ├── manifest.test │ ├── manifest.train │ ├── mean_std.npz │ └── vocab.txt ├── data_utils ├── __init__.py ├── __init__.pyc ├── audio.py ├── audio.pyc ├── audio_featurizer.py ├── audio_featurizer.pyc ├── build_vocab.py ├── compute_mean_std.py ├── normalizer.py ├── normalizer.pyc ├── process_manifest.py ├── process_manifest.pyc ├── speech.py ├── speech.pyc ├── utility.py ├── utility.pyc ├── utils.py └── utils.pyc ├── demo_cache └── __init__.py ├── demo_client.sh ├── demo_server.sh ├── deploy_demo ├── _init_paths.py ├── _init_paths.pyc ├── assert_zero.py ├── assert_zero.pyc ├── demo_client.py └── demo_server.py ├── example └── aishell │ └── run_data.sh ├── img └── arc.png ├── model_utils ├── __init__.py ├── __init__.pyc ├── init_model.py ├── init_model.pyc ├── network.py └── network.pyc ├── models └── lm │ └── __init__.py ├── test.py ├── test_build.py ├── train.py └── utils ├── __init__.py ├── __init__.pyc ├── decoder ├── __init__.py ├── __init__.pyc ├── model.py ├── model.pyc ├── swig_wrapper.py └── swig_wrapper.pyc ├── utility.py └── utility.pyc /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 基于tensorflow的语音识别系统 2 | ## 简介 3 | 基于 tensorflow 的中文语音识别框架, 模型结构参考 百度[Deepspeech2](http://proceedings.mlr.press/v48/amodei16.pdf) 的论文,训练语料采用Aishell 170 小时数据. 模型结构如下图所示: 4 | 5 |

6 | 7 |
百度Deepspeech2 语音识别模型结构 8 |

9 | 10 | **目前模型训练存在不收敛情况,正在调参研究中** 11 | 12 | ## 安装 13 | ### Prerequisites 14 | * 本程序基于 Python2.7 15 | * Tensorflow 版本应大于1.3,否则tf.nn.softmax等函数可能报错 16 | 17 | ### Setup 18 | 19 | * 安装以下依赖库 pkg-config, flac, ogg, vorbis, boost and swig,在Ubuntu 环境下可以通过apt-get 进行安装 20 | ''' 21 | sudo apt-get install -y pkg-config libflac-dev libogg-dev libvorbis-dev libboost-dev swig 22 | ''' 23 | 24 | ## 运行 25 | 26 | * 进入conf文件夹,修改 hyparam.py 中的模型参数 27 | * 进入example/aishell 修改 run_data.sh 内相关存储路径后,运行该脚本生成 manfest.{train,dev,test} 文件、vocab.txt以及 mean_std.npz 28 | * 运行 train.py 训练模型 29 | * 运行test.py 进行测试 30 | 31 | ## server/client 运行 32 | 33 | * 打开 ./demo_client.sh or ./demo_server.sh 文件配置 IP、端口等信息 34 | * 执行 ./demo_server.sh 启动服务器 35 | * 执行 ./demo_client.sh 启动客户端 36 | * 在客户端内,持续按空格进行录音,松开空格后发送音频到服务器端进行语音识别 37 | 38 | ## 语言模型 39 | 语言模型采用的是百度提供的中文语言模型,由KenLM工具生成并剪枝得到。详情请查看[Mandarin LM](https://github.com/PaddlePaddle/DeepSpeech#mandarin-lm) 40 | 41 | ### 语言模型参数 42 | 语言模型用于测试和使用阶段的解码。因此当声学模型训练时,语言模型的参数也需要进行改变。hyparam里的参数只是随手写的... 43 | 44 | ## TODO 45 | * 对模型进行调参或结构调整使其能够使用。 46 | 47 | ## Ref 48 | 以 [xxbb1234021/speech_recognition](https://github.com/xxbb1234021/speech_recognition) 和 [PaddlePaddle/Deepspeech2](https://github.com/PaddlePaddle/DeepSpeech)为基础进行修改 49 | -------------------------------------------------------------------------------- /conf/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/conf/__init__.py -------------------------------------------------------------------------------- /conf/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/conf/__init__.pyc -------------------------------------------------------------------------------- /conf/hyparam.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | 5 | class Config(): 6 | ''' 7 | Class to save all config in traing and decoding 8 | :param: None 9 | ''' 10 | def __init__(self): 11 | 12 | self.batch_size = 8 13 | 14 | # Network hyparam 15 | self.n_brnn_layers = 3 16 | self.n_cell_brnn = 512 17 | self.learning_rate = 0.001 18 | self.keep_dropout_rate = 0.95 19 | self.b_stddev = 0.046875 20 | self.h_stddev = 0.046875 21 | 22 | # Feature 23 | self.n_input = 39 # 计算MFCC的个数 24 | self.n_context = 2 # n gram around current frame 25 | self.specgram_type = 'linear' # if 'linear' use specgram. 'mfcc' use mfcc with mfcc + delta1 + delta2 26 | self.use_bn = True # Batch normalization 27 | 28 | # Decoder 29 | self.use_lm_decoder = True # Wether use lm decoder. If False, use tf.ctc_beam_search_decoder 30 | self.alpha = 1.2 31 | self.beta = 2.5 32 | self.cutoff_prob = 0.99 33 | self.cutoff_top_n = 10 34 | self.num_proc_bsearch = 8 35 | self.beam_size = 400 36 | self.lang_model_path = './models/lm/zh_giga.no_cna_cmn.prune01244.klm' # you can download it in https://github.com/PaddlePaddle/DeepSpeech 37 | 38 | # Config path 39 | self.vocab_path = u'data/aishell/vocab.txt' 40 | self.wav_path = u'/media/nlp/23ACE59C56A55BF3/wav_file/thchs30/thchs30_tensorflow/wav/' 41 | self.lable_file = u'/media/nlp/23ACE59C56A55BF3/wav_file/thchs30/thchs30_tensorflow/doc/trans/test.word.txt' 42 | self.savedir = u'/media/nlp/23ACE59C56A55BF3/wav_file/thchs30/thchs30_tensorflow/' 43 | self.savefile = u'speech.cpkt' 44 | self.tensorboardfile = u'/media/nlp/23ACE59C56A55BF3/wav_file/thchs30/thchs30_tensorflow/wav/log' 45 | -------------------------------------------------------------------------------- /conf/hyparam.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/conf/hyparam.pyc -------------------------------------------------------------------------------- /data/aishell/aishell.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | # author@baidu deepspeech 5 | # modified by Pelhans 6 | 7 | """ 8 | Prepare Aishell mandarin dataset 9 | Create manifest files. 10 | Manifest file is a json-format file with each line containing the 11 | meta data (i.e. audio filepath, transcript and audio duration) 12 | of each audio file in the data set. 13 | """ 14 | from __future__ import absolute_import 15 | from __future__ import division 16 | from __future__ import print_function 17 | 18 | import os, sys 19 | import codecs 20 | import soundfile 21 | import json 22 | import argparse 23 | 24 | sys.path.append(r'../../') 25 | from data_utils.utility import download, unpack 26 | 27 | 28 | DATA_HOME = "/media/nlp/23ACE59C56A55BF3/wav_file/" 29 | 30 | URL_ROOT = 'http://www.openslr.org/resources/33' 31 | DATA_URL = URL_ROOT + '/data_aishell.tgz' 32 | MD5_DATA = '2f494334227864a8a8fec932999db9d8' 33 | 34 | parser = argparse.ArgumentParser(description=__doc__) 35 | parser.add_argument( 36 | "--target_dir", 37 | default=DATA_HOME + "aishell", 38 | type=str, 39 | help="Directory to save the dataset. (default: %(default)s)") 40 | parser.add_argument( 41 | "--manifest_prefix", 42 | default="manifest", 43 | type=str, 44 | help="Filepath prefix for output manifests. (default: %(default)s)") 45 | args = parser.parse_args() 46 | 47 | 48 | def create_manifest(data_dir, manifest_path_prefix): 49 | print("Creating manifest %s ..." % manifest_path_prefix) 50 | json_lines = [] 51 | transcript_path = os.path.join(data_dir, 'transcript', 52 | 'aishell_transcript_v0.8.txt') 53 | transcript_dict = {} 54 | for line in codecs.open(transcript_path, 'r', 'utf-8'): 55 | line = line.strip() 56 | if line == '': continue 57 | audio_id, text = line.split(' ', 1) 58 | # remove withespace 59 | text = ''.join(text.split()) 60 | transcript_dict[audio_id] = text 61 | 62 | data_types = ['train', 'dev', 'test'] 63 | for type in data_types: 64 | del json_lines[:] 65 | audio_dir = os.path.join(data_dir, 'wav', type) 66 | for subfolder, _, filelist in sorted(os.walk(audio_dir)): 67 | for fname in filelist: 68 | audio_path = os.path.join(subfolder, fname) 69 | audio_id = fname[:-4] 70 | # if no transcription for audio then skipped 71 | if audio_id not in transcript_dict: 72 | continue 73 | audio_data, samplerate = soundfile.read(audio_path) 74 | duration = float(len(audio_data) / samplerate) 75 | text = transcript_dict[audio_id] 76 | json_lines.append( 77 | json.dumps( 78 | { 79 | 'audio_filepath': audio_path, 80 | 'duration': duration, 81 | 'text': text 82 | }, 83 | ensure_ascii=False)) 84 | manifest_path = manifest_path_prefix + '.' + type 85 | with codecs.open(manifest_path, 'w', 'utf-8') as fout: 86 | for line in json_lines: 87 | fout.write(line + '\n') 88 | 89 | 90 | def prepare_dataset(url, md5sum, target_dir, manifest_path): 91 | """Download, unpack and create manifest file.""" 92 | data_dir = os.path.join(target_dir, 'data_aishell') 93 | if not os.path.exists(data_dir): 94 | filepath = download(url, md5sum, target_dir) 95 | unpack(filepath, target_dir) 96 | # unpack all audio tar files 97 | audio_dir = os.path.join(data_dir, 'wav') 98 | for subfolder, _, filelist in sorted(os.walk(audio_dir)): 99 | for ftar in filelist: 100 | unpack(os.path.join(subfolder, ftar), subfolder, True) 101 | else: 102 | print("Skip downloading and unpacking. Data already exists in %s." % 103 | target_dir) 104 | create_manifest(data_dir, manifest_path) 105 | 106 | 107 | def main(): 108 | if args.target_dir.startswith('~'): 109 | args.target_dir = os.path.expanduser(args.target_dir) 110 | 111 | prepare_dataset( 112 | url=DATA_URL, 113 | md5sum=MD5_DATA, 114 | target_dir=args.target_dir, 115 | manifest_path=args.manifest_prefix) 116 | 117 | 118 | if __name__ == '__main__': 119 | main() 120 | -------------------------------------------------------------------------------- /data/aishell/mean_std.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/data/aishell/mean_std.npz -------------------------------------------------------------------------------- /data/aishell/vocab.txt: -------------------------------------------------------------------------------- 1 | 的 2 | 一 3 | 在 4 | 是 5 | 中 6 | 十 7 | 人 8 | 有 9 | 二 10 | 上 11 | 了 12 | 不 13 | 国 14 | 市 15 | 大 16 | 业 17 | 为 18 | 年 19 | 三 20 | 发 21 | 个 22 | 出 23 | 分 24 | 行 25 | 会 26 | 地 27 | 公 28 | 成 29 | 这 30 | 和 31 | 到 32 | 五 33 | 对 34 | 产 35 | 时 36 | 能 37 | 场 38 | 房 39 | 来 40 | 以 41 | 百 42 | 新 43 | 日 44 | 之 45 | 者 46 | 将 47 | 现 48 | 要 49 | 家 50 | 四 51 | 多 52 | 资 53 | 月 54 | 也 55 | 后 56 | 零 57 | 方 58 | 下 59 | 机 60 | 前 61 | 于 62 | 生 63 | 点 64 | 比 65 | 开 66 | 高 67 | 动 68 | 经 69 | 进 70 | 报 71 | 赛 72 | 体 73 | 用 74 | 价 75 | 金 76 | 车 77 | 可 78 | 万 79 | 子 80 | 司 81 | 过 82 | 被 83 | 自 84 | 手 85 | 本 86 | 全 87 | 作 88 | 目 89 | 八 90 | 最 91 | 电 92 | 六 93 | 部 94 | 我 95 | 交 96 | 七 97 | 面 98 | 力 99 | 九 100 | 企 101 | 度 102 | 实 103 | 加 104 | 元 105 | 小 106 | 其 107 | 定 108 | 已 109 | 同 110 | 还 111 | 城 112 | 而 113 | 工 114 | 内 115 | 与 116 | 关 117 | 合 118 | 得 119 | 他 120 | 法 121 | 网 122 | 品 123 | 就 124 | 入 125 | 记 126 | 理 127 | 名 128 | 长 129 | 事 130 | 两 131 | 商 132 | 女 133 | 们 134 | 京 135 | 都 136 | 并 137 | 但 138 | 制 139 | 平 140 | 化 141 | 主 142 | 期 143 | 重 144 | 次 145 | 据 146 | 表 147 | 保 148 | 通 149 | 相 150 | 利 151 | 政 152 | 道 153 | 务 154 | 天 155 | 量 156 | 间 157 | 展 158 | 北 159 | 第 160 | 员 161 | 所 162 | 提 163 | 因 164 | 海 165 | 好 166 | 数 167 | 投 168 | 示 169 | 管 170 | 建 171 | 外 172 | 更 173 | 区 174 | 从 175 | 等 176 | 项 177 | 应 178 | 运 179 | 性 180 | 计 181 | 增 182 | 收 183 | 当 184 | 信 185 | 明 186 | 位 187 | 着 188 | 民 189 | 起 190 | 学 191 | 台 192 | 规 193 | 调 194 | 持 195 | 设 196 | 很 197 | 今 198 | 没 199 | 格 200 | 正 201 | 股 202 | 心 203 | 然 204 | 去 205 | 此 206 | 安 207 | 无 208 | 如 209 | 受 210 | 科 211 | 美 212 | 果 213 | 线 214 | 身 215 | 队 216 | 基 217 | 达 218 | 世 219 | 东 220 | 证 221 | 需 222 | 费 223 | 近 224 | 情 225 | 消 226 | 布 227 | 未 228 | 标 229 | 些 230 | 求 231 | 步 232 | 男 233 | 看 234 | 里 235 | 向 236 | 千 237 | 说 238 | 息 239 | 回 240 | 式 241 | 及 242 | 米 243 | 门 244 | 意 245 | 接 246 | 改 247 | 续 248 | 监 249 | 联 250 | 总 251 | 打 252 | 亿 253 | 讯 254 | 至 255 | 销 256 | 获 257 | 件 258 | 住 259 | 水 260 | 整 261 | 老 262 | 张 263 | 院 264 | 技 265 | 环 266 | 该 267 | 局 268 | 路 269 | 特 270 | 售 271 | 强 272 | 问 273 | 购 274 | 楼 275 | 由 276 | 统 277 | 处 278 | 涨 279 | 让 280 | 乐 281 | 题 282 | 决 283 | 系 284 | 称 285 | 影 286 | 推 287 | 银 288 | 少 289 | 广 290 | 只 291 | 显 292 | 战 293 | 己 294 | 降 295 | 常 296 | 创 297 | 户 298 | 首 299 | 完 300 | 查 301 | 案 302 | 选 303 | 种 304 | 策 305 | 易 306 | 牌 307 | 参 308 | 立 309 | 营 310 | 跑 311 | 结 312 | 势 313 | 备 314 | 认 315 | 程 316 | 造 317 | 球 318 | 准 319 | 低 320 | 各 321 | 超 322 | 友 323 | 代 324 | 存 325 | 再 326 | 级 327 | 放 328 | 做 329 | 原 330 | 文 331 | 险 332 | 物 333 | 传 334 | 款 335 | 样 336 | 单 337 | 较 338 | 告 339 | 转 340 | 界 341 | 供 342 | 警 343 | 解 344 | 给 345 | 客 346 | 风 347 | 华 348 | 州 349 | 预 350 | 周 351 | 取 352 | 她 353 | 率 354 | 指 355 | 则 356 | 走 357 | 搜 358 | 每 359 | 导 360 | 视 361 | 片 362 | 变 363 | 集 364 | 升 365 | 排 366 | 口 367 | 型 368 | 头 369 | 任 370 | 源 371 | 术 372 | 积 373 | 服 374 | 直 375 | 非 376 | 研 377 | 别 378 | 约 379 | 军 380 | 快 381 | 组 382 | 速 383 | 斯 384 | 社 385 | 专 386 | 医 387 | 活 388 | 领 389 | 使 390 | 王 391 | 器 392 | 想 393 | 马 394 | 构 395 | 团 396 | 份 397 | 模 398 | 难 399 | 智 400 | 或 401 | 先 402 | 星 403 | 施 404 | 带 405 | 么 406 | 拉 407 | 引 408 | 岁 409 | 照 410 | 微 411 | 融 412 | 支 413 | 南 414 | 狐 415 | 儿 416 | 游 417 | 办 418 | 始 419 | 权 420 | 绩 421 | 士 422 | 媒 423 | 确 424 | 半 425 | 仅 426 | 奥 427 | 控 428 | 况 429 | 深 430 | 限 431 | 委 432 | 随 433 | 税 434 | 类 435 | 越 436 | 注 437 | 流 438 | 望 439 | 幅 440 | 均 441 | 争 442 | 亚 443 | 财 444 | 节 445 | 包 446 | 划 447 | 众 448 | 额 449 | 空 450 | 态 451 | 西 452 | 终 453 | 土 454 | 连 455 | 致 456 | 娱 457 | 某 458 | 伤 459 | 济 460 | 育 461 | 值 462 | 际 463 | 仍 464 | 责 465 | 充 466 | 知 467 | 买 468 | 具 469 | 师 470 | 汽 471 | 质 472 | 几 473 | 响 474 | 反 475 | 究 476 | 条 477 | 李 478 | 居 479 | 共 480 | 感 481 | 站 482 | 露 483 | 观 484 | 优 485 | 宅 486 | 继 487 | 季 488 | 冠 489 | 教 490 | 光 491 | 负 492 | 采 493 | 博 494 | 见 495 | 互 496 | 域 497 | 范 498 | 山 499 | 段 500 | 依 501 | 极 502 | 气 503 | 图 504 | 境 505 | 举 506 | 套 507 | 何 508 | 尔 509 | 失 510 | 形 511 | 落 512 | 府 513 | 锦 514 | 健 515 | 复 516 | 益 517 | 贷 518 | 许 519 | 林 520 | 票 521 | 把 522 | 又 523 | 审 524 | 断 525 | 效 526 | 竞 527 | 停 528 | 击 529 | 容 530 | 色 531 | 双 532 | 拿 533 | 却 534 | 足 535 | 热 536 | 松 537 | 那 538 | 清 539 | 诉 540 | 曾 541 | 议 542 | 减 543 | 孩 544 | 考 545 | 功 546 | 助 547 | 板 548 | 除 549 | 太 550 | 试 551 | 港 552 | 疑 553 | 例 554 | 配 555 | 你 556 | 刘 557 | 占 558 | 根 559 | 装 560 | 才 561 | 置 562 | 突 563 | 即 564 | 且 565 | 离 566 | 论 567 | 盘 568 | 涉 569 | 破 570 | 爱 571 | 真 572 | 晚 573 | 演 574 | 练 575 | 纪 576 | 券 577 | 货 578 | 农 579 | 职 580 | 付 581 | 压 582 | 死 583 | 龙 584 | 库 585 | 言 586 | 火 587 | 严 588 | 号 589 | 护 590 | 稳 591 | 话 592 | 协 593 | 昨 594 | 善 595 | 判 596 | 算 597 | 救 598 | 满 599 | 克 600 | 亲 601 | 评 602 | 遭 603 | 红 604 | 卖 605 | 江 606 | 软 607 | 苹 608 | 田 609 | 药 610 | 希 611 | 普 612 | 略 613 | 官 614 | 验 615 | 闻 616 | 按 617 | 飞 618 | 边 619 | 补 620 | 担 621 | 索 622 | 检 623 | 底 624 | 厂 625 | 故 626 | 革 627 | 钱 628 | 频 629 | 象 630 | 必 631 | 租 632 | 湾 633 | 执 634 | 校 635 | 虽 636 | 托 637 | 债 638 | 拍 639 | 录 640 | 够 641 | 远 642 | 园 643 | 像 644 | 阳 645 | 馀 646 | 历 647 | 润 648 | 料 649 | 早 650 | 征 651 | 兴 652 | 康 653 | 店 654 | 状 655 | 端 656 | 否 657 | 障 658 | 病 659 | 便 660 | 围 661 | 往 662 | 层 663 | 批 664 | 测 665 | 尽 666 | 省 667 | 精 668 | 核 669 | 德 670 | 香 671 | 移 672 | 承 673 | 群 674 | 列 675 | 临 676 | 食 677 | 介 678 | 留 679 | 云 680 | 遇 681 | 请 682 | 跌 683 | 奖 684 | 宝 685 | 轻 686 | 激 687 | 属 688 | 夫 689 | 它 690 | 景 691 | 什 692 | 维 693 | 假 694 | 村 695 | 英 696 | 宣 697 | 治 698 | 花 699 | 夺 700 | 白 701 | 初 702 | 胜 703 | 透 704 | 神 705 | 福 706 | 修 707 | 访 708 | 铁 709 | 养 710 | 副 711 | 跳 712 | 辆 713 | 登 714 | 秒 715 | 卡 716 | 违 717 | 婚 718 | 伟 719 | 幕 720 | 短 721 | 届 722 | 杨 723 | 角 724 | 差 725 | 午 726 | 纷 727 | 律 728 | 逐 729 | 左 730 | 训 731 | 董 732 | 刚 733 | 波 734 | 青 735 | 爆 736 | 陈 737 | 候 738 | 宁 739 | 酒 740 | 适 741 | 觉 742 | 字 743 | 书 744 | 似 745 | 黄 746 | 括 747 | 嫌 748 | 块 749 | 防 750 | 径 751 | 待 752 | 申 753 | 甚 754 | 独 755 | 退 756 | 庭 757 | 航 758 | 识 759 | 巴 760 | 右 761 | 述 762 | 止 763 | 亡 764 | 送 765 | 冲 766 | 眼 767 | 乘 768 | 络 769 | 罪 770 | 母 771 | 席 772 | 石 773 | 找 774 | 义 775 | 尼 776 | 令 777 | 志 778 | 换 779 | 析 780 | 损 781 | 卫 782 | 另 783 | 央 784 | 轮 785 | 扩 786 | 绿 787 | 滑 788 | 念 789 | 镇 790 | 刑 791 | 毒 792 | 史 793 | 紧 794 | 吴 795 | 免 796 | 抢 797 | 豪 798 | 乎 799 | 谈 800 | 纳 801 | 妈 802 | 罗 803 | 河 804 | 童 805 | 油 806 | 泳 807 | 洲 808 | 倒 809 | 吸 810 | 素 811 | 密 812 | 孙 813 | 富 814 | 县 815 | 穿 816 | 声 817 | 币 818 | 街 819 | 择 820 | 巨 821 | 犯 822 | 害 823 | 苏 824 | 思 825 | 愿 826 | 庆 827 | 促 828 | 追 829 | 跟 830 | 借 831 | 妻 832 | 措 833 | 赔 834 | 杀 835 | 旅 836 | 晨 837 | 摄 838 | 骗 839 | 温 840 | 汉 841 | 帮 842 | 链 843 | 迎 844 | 习 845 | 趋 846 | 贴 847 | 阶 848 | 疗 849 | 驾 850 | 黑 851 | 圳 852 | 版 853 | 罚 854 | 吃 855 | 亮 856 | 挥 857 | 背 858 | 虑 859 | 阿 860 | 私 861 | 细 862 | 绝 863 | 脑 864 | 础 865 | 切 866 | 命 867 | 裁 868 | 韩 869 | 写 870 | 伙 871 | 抓 872 | 须 873 | 秀 874 | 良 875 | 班 876 | 父 877 | 派 878 | 缺 879 | 享 880 | 脸 881 | 启 882 | 尚 883 | 戏 884 | 池 885 | 陆 886 | 撞 887 | 剧 888 | 危 889 | 招 890 | 签 891 | 戴 892 | 材 893 | 喜 894 | 夜 895 | 延 896 | 渐 897 | 偿 898 | 披 899 | 宗 900 | 针 901 | 操 902 | 募 903 | 拳 904 | 顺 905 | 艺 906 | 释 907 | 衣 908 | 拥 909 | 味 910 | 察 911 | 兰 912 | 迷 913 | 困 914 | 曝 915 | 竟 916 | 春 917 | 励 918 | 靠 919 | 室 920 | 欧 921 | 干 922 | 佳 923 | 估 924 | 坚 925 | 朋 926 | 净 927 | 盖 928 | 圈 929 | 歌 930 | 截 931 | 急 932 | 订 933 | 徽 934 | 束 935 | 患 936 | 顶 937 | 驶 938 | 距 939 | 序 940 | 累 941 | 晒 942 | 悉 943 | 丰 944 | 烈 945 | 姐 946 | 惠 947 | 硬 948 | 织 949 | 顾 950 | 挑 951 | 桥 952 | 培 953 | 旬 954 | 永 955 | 讨 956 | 暴 957 | 玩 958 | 伴 959 | 污 960 | 键 961 | 误 962 | 湖 963 | 怀 964 | 峰 965 | 欢 966 | 彩 967 | 归 968 | 冰 969 | 惊 970 | 探 971 | 古 972 | 败 973 | 异 974 | 抗 975 | 附 976 | 听 977 | 旧 978 | 馆 979 | 怎 980 | 缓 981 | 吗 982 | 暂 983 | 折 984 | 武 985 | 笑 986 | 刺 987 | 输 988 | 血 989 | 杰 990 | 熟 991 | 播 992 | 哪 993 | 简 994 | 休 995 | 阵 996 | 劳 997 | 拟 998 | 雪 999 | 坐 1000 | 账 1001 | 予 1002 | 昆 1003 | 丽 1004 | 刻 1005 | 避 1006 | 架 1007 | 凌 1008 | 谷 1009 | 钟 1010 | 绍 1011 | 音 1012 | 丈 1013 | 寻 1014 | 若 1015 | 综 1016 | 暖 1017 | 禁 1018 | 印 1019 | 威 1020 | 礼 1021 | 屋 1022 | 奇 1023 | 嘉 1024 | 语 1025 | 筹 1026 | 龄 1027 | 汇 1028 | 迅 1029 | 错 1030 | 徐 1031 | 雨 1032 | 座 1033 | 夏 1034 | 宽 1035 | 笔 1036 | 迪 1037 | 尤 1038 | 伦 1039 | 泽 1040 | 筑 1041 | 哥 1042 | 炒 1043 | 乡 1044 | 般 1045 | 粉 1046 | 谢 1047 | 倍 1048 | 攻 1049 | 野 1050 | 概 1051 | 肉 1052 | 朱 1053 | 杯 1054 | 虚 1055 | 鼓 1056 | 朝 1057 | 梦 1058 | 掉 1059 | 缩 1060 | 屏 1061 | 召 1062 | 震 1063 | 呼 1064 | 津 1065 | 冬 1066 | 码 1067 | 弟 1068 | 编 1069 | 幸 1070 | 郑 1071 | 潮 1072 | 锁 1073 | 拘 1074 | 梯 1075 | 渠 1076 | 储 1077 | 授 1078 | 晓 1079 | 诈 1080 | 秘 1081 | 涛 1082 | 川 1083 | 脚 1084 | 映 1085 | 染 1086 | 盛 1087 | 遍 1088 | 肥 1089 | 枪 1090 | 聚 1091 | 蓝 1092 | 洗 1093 | 斗 1094 | 讲 1095 | 画 1096 | 亏 1097 | 符 1098 | 刀 1099 | 吉 1100 | 翻 1101 | 扣 1102 | 守 1103 | 遗 1104 | 洋 1105 | 赞 1106 | 珠 1107 | 久 1108 | 呈 1109 | 宇 1110 | 丹 1111 | 侵 1112 | 苦 1113 | 挂 1114 | 毛 1115 | 森 1116 | 慢 1117 | 盗 1118 | 脱 1119 | 肯 1120 | 摩 1121 | 兵 1122 | 读 1123 | 枚 1124 | 添 1125 | 贵 1126 | 哈 1127 | 丝 1128 | 跨 1129 | 跃 1130 | 皮 1131 | 询 1132 | 榜 1133 | 顿 1134 | 逃 1135 | 胎 1136 | 诺 1137 | 沙 1138 | 触 1139 | 饭 1140 | 妹 1141 | 载 1142 | 俄 1143 | 揭 1144 | 刷 1145 | 隐 1146 | 封 1147 | 拆 1148 | 郭 1149 | 草 1150 | 妇 1151 | 赶 1152 | 册 1153 | 督 1154 | 芯 1155 | 孕 1156 | 既 1157 | 袭 1158 | 档 1159 | 舞 1160 | 贸 1161 | 慧 1162 | 陷 1163 | 爸 1164 | 餐 1165 | 木 1166 | 宏 1167 | 闭 1168 | 奶 1169 | 谁 1170 | 丁 1171 | 荣 1172 | 铜 1173 | 盈 1174 | 扬 1175 | 典 1176 | 桩 1177 | 拒 1178 | 毕 1179 | 宫 1180 | 捐 1181 | 返 1182 | 浪 1183 | 扶 1184 | 撑 1185 | 摘 1186 | 焦 1187 | 莞 1188 | 旗 1189 | 燃 1190 | 弃 1191 | 偷 1192 | 援 1193 | 途 1194 | 含 1195 | 坛 1196 | 末 1197 | 睡 1198 | 骨 1199 | 叫 1200 | 厅 1201 | 雷 1202 | 抱 1203 | 努 1204 | 吕 1205 | 镜 1206 | 玲 1207 | 玉 1208 | 郎 1209 | 弹 1210 | 抵 1211 | 床 1212 | 迹 1213 | 掌 1214 | 散 1215 | 坏 1216 | 煤 1217 | 盟 1218 | 冷 1219 | 胡 1220 | 虹 1221 | 瑞 1222 | 振 1223 | 祖 1224 | 恩 1225 | 潜 1226 | 菜 1227 | 仪 1228 | 牙 1229 | 伏 1230 | 牛 1231 | 痛 1232 | 雅 1233 | 固 1234 | 爷 1235 | 呢 1236 | 芝 1237 | 赵 1238 | 鲁 1239 | 梁 1240 | 淡 1241 | 狂 1242 | 握 1243 | 静 1244 | 纯 1245 | 勇 1246 | 沟 1247 | 纠 1248 | 赢 1249 | 杂 1250 | 砸 1251 | 婆 1252 | 藏 1253 | 慎 1254 | 搭 1255 | 塞 1256 | 蔡 1257 | 献 1258 | 齐 1259 | 奋 1260 | 章 1261 | 恶 1262 | 树 1263 | 晋 1264 | 秋 1265 | 冒 1266 | 剩 1267 | 祝 1268 | 课 1269 | 烟 1270 | 宜 1271 | 拼 1272 | 喝 1273 | 泰 1274 | 偏 1275 | 钢 1276 | 狗 1277 | 鲜 1278 | 寿 1279 | 隔 1280 | 浙 1281 | 坦 1282 | 籍 1283 | 鸟 1284 | 敏 1285 | 弱 1286 | 替 1287 | 趣 1288 | 剂 1289 | 叶 1290 | 杭 1291 | 答 1292 | 恢 1293 | 混 1294 | 耗 1295 | 炸 1296 | 乱 1297 | 杆 1298 | 族 1299 | 堂 1300 | 窃 1301 | 腿 1302 | 赚 1303 | 骑 1304 | 鱼 1305 | 疾 1306 | 诚 1307 | 恋 1308 | 缴 1309 | 棒 1310 | 岛 1311 | 恐 1312 | 捕 1313 | 巢 1314 | 侧 1315 | 迫 1316 | 萌 1317 | 船 1318 | 欲 1319 | 徒 1320 | 奔 1321 | 烧 1322 | 瓦 1323 | 尝 1324 | 递 1325 | 厚 1326 | 灵 1327 | 辉 1328 | 佛 1329 | 醒 1330 | 吧 1331 | 薪 1332 | 拓 1333 | 摆 1334 | 乏 1335 | 残 1336 | 莫 1337 | 矿 1338 | 琪 1339 | 迁 1340 | 阻 1341 | 雄 1342 | 扮 1343 | 碍 1344 | 毫 1345 | 曲 1346 | 梅 1347 | 辑 1348 | 衡 1349 | 射 1350 | 姓 1351 | 吨 1352 | 唯 1353 | 乒 1354 | 炳 1355 | 踪 1356 | 汪 1357 | 逆 1358 | 症 1359 | 繁 1360 | 滴 1361 | 翔 1362 | 卷 1363 | 卓 1364 | 谓 1365 | 迟 1366 | 拖 1367 | 灾 1368 | 碳 1369 | 扎 1370 | 亦 1371 | 沈 1372 | 陪 1373 | 娘 1374 | 洛 1375 | 忧 1376 | 唐 1377 | 轨 1378 | 尾 1379 | 赴 1380 | 誉 1381 | 窗 1382 | 诊 1383 | 怕 1384 | 凭 1385 | 党 1386 | 佐 1387 | 劵 1388 | 玮 1389 | 洁 1390 | 奏 1391 | 垄 1392 | 署 1393 | 恒 1394 | 泛 1395 | 斤 1396 | 赌 1397 | 菲 1398 | 莱 1399 | 撤 1400 | 缘 1401 | 伍 1402 | 旦 1403 | 蒙 1404 | 闹 1405 | 幼 1406 | 敢 1407 | 庄 1408 | 沿 1409 | 搏 1410 | 旺 1411 | 豆 1412 | 惯 1413 | 晶 1414 | 忙 1415 | 溢 1416 | 槛 1417 | 旁 1418 | 绑 1419 | 诸 1420 | 辞 1421 | 液 1422 | 纸 1423 | 氧 1424 | 鉴 1425 | 坡 1426 | 寸 1427 | 箱 1428 | 劲 1429 | 欠 1430 | 哲 1431 | 淘 1432 | 逼 1433 | 墨 1434 | 楚 1435 | 昌 1436 | 蛋 1437 | 猛 1438 | 岗 1439 | 岸 1440 | 唱 1441 | 坠 1442 | 劫 1443 | 饮 1444 | 咨 1445 | 摔 1446 | 搞 1447 | 伯 1448 | 墅 1449 | 墙 1450 | 殊 1451 | 宾 1452 | 洪 1453 | 井 1454 | 仓 1455 | 堵 1456 | 伊 1457 | 兼 1458 | 霸 1459 | 赖 1460 | 琳 1461 | 殴 1462 | 诗 1463 | 砍 1464 | 轿 1465 | 慈 1466 | 燕 1467 | 携 1468 | 允 1469 | 庞 1470 | 铺 1471 | 彻 1472 | 邀 1473 | 沉 1474 | 涵 1475 | 堪 1476 | 抛 1477 | 姆 1478 | 喊 1479 | 牵 1480 | 柯 1481 | 耳 1482 | 倾 1483 | 浦 1484 | 绕 1485 | 挖 1486 | 圾 1487 | 鸡 1488 | 贩 1489 | 垃 1490 | 腐 1491 | 漏 1492 | 阅 1493 | 览 1494 | 滨 1495 | 挺 1496 | 抽 1497 | 拨 1498 | 癌 1499 | 窄 1500 | 逊 1501 | 偶 1502 | 婷 1503 | 词 1504 | 横 1505 | 颁 1506 | 贡 1507 | 抄 1508 | 羊 1509 | 聘 1510 | 扰 1511 | 吐 1512 | 冕 1513 | 疯 1514 | 虎 1515 | 沪 1516 | 腾 1517 | 糖 1518 | 娃 1519 | 栏 1520 | 炼 1521 | 淀 1522 | 贿 1523 | 婴 1524 | 怪 1525 | 澳 1526 | 泄 1527 | 捷 1528 | 灯 1529 | 植 1530 | 篮 1531 | 欣 1532 | 忍 1533 | 帅 1534 | 忽 1535 | 朗 1536 | 冻 1537 | 圆 1538 | 废 1539 | 凯 1540 | 押 1541 | 壁 1542 | 霞 1543 | 尸 1544 | 辛 1545 | 祥 1546 | 泡 1547 | 迈 1548 | 械 1549 | 淫 1550 | 奸 1551 | 佩 1552 | 拔 1553 | 攀 1554 | 卧 1555 | 毁 1556 | 瓶 1557 | 句 1558 | 裂 1559 | 杜 1560 | 踏 1561 | 默 1562 | 丢 1563 | 绪 1564 | 扫 1565 | 麻 1566 | 莉 1567 | 舍 1568 | 驰 1569 | 颜 1570 | 魔 1571 | 搬 1572 | 鬼 1573 | 乔 1574 | 邮 1575 | 疆 1576 | 乳 1577 | 鹏 1578 | 塌 1579 | 君 1580 | 艰 1581 | 暗 1582 | 宿 1583 | 贝 1584 | 姿 1585 | 愈 1586 | 脏 1587 | 兄 1588 | 塔 1589 | 眠 1590 | 寓 1591 | 贯 1592 | 聊 1593 | 碰 1594 | 侦 1595 | 酷 1596 | 敦 1597 | 宋 1598 | 仿 1599 | 廉 1600 | 俩 1601 | 劝 1602 | 羽 1603 | 颖 1604 | 忘 1605 | 巩 1606 | 契 1607 | 著 1608 | 敌 1609 | 舒 1610 | 循 1611 | 肖 1612 | 饼 1613 | 荐 1614 | 挤 1615 | 曼 1616 | 熊 1617 | 悬 1618 | 兑 1619 | 厦 1620 | 谨 1621 | 详 1622 | 惨 1623 | 洞 1624 | 湃 1625 | 敬 1626 | 恰 1627 | 番 1628 | 涯 1629 | 巡 1630 | 纵 1631 | 隆 1632 | 澎 1633 | 炉 1634 | 肩 1635 | 酸 1636 | 锋 1637 | 浮 1638 | 摸 1639 | 伐 1640 | 涌 1641 | 糕 1642 | 薄 1643 | 悄 1644 | 麦 1645 | 菌 1646 | 茶 1647 | 哭 1648 | 乌 1649 | 谋 1650 | 稿 1651 | 漫 1652 | 逸 1653 | 樊 1654 | 摇 1655 | 娜 1656 | 猫 1657 | 驱 1658 | 酬 1659 | 姑 1660 | 烯 1661 | 粮 1662 | 凶 1663 | 巧 1664 | 吓 1665 | 纹 1666 | 摊 1667 | 仁 1668 | 伪 1669 | 邻 1670 | 冀 1671 | 讼 1672 | 灭 1673 | 稀 1674 | 吊 1675 | 尖 1676 | 凡 1677 | 驻 1678 | 胞 1679 | 屡 1680 | 厉 1681 | 傅 1682 | 冯 1683 | 桌 1684 | 俊 1685 | 仑 1686 | 汰 1687 | 诞 1688 | 姻 1689 | 惜 1690 | 闪 1691 | 郁 1692 | 尊 1693 | 怒 1694 | 鸿 1695 | 泉 1696 | 帕 1697 | 胖 1698 | 俱 1699 | 歉 1700 | 肇 1701 | 遥 1702 | 闯 1703 | 咬 1704 | 斌 1705 | 蕾 1706 | 甲 1707 | 磨 1708 | 抚 1709 | 邦 1710 | 勒 1711 | 尺 1712 | 赫 1713 | 颇 1714 | 埃 1715 | 鑫 1716 | 骂 1717 | 卢 1718 | 妙 1719 | 饰 1720 | 稍 1721 | 晰 1722 | 锻 1723 | 肌 1724 | 熙 1725 | 赠 1726 | 闲 1727 | 郊 1728 | 艾 1729 | 扭 1730 | 圣 1731 | 腰 1732 | 浓 1733 | 柳 1734 | 黎 1735 | 伸 1736 | 泥 1737 | 尿 1738 | 暑 1739 | 掀 1740 | 匹 1741 | 悲 1742 | 甜 1743 | 猪 1744 | 啦 1745 | 敲 1746 | 忆 1747 | 颗 1748 | 抑 1749 | 牢 1750 | 堆 1751 | 乓 1752 | 姜 1753 | 汤 1754 | 芬 1755 | 轴 1756 | 臂 1757 | 甘 1758 | 畅 1759 | 腹 1760 | 崇 1761 | 袁 1762 | 溺 1763 | 紫 1764 | 辅 1765 | 盾 1766 | 厕 1767 | 浩 1768 | 胶 1769 | 蒋 1770 | 旋 1771 | 嘴 1772 | 邱 1773 | 刊 1774 | 矛 1775 | 渡 1776 | 稽 1777 | 珍 1778 | 塑 1779 | 践 1780 | 玻 1781 | 幻 1782 | 履 1783 | 邓 1784 | 碎 1785 | 盐 1786 | 宴 1787 | 割 1788 | 赏 1789 | 姚 1790 | 拦 1791 | 毅 1792 | 泪 1793 | 吁 1794 | 帝 1795 | 欺 1796 | 壮 1797 | 帖 1798 | 苗 1799 | 旭 1800 | 柜 1801 | 槽 1802 | 锂 1803 | 萨 1804 | 悦 1805 | 拐 1806 | 惩 1807 | 遵 1808 | 贾 1809 | 鞋 1810 | 桂 1811 | 璃 1812 | 仔 1813 | 祸 1814 | 篇 1815 | 挡 1816 | 曹 1817 | 锡 1818 | 憾 1819 | 裤 1820 | 诱 1821 | 肃 1822 | 陶 1823 | 硕 1824 | 赁 1825 | 衔 1826 | 裕 1827 | 杠 1828 | 厘 1829 | 氮 1830 | 潘 1831 | 夹 1832 | 潭 1833 | 艳 1834 | 疼 1835 | 揽 1836 | 猜 1837 | 馈 1838 | 逾 1839 | 疏 1840 | 秩 1841 | 劣 1842 | 凤 1843 | 烦 1844 | 漂 1845 | 忠 1846 | 屈 1847 | 廷 1848 | 谣 1849 | 弯 1850 | 虾 1851 | 绮 1852 | 倡 1853 | 舆 1854 | 剑 1855 | 辽 1856 | 蝶 1857 | 兆 1858 | 棋 1859 | 栋 1860 | 雇 1861 | 魅 1862 | 擦 1863 | 疲 1864 | 删 1865 | 魏 1866 | 妥 1867 | 催 1868 | 垫 1869 | 啊 1870 | 氏 1871 | 皇 1872 | 彭 1873 | 弗 1874 | 擅 1875 | 氛 1876 | 沃 1877 | 膜 1878 | 钻 1879 | 懂 1880 | 瑄 1881 | 裸 1882 | 貌 1883 | 勤 1884 | 帽 1885 | 碑 1886 | 颠 1887 | 澄 1888 | 址 1889 | 奎 1890 | 蜜 1891 | 孤 1892 | 汗 1893 | 勾 1894 | 锅 1895 | 闫 1896 | 捧 1897 | 颈 1898 | 笼 1899 | 胁 1900 | 框 1901 | 虫 1902 | 荷 1903 | 葛 1904 | 寺 1905 | 捞 1906 | 厨 1907 | 爬 1908 | 辩 1909 | 贪 1910 | 丑 1911 | 削 1912 | 湘 1913 | 鼻 1914 | 乃 1915 | 柏 1916 | 拜 1917 | 描 1918 | 狱 1919 | 吵 1920 | 佣 1921 | 您 1922 | 飙 1923 | 吹 1924 | 轰 1925 | 宠 1926 | 岳 1927 | 怡 1928 | 咖 1929 | 贼 1930 | 磊 1931 | 耐 1932 | 灰 1933 | 贤 1934 | 逝 1935 | 侠 1936 | 肠 1937 | 抬 1938 | 昂 1939 | 俗 1940 | 姣 1941 | 辖 1942 | 瘦 1943 | 奈 1944 | 填 1945 | 昕 1946 | 虐 1947 | 萧 1948 | 蓄 1949 | 猥 1950 | 醉 1951 | 陕 1952 | 贫 1953 | 坑 1954 | 娇 1955 | 饱 1956 | 捡 1957 | 轩 1958 | 峻 1959 | 盲 1960 | 帆 1961 | 瓜 1962 | 雯 1963 | 滞 1964 | 串 1965 | 狼 1966 | 役 1967 | 沫 1968 | 尴 1969 | 戒 1970 | 鼠 1971 | 屯 1972 | 尬 1973 | 瑜 1974 | 睛 1975 | 茂 1976 | 彦 1977 | 喷 1978 | 掘 1979 | 弥 1980 | 棚 1981 | 亨 1982 | 媳 1983 | 插 1984 | 琴 1985 | 凉 1986 | 灿 1987 | 罕 1988 | 纽 1989 | 彼 1990 | 凸 1991 | 肚 1992 | 逻 1993 | 侯 1994 | 峡 1995 | 孔 1996 | 炮 1997 | 荡 1998 | 跪 1999 | 壳 2000 | 荒 2001 | 馨 2002 | 叹 2003 | 踢 2004 | 鸣 2005 | 钧 2006 | 谭 2007 | 酿 2008 | 瞬 2009 | 孟 2010 | 砖 2011 | 旨 2012 | 玛 2013 | 棍 2014 | 盆 2015 | 茨 2016 | 胀 2017 | 蔚 2018 | 躲 2019 | 蒂 2020 | 滚 2021 | 柱 2022 | 涂 2023 | 肢 2024 | 胸 2025 | 崛 2026 | 肤 2027 | 尘 2028 | 衷 2029 | 辰 2030 | 阔 2031 | 鼎 2032 | 剪 2033 | 斜 2034 | 滋 2035 | 页 2036 | 斩 2037 | 柔 2038 | 璇 2039 | 僵 2040 | 萄 2041 | 伞 2042 | 扇 2043 | 丙 2044 | 昏 2045 | 阴 2046 | 烂 2047 | 谐 2048 | 葡 2049 | 挣 2050 | 桶 2051 | 贺 2052 | 寄 2053 | 秦 2054 | 妆 2055 | 碗 2056 | 吞 2057 | 萎 2058 | 桑 2059 | 媛 2060 | 喂 2061 | 驳 2062 | 捉 2063 | 弄 2064 | 谎 2065 | 楠 2066 | 晕 2067 | 怨 2068 | 逢 2069 | 煌 2070 | 赋 2071 | 雾 2072 | 吻 2073 | 戈 2074 | 窝 2075 | 溪 2076 | 愤 2077 | 逮 2078 | 聪 2079 | 衰 2080 | 夕 2081 | 埋 2082 | 痕 2083 | 睐 2084 | 芳 2085 | 滩 2086 | 咚 2087 | 粗 2088 | 孵 2089 | 芸 2090 | 渣 2091 | 嫁 2092 | 肿 2093 | 硅 2094 | 厢 2095 | 寒 2096 | 脂 2097 | 辈 2098 | 钓 2099 | 肝 2100 | 趁 2101 | 桃 2102 | 卿 2103 | 椅 2104 | 耍 2105 | 硫 2106 | 喀 2107 | 亵 2108 | 躺 2109 | 炎 2110 | 翼 2111 | 杉 2112 | 韦 2113 | 竹 2114 | 译 2115 | 渝 2116 | 袋 2117 | 侣 2118 | 郝 2119 | 扑 2120 | 呆 2121 | 绘 2122 | 侃 2123 | 寨 2124 | 琐 2125 | 钩 2126 | 膝 2127 | 瞩 2128 | 殖 2129 | 墓 2130 | 盒 2131 | 炭 2132 | 慌 2133 | 璨 2134 | 缝 2135 | 踩 2136 | 陵 2137 | 囊 2138 | 扔 2139 | 塘 2140 | 谌 2141 | 辟 2142 | 狠 2143 | 趴 2144 | 梳 2145 | 啡 2146 | 粒 2147 | 匪 2148 | 盼 2149 | 夸 2150 | 缠 2151 | 烫 2152 | 歧 2153 | 铅 2154 | 靖 2155 | 叠 2156 | 挽 2157 | 腕 2158 | 罩 2159 | 闸 2160 | 垒 2161 | 弊 2162 | 斥 2163 | 刹 2164 | 锐 2165 | 勘 2166 | 坊 2167 | 慰 2168 | 衍 2169 | 掩 2170 | 翁 2171 | 皆 2172 | 撒 2173 | 拾 2174 | 耀 2175 | 臭 2176 | 顽 2177 | 兹 2178 | 辱 2179 | 仰 2180 | 掷 2181 | 舱 2182 | 蛇 2183 | 脉 2184 | 贬 2185 | 蟹 2186 | 疫 2187 | 穷 2188 | 彰 2189 | 猴 2190 | 裹 2191 | 钥 2192 | 胃 2193 | 宪 2194 | 晖 2195 | 蹈 2196 | 炫 2197 | 犬 2198 | 湿 2199 | 啥 2200 | 掏 2201 | 凝 2202 | 畴 2203 | 挪 2204 | 妮 2205 | 咏 2206 | 坎 2207 | 睹 2208 | 霍 2209 | 倪 2210 | 惑 2211 | 扳 2212 | 酝 2213 | 渴 2214 | 蜂 2215 | 捅 2216 | 碾 2217 | 蚁 2218 | 冤 2219 | 擂 2220 | 奢 2221 | 浅 2222 | 靓 2223 | 帷 2224 | 匿 2225 | 娟 2226 | 惹 2227 | 辨 2228 | 瘫 2229 | 铬 2230 | 刮 2231 | 纱 2232 | 辐 2233 | 狮 2234 | 牲 2235 | 陌 2236 | 撕 2237 | 咸 2238 | 剥 2239 | 瓷 2240 | 脆 2241 | 胆 2242 | 妍 2243 | 皓 2244 | 舰 2245 | 瞄 2246 | 盯 2247 | 遂 2248 | 匙 2249 | 锈 2250 | 傲 2251 | 坝 2252 | 耕 2253 | 悔 2254 | 濠 2255 | 膀 2256 | 柄 2257 | 禽 2258 | 禾 2259 | 牺 2260 | 翘 2261 | 卸 2262 | 晴 2263 | 纤 2264 | 朵 2265 | 堡 2266 | 铭 2267 | 渗 2268 | 喻 2269 | 叔 2270 | 羞 2271 | 逛 2272 | 猝 2273 | 沸 2274 | 浸 2275 | 氢 2276 | 嫖 2277 | 慨 2278 | 僧 2279 | 岩 2280 | 辣 2281 | 怖 2282 | 浏 2283 | 犹 2284 | 淇 2285 | 奠 2286 | 函 2287 | 咕 2288 | 亭 2289 | 御 2290 | 羡 2291 | 糟 2292 | 亩 2293 | 垂 2294 | 肺 2295 | 滥 2296 | 慕 2297 | 淑 2298 | 眉 2299 | 瑟 2300 | 斑 2301 | 魄 2302 | 扯 2303 | 骚 2304 | 跻 2305 | 崔 2306 | 纺 2307 | 撰 2308 | 裙 2309 | 鲍 2310 | 擎 2311 | 洽 2312 | 恨 2313 | 肾 2314 | 谦 2315 | 岭 2316 | 贞 2317 | 乞 2318 | 甩 2319 | 莲 2320 | 袖 2321 | 瘾 2322 | 筋 2323 | 仗 2324 | 骤 2325 | 琦 2326 | 邹 2327 | 悍 2328 | 糊 2329 | 穆 2330 | 盔 2331 | 牧 2332 | 纲 2333 | 勃 2334 | 莎 2335 | 昔 2336 | 箭 2337 | 嫂 2338 | 楷 2339 | 坞 2340 | 叉 2341 | 莓 2342 | 祈 2343 | 霏 2344 | 簿 2345 | 锤 2346 | 妨 2347 | 迄 2348 | 磷 2349 | 娶 2350 | 瞒 2351 | 汁 2352 | 孝 2353 | 宰 2354 | 丧 2355 | 沦 2356 | 萍 2357 | 咋 2358 | 蔓 2359 | 巅 2360 | 霾 2361 | 苛 2362 | 唇 2363 | 脖 2364 | 崩 2365 | 砂 2366 | 蛙 2367 | 啤 2368 | 浆 2369 | 谱 2370 | 艘 2371 | 烤 2372 | 蔬 2373 | 鹤 2374 | 雕 2375 | 爽 2376 | 兽 2377 | 豫 2378 | 绵 2379 | 奉 2380 | 泼 2381 | 抹 2382 | 俏 2383 | 惧 2384 | 恼 2385 | 兜 2386 | 赤 2387 | 撼 2388 | 廊 2389 | 铸 2390 | 甸 2391 | 挫 2392 | 坍 2393 | 姨 2394 | 闷 2395 | 痴 2396 | 饿 2397 | 艇 2398 | 淮 2399 | 朴 2400 | 柴 2401 | 仙 2402 | 剔 2403 | 荧 2404 | 捣 2405 | 钦 2406 | 倩 2407 | 兔 2408 | 肋 2409 | 捍 2410 | 萤 2411 | 绳 2412 | 剖 2413 | 秉 2414 | 哀 2415 | 侈 2416 | 遮 2417 | 霖 2418 | 碟 2419 | 佑 2420 | 韵 2421 | 钉 2422 | 鳄 2423 | 瘤 2424 | 渊 2425 | 渔 2426 | 缔 2427 | 灌 2428 | 罢 2429 | 茅 2430 | 帐 2431 | 浇 2432 | 葩 2433 | 赂 2434 | 竣 2435 | 壤 2436 | 躁 2437 | 嘲 2438 | 凑 2439 | 弈 2440 | 鸭 2441 | 芽 2442 | 钞 2443 | 薇 2444 | 苑 2445 | 漠 2446 | 磅 2447 | 铲 2448 | 廖 2449 | 溜 2450 | 戚 2451 | 竖 2452 | 遏 2453 | 衫 2454 | 彤 2455 | 幺 2456 | 屠 2457 | 旱 2458 | 忌 2459 | 耶 2460 | 钮 2461 | 侨 2462 | 筛 2463 | 磁 2464 | 魂 2465 | 寂 2466 | 焰 2467 | 酵 2468 | 庙 2469 | 卵 2470 | 婿 2471 | 澡 2472 | 嘛 2473 | 邵 2474 | 捂 2475 | 筒 2476 | 秽 2477 | 蝙 2478 | 蹲 2479 | 爹 2480 | 蝠 2481 | 蕴 2482 | 溯 2483 | 尹 2484 | 余 2485 | 焚 2486 | 珊 2487 | 蹭 2488 | 巷 2489 | 蒸 2490 | 龟 2491 | 缉 2492 | 逗 2493 | 妖 2494 | 硝 2495 | 呀 2496 | 歪 2497 | 浴 2498 | 靡 2499 | 翰 2500 | 噪 2501 | 鹅 2502 | 挨 2503 | 愧 2504 | 攒 2505 | 捆 2506 | 谍 2507 | 潼 2508 | 巾 2509 | 燥 2510 | 峥 2511 | 脊 2512 | 蛛 2513 | 悠 2514 | 螺 2515 | 臣 2516 | 熬 2517 | 泣 2518 | 讶 2519 | 悼 2520 | 崖 2521 | 孚 2522 | 牟 2523 | 阱 2524 | 锰 2525 | 擒 2526 | 詹 2527 | 枯 2528 | 嚣 2529 | 稻 2530 | 傍 2531 | 蜘 2532 | 洒 2533 | 墩 2534 | 仲 2535 | 讳 2536 | 傻 2537 | 蕉 2538 | 昊 2539 | 蚊 2540 | 迭 2541 | 碧 2542 | 嵌 2543 | 衅 2544 | 舌 2545 | 佬 2546 | 窜 2547 | 赎 2548 | 唤 2549 | 莹 2550 | 泊 2551 | 罄 2552 | 匆 2553 | 诠 2554 | 饥 2555 | 娼 2556 | 堰 2557 | 畸 2558 | 螃 2559 | 邢 2560 | 棉 2561 | 猎 2562 | 呛 2563 | 坪 2564 | 蓉 2565 | 肆 2566 | 碌 2567 | 氯 2568 | 愉 2569 | 罐 2570 | 骏 2571 | 撇 2572 | 惟 2573 | 铃 2574 | 饶 2575 | 甄 2576 | 徊 2577 | 茫 2578 | 扒 2579 | 竭 2580 | 闵 2581 | 徘 2582 | 丸 2583 | 膨 2584 | 铝 2585 | 榆 2586 | 蛮 2587 | 煎 2588 | 遣 2589 | 嫩 2590 | 僻 2591 | 菇 2592 | 俯 2593 | 衬 2594 | 菱 2595 | 骄 2596 | 咪 2597 | 舟 2598 | 茵 2599 | 薛 2600 | 祭 2601 | 瞻 2602 | 隙 2603 | 宛 2604 | 榨 2605 | 炯 2606 | 哗 2607 | 渭 2608 | 雁 2609 | 婉 2610 | 辄 2611 | 龚 2612 | 玥 2613 | 臻 2614 | 凰 2615 | 掐 2616 | 儒 2617 | 飘 2618 | 淋 2619 | 荆 2620 | 屁 2621 | 滢 2622 | 蔽 2623 | 蘑 2624 | 肪 2625 | 愁 2626 | 蝉 2627 | 麟 2628 | 歇 2629 | 媚 2630 | 趟 2631 | 畏 2632 | 仇 2633 | 汛 2634 | 舅 2635 | 澜 2636 | 丛 2637 | 韶 2638 | 鞠 2639 | 芮 2640 | 溅 2641 | 氨 2642 | 灼 2643 | 缸 2644 | 谅 2645 | 饽 2646 | 呕 2647 | 枝 2648 | 勿 2649 | 哑 2650 | 呦 2651 | 堤 2652 | 焕 2653 | 蒲 2654 | 桐 2655 | 俞 2656 | 狭 2657 | 藤 2658 | 宵 2659 | 耻 2660 | 驴 2661 | 誓 2662 | 茹 2663 | 膏 2664 | 沽 2665 | 恺 2666 | 阁 2667 | 坟 2668 | 姗 2669 | 涤 2670 | 芦 2671 | 淤 2672 | 椎 2673 | 瑰 2674 | 痪 2675 | 鹿 2676 | 殿 2677 | 禅 2678 | 溶 2679 | 庸 2680 | 坤 2681 | 曙 2682 | 臀 2683 | 抖 2684 | 弧 2685 | 淄 2686 | 阚 2687 | 湛 2688 | 瑕 2689 | 弓 2690 | 赃 2691 | 矶 2692 | 埔 2693 | 溃 2694 | 丘 2695 | 寡 2696 | 乙 2697 | 阐 2698 | 腊 2699 | 哦 2700 | 咽 2701 | 仕 2702 | 鄂 2703 | 挠 2704 | 阮 2705 | 煮 2706 | 梨 2707 | 锯 2708 | 刃 2709 | 睁 2710 | 瀚 2711 | 缅 2712 | 焱 2713 | 慑 2714 | 岔 2715 | 豹 2716 | 讹 2717 | 崭 2718 | 厌 2719 | 齿 2720 | 渎 2721 | 闺 2722 | 栈 2723 | 潇 2724 | 髓 2725 | 葬 2726 | 奕 2727 | 绎 2728 | 拇 2729 | 敞 2730 | 钾 2731 | 睿 2732 | 羹 2733 | 扛 2734 | 碘 2735 | 篡 2736 | 蹬 2737 | 畜 2738 | 挟 2739 | 敛 2740 | 岌 2741 | 猩 2742 | 馒 2743 | 咒 2744 | 骆 2745 | 阙 2746 | 绞 2747 | 俨 2748 | 枢 2749 | 酱 2750 | 叙 2751 | 绰 2752 | 株 2753 | 芒 2754 | 翠 2755 | 冉 2756 | 钰 2757 | 揪 2758 | 焊 2759 | 耿 2760 | 歹 2761 | 雏 2762 | 冶 2763 | 芭 2764 | 锣 2765 | 弘 2766 | 搅 2767 | 恭 2768 | 颓 2769 | 竿 2770 | 诟 2771 | 幽 2772 | 蚀 2773 | 庐 2774 | 撬 2775 | 蓬 2776 | 榴 2777 | 隶 2778 | 崎 2779 | 毯 2780 | 蝴 2781 | 搂 2782 | 梗 2783 | 畔 2784 | 夷 2785 | 呐 2786 | 懈 2787 | 橘 2788 | 翡 2789 | 霉 2790 | 哒 2791 | 橙 2792 | 讽 2793 | 隅 2794 | 沾 2795 | 拢 2796 | 窑 2797 | 哄 2798 | 泻 2799 | 浑 2800 | 泸 2801 | 榕 2802 | 沧 2803 | 陀 2804 | 熄 2805 | 絮 2806 | 淹 2807 | 脐 2808 | 拽 2809 | 汹 2810 | 疵 2811 | 魁 2812 | 钠 2813 | 淳 2814 | 昧 2815 | 瑛 2816 | 霜 2817 | 幂 2818 | 彬 2819 | 凳 2820 | 娅 2821 | 霆 2822 | 矩 2823 | 劈 2824 | 颅 2825 | 惕 2826 | 腔 2827 | 滤 2828 | 婧 2829 | 醋 2830 | 袱 2831 | 棕 2832 | 聂 2833 | 锏 2834 | 疤 2835 | 粪 2836 | 鞍 2837 | 沛 2838 | 氰 2839 | 螂 2840 | 聋 2841 | 勺 2842 | 漆 2843 | 裴 2844 | 磕 2845 | 汝 2846 | 茜 2847 | 禹 2848 | 篷 2849 | 屹 2850 | 剽 2851 | 剿 2852 | 幢 2853 | 韧 2854 | 烨 2855 | 鳌 2856 | 邪 2857 | 涩 2858 | 芷 2859 | 翅 2860 | 嘘 2861 | 饲 2862 | 倘 2863 | 彝 2864 | 玄 2865 | 蜡 2866 | 矫 2867 | 拎 2868 | 蜕 2869 | 诡 2870 | 炜 2871 | 窖 2872 | 辜 2873 | 耽 2874 | 呵 2875 | 锆 2876 | 乖 2877 | 脾 2878 | 冈 2879 | 斐 2880 | 蟑 2881 | 摧 2882 | 咱 2883 | 驼 2884 | 蹊 2885 | 佟 2886 | 勋 2887 | 彪 2888 | 渤 2889 | 踞 2890 | 肘 2891 | 贱 2892 | 玺 2893 | 茄 2894 | 醛 2895 | 昭 2896 | 驿 2897 | 杖 2898 | 熏 2899 | 枫 2900 | 晟 2901 | 倦 2902 | 瑾 2903 | 瀑 2904 | 诬 2905 | 笋 2906 | 诀 2907 | 葵 2908 | 祷 2909 | 橄 2910 | 铀 2911 | 幌 2912 | 恳 2913 | 辗 2914 | 眨 2915 | 萱 2916 | 悟 2917 | 琼 2918 | 菡 2919 | 抉 2920 | 榄 2921 | 亥 2922 | 毽 2923 | 杏 2924 | 椒 2925 | 漩 2926 | 玫 2927 | 舶 2928 | 娴 2929 | 坂 2930 | 伽 2931 | 甫 2932 | 黛 2933 | 拣 2934 | 恤 2935 | 剃 2936 | 揣 2937 | 旷 2938 | 敷 2939 | 绯 2940 | 谴 2941 | 祁 2942 | 穗 2943 | 遛 2944 | 朔 2945 | 殷 2946 | 峙 2947 | 柿 2948 | 懒 2949 | 醇 2950 | 袍 2951 | 颂 2952 | 钊 2953 | 涡 2954 | 痱 2955 | 跷 2956 | 窘 2957 | 勉 2958 | 曦 2959 | 缪 2960 | 惫 2961 | 涞 2962 | 蚂 2963 | 晃 2964 | 梭 2965 | 阎 2966 | 拱 2967 | 矢 2968 | 葫 2969 | 砾 2970 | 烹 2971 | 坷 2972 | 谜 2973 | 忱 2974 | 芙 2975 | 琢 2976 | 笨 2977 | 堕 2978 | 芜 2979 | 胰 2980 | 粹 2981 | 壶 2982 | 怜 2983 | 侮 2984 | 绸 2985 | 匝 2986 | 囚 2987 | 洼 2988 | 袂 2989 | 烽 2990 | 躬 2991 | 汀 2992 | 绷 2993 | 匕 2994 | 窥 2995 | 毙 2996 | 戳 2997 | 痒 2998 | 萝 2999 | 晤 3000 | 霄 3001 | 坯 3002 | 泾 3003 | 窒 3004 | 黯 3005 | 垮 3006 | 剁 3007 | 羁 3008 | 栖 3009 | 炙 3010 | 哨 3011 | 珉 3012 | 捏 3013 | 俪 3014 | 腺 3015 | 厮 3016 | 雀 3017 | 嫉 3018 | 粘 3019 | 庚 3020 | 赣 3021 | 腻 3022 | 镶 3023 | 蠢 3024 | 暧 3025 | 纬 3026 | 谤 3027 | 荔 3028 | 泷 3029 | 钜 3030 | 卉 3031 | 岂 3032 | 囤 3033 | 匾 3034 | 鸽 3035 | 瞎 3036 | 妒 3037 | 诽 3038 | 谊 3039 | 庾 3040 | 荫 3041 | 耘 3042 | 丐 3043 | 淆 3044 | 滔 3045 | 雍 3046 | 搁 3047 | 桔 3048 | 涝 3049 | 踹 3050 | 饪 3051 | 璞 3052 | 蛟 3053 | 灶 3054 | 沮 3055 | 栗 3056 | 枞 3057 | 喔 3058 | 獗 3059 | 寥 3060 | 猖 3061 | 棵 3062 | 爪 3063 | 蜇 3064 | 娥 3065 | 掴 3066 | 秸 3067 | 蒜 3068 | 禺 3069 | 晏 3070 | 碱 3071 | 桦 3072 | 绊 3073 | 嗅 3074 | 豁 3075 | 猿 3076 | 耷 3077 | 喆 3078 | 慷 3079 | 梓 3080 | 噬 3081 | 诿 3082 | 嬛 3083 | 韬 3084 | 缆 3085 | 鹰 3086 | 裔 3087 | 吼 3088 | 俘 3089 | 喉 3090 | 伺 3091 | 莽 3092 | 穴 3093 | 荼 3094 | 驭 3095 | 毋 3096 | 橡 3097 | 辍 3098 | 侄 3099 | 巫 3100 | 藻 3101 | 筵 3102 | 畊 3103 | 鹭 3104 | 遴 3105 | 珮 3106 | 匈 3107 | 皱 3108 | 懿 3109 | 秆 3110 | 亟 3111 | 疹 3112 | 峨 3113 | 胫 3114 | 殒 3115 | 昙 3116 | 憬 3117 | 樱 3118 | 鸦 3119 | 宕 3120 | 耒 3121 | 绽 3122 | 眷 3123 | 陋 3124 | 蔗 3125 | 陡 3126 | 躯 3127 | 磋 3128 | 蹿 3129 | 犀 3130 | 窍 3131 | 吾 3132 | 糙 3133 | 蕊 3134 | 剐 3135 | 眈 3136 | 窨 3137 | 囧 3138 | 褒 3139 | 菊 3140 | 镑 3141 | 壹 3142 | 蝇 3143 | 麋 3144 | 褚 3145 | 巍 3146 | 垦 3147 | 蛰 3148 | 喧 3149 | 襄 3150 | 哽 3151 | 悚 3152 | 邯 3153 | 觑 3154 | 翟 3155 | 炖 3156 | 瓯 3157 | 枣 3158 | 掠 3159 | 珀 3160 | 酗 3161 | 帜 3162 | 鞭 3163 | 蹼 3164 | 璀 3165 | 羲 3166 | 寇 3167 | 璧 3168 | 殡 3169 | 皂 3170 | 煊 3171 | 婵 3172 | 膺 3173 | 沼 3174 | 寞 3175 | 癫 3176 | 叮 3177 | 饵 3178 | 戎 3179 | 奴 3180 | 铉 3181 | 缚 3182 | 咳 3183 | 镉 3184 | 棠 3185 | 卜 3186 | 苍 3187 | 郸 3188 | 蒿 3189 | 苯 3190 | 暨 3191 | 沥 3192 | 憧 3193 | 钵 3194 | 岚 3195 | 捻 3196 | 娄 3197 | 缭 3198 | 赡 3199 | 宙 3200 | 斧 3201 | 峭 3202 | 禄 3203 | 夯 3204 | 飚 3205 | 噱 3206 | 瞅 3207 | 馅 3208 | 蹴 3209 | 颐 3210 | 淞 3211 | 汞 3212 | 咎 3213 | 茗 3214 | 踝 3215 | 踵 3216 | 呜 3217 | 怂 3218 | 矣 3219 | 扁 3220 | 挚 3221 | 厥 3222 | 拯 3223 | 凹 3224 | 晗 3225 | 煦 3226 | 隋 3227 | 淝 3228 | 镍 3229 | 跤 3230 | 炬 3231 | 肛 3232 | 骷 3233 | 遐 3234 | 襁 3235 | 赘 3236 | 辙 3237 | 嘀 3238 | 莆 3239 | 倚 3240 | 顷 3241 | 豚 3242 | 杳 3243 | 暇 3244 | 祠 3245 | 粟 3246 | 嚼 3247 | 诫 3248 | 瞧 3249 | 舵 3250 | 廓 3251 | 竺 3252 | 觅 3253 | 祺 3254 | 膊 3255 | 丫 3256 | 骸 3257 | 殃 3258 | 辫 3259 | 昱 3260 | 甥 3261 | 藜 3262 | 仨 3263 | 怠 3264 | 揍 3265 | 拷 3266 | 陨 3267 | 渍 3268 | 梧 3269 | 攥 3270 | 泔 3271 | 崴 3272 | 涿 3273 | 刁 3274 | 漓 3275 | 嘟 3276 | 栽 3277 | 窟 3278 | 咫 3279 | 鲸 3280 | 栓 3281 | 嗯 3282 | 矗 3283 | 褓 3284 | 阀 3285 | 褪 3286 | 嫣 3287 | 蜀 3288 | 癖 3289 | 榔 3290 | 蔷 3291 | 惺 3292 | 枕 3293 | 晔 3294 | 礁 3295 | 奂 3296 | 逍 3297 | 槌 3298 | 釜 3299 | 靳 3300 | 煞 3301 | 籁 3302 | 徙 3303 | 妃 3304 | 髅 3305 | 嗒 3306 | 泌 3307 | 喇 3308 | 嘱 3309 | 痹 3310 | 熠 3311 | 厄 3312 | 羿 3313 | 棱 3314 | 萃 3315 | 酌 3316 | 歆 3317 | 甯 3318 | 砒 3319 | 泵 3320 | 漳 3321 | 牡 3322 | 峪 3323 | 搀 3324 | 煜 3325 | 诵 3326 | 噩 3327 | 墟 3328 | 蹦 3329 | 屑 3330 | 瘠 3331 | 弦 3332 | 踊 3333 | 胯 3334 | 簧 3335 | 虏 3336 | 拙 3337 | 汲 3338 | 拮 3339 | 籽 3340 | 孜 3341 | 蹄 3342 | 腆 3343 | 獒 3344 | 铨 3345 | 羚 3346 | 聆 3347 | 屎 3348 | 譬 3349 | 谙 3350 | 狸 3351 | 筷 3352 | 叼 3353 | 枉 3354 | 骋 3355 | 麾 3356 | 戛 3357 | 粤 3358 | 粥 3359 | 毓 3360 | 壕 3361 | 铐 3362 | 撂 3363 | 莘 3364 | 闽 3365 | 砺 3366 | 凿 3367 | 宸 3368 | 胺 3369 | 邬 3370 | 惋 3371 | 趾 3372 | 拌 3373 | 琨 3374 | 尧 3375 | 憋 3376 | 珲 3377 | 懋 3378 | 诧 3379 | 媲 3380 | 亢 3381 | 芋 3382 | 蔺 3383 | 咄 3384 | 抠 3385 | 憨 3386 | 矮 3387 | 筱 3388 | 匀 3389 | 薯 3390 | 叭 3391 | 琅 3392 | 袒 3393 | 笛 3394 | 闰 3395 | 恍 3396 | 汕 3397 | 熹 3398 | 濒 3399 | 麓 3400 | 缜 3401 | 滕 3402 | 赐 3403 | 唏 3404 | 轧 3405 | 骁 3406 | 翱 3407 | 嬉 3408 | 馊 3409 | 渚 3410 | 腥 3411 | 掣 3412 | 髦 3413 | 焯 3414 | 睫 3415 | 绚 3416 | 镳 3417 | 伎 3418 | 帘 3419 | 缀 3420 | 缮 3421 | 煲 3422 | 炅 3423 | 夭 3424 | 榷 3425 | 荟 3426 | 腼 3427 | 覆 3428 | 姬 3429 | 涅 3430 | 钛 3431 | 罂 3432 | 寅 3433 | 潢 3434 | 焉 3435 | 蔑 3436 | 稚 3437 | 汶 3438 | 岖 3439 | 涠 3440 | 痰 3441 | 崂 3442 | 伶 3443 | 奚 3444 | 卞 3445 | 愕 3446 | 蛐 3447 | 硚 3448 | 啧 3449 | 吝 3450 | 瞰 3451 | 犷 3452 | 瓣 3453 | 皋 3454 | 睬 3455 | 磐 3456 | 柚 3457 | 郡 3458 | 垤 3459 | 撮 3460 | 裆 3461 | 町 3462 | 菁 3463 | 翩 3464 | 饺 3465 | 霹 3466 | 滁 3467 | 灞 3468 | 缨 3469 | 噎 3470 | 缕 3471 | 窦 3472 | 嗤 3473 | 橱 3474 | 矸 3475 | 瘀 3476 | 嚷 3477 | 匮 3478 | 飓 3479 | 敖 3480 | 愚 3481 | 拄 3482 | 琬 3483 | 蕙 3484 | 卤 3485 | 瑶 3486 | 摁 3487 | 绒 3488 | 蝗 3489 | 柬 3490 | 惮 3491 | 昀 3492 | 殉 3493 | 垡 3494 | 苣 3495 | 忑 3496 | 锌 3497 | 庵 3498 | 槟 3499 | 匣 3500 | 忐 3501 | 畿 3502 | 箍 3503 | 圪 3504 | 鹜 3505 | 唾 3506 | 痣 3507 | 瞪 3508 | 檬 3509 | 腱 3510 | 黏 3511 | 馋 3512 | 邑 3513 | 膛 3514 | 嗓 3515 | 喘 3516 | 诅 3517 | 釉 3518 | 褂 3519 | 砌 3520 | 苟 3521 | 迂 3522 | 杞 3523 | 绣 3524 | 卦 3525 | 檀 3526 | 胳 3527 | 炽 3528 | 琛 3529 | 掰 3530 | 戟 3531 | 痫 3532 | 隧 3533 | 紊 3534 | 嗑 3535 | 璐 3536 | 膳 3537 | 葱 3538 | 谬 3539 | 姥 3540 | 懵 3541 | 跆 3542 | 颤 3543 | 懊 3544 | 骥 3545 | 禀 3546 | 哼 3547 | 栅 3548 | 熔 3549 | 悴 3550 | 沓 3551 | 寝 3552 | 呃 3553 | 贮 3554 | 讧 3555 | 鹊 3556 | 苇 3557 | 谩 3558 | 啃 3559 | 痘 3560 | 摹 3561 | 锄 3562 | 嘎 3563 | 禧 3564 | 抡 3565 | 瑚 3566 | 孰 3567 | 俐 3568 | 腌 3569 | 庇 3570 | 渀 3571 | 烃 3572 | 沐 3573 | 鄙 3574 | 讪 3575 | 掺 3576 | 狄 3577 | 斋 3578 | 搡 3579 | 塾 3580 | 缄 3581 | 诩 3582 | 睦 3583 | 棺 3584 | 袄 3585 | 涧 3586 | 茸 3587 | 鳝 3588 | 渺 3589 | 皖 3590 | 媞 3591 | 铂 3592 | 僚 3593 | 柠 3594 | 毗 3595 | 沣 3596 | 拧 3597 | 扼 3598 | 蚕 3599 | 溥 3600 | 匠 3601 | 哉 3602 | 婶 3603 | 靴 3604 | 寮 3605 | 滇 3606 | 诙 3607 | 糯 3608 | 叱 3609 | 莴 3610 | 雳 3611 | 钴 3612 | 鹃 3613 | 跺 3614 | 搪 3615 | 瞳 3616 | 叛 3617 | 嗨 3618 | 蜓 3619 | 妞 3620 | 垢 3621 | 迸 3622 | 拗 3623 | 鄞 3624 | 洱 3625 | 昼 3626 | 啼 3627 | 悖 3628 | 铆 3629 | 矜 3630 | 筏 3631 | 恿 3632 | 酪 3633 | 憔 3634 | 蜚 3635 | 黝 3636 | 蚯 3637 | 舛 3638 | 嚏 3639 | 掂 3640 | 罹 3641 | 鲨 3642 | 锹 3643 | 痊 3644 | 锵 3645 | 椰 3646 | 濮 3647 | 皙 3648 | 舀 3649 | 貅 3650 | 鄢 3651 | 琊 3652 | 浊 3653 | 兢 3654 | 汴 3655 | 殆 3656 | 伉 3657 | 袜 3658 | 咤 3659 | 酶 3660 | 屿 3661 | 垣 3662 | 秧 3663 | 躏 3664 | 氓 3665 | 盹 3666 | 嚎 3667 | 暮 3668 | 飒 3669 | 妩 3670 | 抒 3671 | 帼 3672 | 楞 3673 | 嗡 3674 | 灸 3675 | 袈 3676 | 樟 3677 | 綦 3678 | 貔 3679 | 晾 3680 | 窕 3681 | 琶 3682 | 臃 3683 | 婪 3684 | 榈 3685 | 箔 3686 | 辘 3687 | 蔫 3688 | 婕 3689 | 恙 3690 | 酣 3691 | 袆 3692 | 裟 3693 | 挎 3694 | 烘 3695 | 妄 3696 | 掮 3697 | 觊 3698 | 戮 3699 | 粽 3700 | 氾 3701 | 烁 3702 | 邺 3703 | 蟀 3704 | 俸 3705 | 惚 3706 | 瓢 3707 | 挝 3708 | 姊 3709 | 啸 3710 | 饕 3711 | 雌 3712 | 瞿 3713 | 胱 3714 | 犒 3715 | 鸥 3716 | 逡 3717 | 斛 3718 | 溧 3719 | 铿 3720 | 圩 3721 | 砷 3722 | 卑 3723 | 铎 3724 | 柞 3725 | 瓮 3726 | 酋 3727 | 隼 3728 | 锚 3729 | 嗷 3730 | 淖 3731 | 棘 3732 | 忻 3733 | 嗦 3734 | 弑 3735 | 沁 3736 | 覃 3737 | 蚝 3738 | 隽 3739 | 蹂 3740 | 熨 3741 | 侬 3742 | 鳖 3743 | 彿 3744 | 蝎 3745 | 彷 3746 | 鸠 3747 | 吆 3748 | 锷 3749 | 揉 3750 | 屌 3751 | 泗 3752 | 稣 3753 | 茁 3754 | 胚 3755 | 掖 3756 | 砥 3757 | 扉 3758 | 叨 3759 | 虱 3760 | 弩 3761 | 磺 3762 | 痼 3763 | 砰 3764 | 殭 3765 | 猬 3766 | 榻 3767 | 簸 3768 | 祉 3769 | 瑙 3770 | 祛 3771 | 鳞 3772 | 骜 3773 | 涕 3774 | 鲟 3775 | 溉 3776 | 鲶 3777 | 姝 3778 | 檐 3779 | 玖 3780 | 疚 3781 | 哆 3782 | 丞 3783 | 俾 3784 | 鹞 3785 | 藩 3786 | 圃 3787 | 瀛 3788 | 菠 3789 | 筐 3790 | 镯 3791 | 邃 3792 | 捺 3793 | 萋 3794 | 悸 3795 | 镁 3796 | 腑 3797 | 髋 3798 | 俚 3799 | 栾 3800 | 陂 3801 | 蜷 3802 | 卯 3803 | 凄 3804 | 舸 3805 | 捎 3806 | 餮 3807 | 窈 3808 | 锭 3809 | 佶 3810 | 嗽 3811 | 笈 3812 | 殓 3813 | 郫 3814 | 甬 3815 | 蟋 3816 | 狙 3817 | 槿 3818 | 馥 3819 | 蠕 3820 | 椿 3821 | 侥 3822 | 嘻 3823 | 箕 3824 | 怦 3825 | 绅 3826 | 闳 3827 | 蜿 3828 | 珜 3829 | 惦 3830 | 镖 3831 | 蚓 3832 | 漯 3833 | 郜 3834 | 狰 3835 | 狒 3836 | 啵 3837 | 鳅 3838 | 葆 3839 | 澈 3840 | 蜒 3841 | 伢 3842 | 抿 3843 | 佼 3844 | 瘟 3845 | 霁 3846 | 朽 3847 | 陇 3848 | 觎 3849 | 燎 3850 | 迢 3851 | 腩 3852 | 驯 3853 | 铰 3854 | 槐 3855 | 逞 3856 | 萦 3857 | 秃 3858 | 爵 3859 | 乍 3860 | 珑 3861 | 烊 3862 | 淌 3863 | 钝 3864 | 渲 3865 | 摒 3866 | 颚 3867 | 荤 3868 | 狞 3869 | 浚 3870 | 邋 3871 | 馑 3872 | 咣 3873 | 诲 3874 | 暄 3875 | 骼 3876 | 琉 3877 | 黔 3878 | 擘 3879 | 嵩 3880 | 忪 3881 | 茬 3882 | 浈 3883 | 凋 3884 | 垌 3885 | 壑 3886 | t 3887 | 谑 3888 | 嗣 3889 | 拭 3890 | 稼 3891 | 竽 3892 | 羔 3893 | 褛 3894 | 惆 3895 | 掬 3896 | 吏 3897 | 哎 3898 | 湍 3899 | 泞 3900 | 霎 3901 | 亳 3902 | 炷 3903 | 蜈 3904 | 瘸 3905 | 鼾 3906 | 戬 3907 | 潞 3908 | 恬 3909 | 陬 3910 | 烷 3911 | 琏 3912 | 骐 3913 | 氟 3914 | 匡 3915 | 锥 3916 | 宦 3917 | 薙 3918 | 癣 3919 | 惬 3920 | 粕 3921 | 妧 3922 | 鸾 3923 | 狡 3924 | 遢 3925 | 巿 3926 | 琥 3927 | 盎 3928 | 寐 3929 | 莅 3930 | 榉 3931 | 璋 3932 | 嵘 3933 | 佘 3934 | 枭 3935 | 窿 3936 | 侑 3937 | 孪 3938 | 芪 3939 | 腮 3940 | 裳 3941 | 啶 3942 | 叵 3943 | 岐 3944 | 札 3945 | 玟 3946 | 嫔 3947 | 铠 3948 | 篪 3949 | 卒 3950 | 隍 3951 | 唠 3952 | 兀 3953 | 沱 3954 | 蓁 3955 | 珏 3956 | 跛 3957 | 铡 3958 | 锟 3959 | 泱 3960 | 瞌 3961 | 麒 3962 | 肮 3963 | 揄 3964 | 桉 3965 | 胧 3966 | 濑 3967 | 耄 3968 | 锢 3969 | 冥 3970 | 眶 3971 | 霈 3972 | 靶 3973 | 怯 3974 | 漱 3975 | 蛀 3976 | 赝 3977 | 宓 3978 | 傥 3979 | 梵 3980 | 褴 3981 | 妊 3982 | 诶 3983 | 娩 3984 | 脯 3985 | 荃 3986 | 襟 3987 | 蜊 3988 | 娌 3989 | 舜 3990 | 侍 3991 | 胛 3992 | 孢 3993 | 洙 3994 | 涮 3995 | 仄 3996 | 臆 3997 | 凇 3998 | 缤 3999 | 瘪 4000 | 撸 4001 | 蹚 4002 | 磡 4003 | 悯 4004 | 嚓 4005 | 醐 4006 | 吩 4007 | 昵 4008 | 嬷 4009 | 荇 4010 | 擞 4011 | 眺 4012 | 茆 4013 | 娠 4014 | 纣 4015 | 榭 4016 | 楂 4017 | 蓟 4018 | 泯 4019 | 睾 4020 | 俭 4021 | 咔 4022 | 嘶 4023 | 吿 4024 | 颍 4025 | 啕 4026 | 沅 4027 | 唬 4028 | 谚 4029 | 斟 4030 | 羣 4031 | 螳 4032 | 撵 4033 | 虞 4034 | 嫡 4035 | 咐 4036 | 嫦 4037 | 挛 4038 | 濡 4039 | 佃 4040 | 惶 4041 | 塍 4042 | 琵 4043 | 谀 4044 | 芊 4045 | 醍 4046 | 遨 4047 | 碚 4048 | 嗲 4049 | 孺 4050 | 噼 4051 | 殚 4052 | 漭 4053 | 绌 4054 | 拴 4055 | 瘁 4056 | 貉 4057 | 怅 4058 | 揩 4059 | 镐 4060 | 曰 4061 | 嵋 4062 | 潦 4063 | 衙 4064 | 绢 4065 | 崧 4066 | 刨 4067 | 岷 4068 | 燊 4069 | 喽 4070 | 猾 4071 | 粱 4072 | 叩 4073 | 蕲 4074 | 跄 4075 | 獾 4076 | 侗 4077 | 肴 4078 | 拈 4079 | 敝 4080 | 罡 4081 | 炕 4082 | 疝 4083 | 掳 4084 | 埠 4085 | 悌 4086 | 狩 4087 | 牾 4088 | 沂 4089 | 珺 4090 | 妤 4091 | 怵 4092 | 纶 4093 | 蛆 4094 | 黍 4095 | k 4096 | 蜗 4097 | 秤 4098 | 垅 4099 | 骇 4100 | 焖 4101 | 洵 4102 | 秣 4103 | 泓 4104 | 兖 4105 | 莺 4106 | 耋 4107 | 漕 4108 | 蜥 4109 | 艋 4110 | 疃 4111 | 鏖 4112 | 涓 4113 | 咙 4114 | 槎 4115 | 汾 4116 | 湄 4117 | 孱 4118 | 眯 4119 | 鸵 4120 | 遁 4121 | 魇 4122 | 腋 4123 | 觐 4124 | 铮 4125 | 竲 4126 | 徉 4127 | 蚣 4128 | 薰 4129 | 咯 4130 | 漾 4131 | 噘 4132 | 庶 4133 | 脍 4134 | 隘 4135 | 镊 4136 | 鸳 4137 | 涟 4138 | 厩 4139 | 翌 4140 | 揶 4141 | 犁 4142 | 埜 4143 | 冗 4144 | 氪 4145 | 砲 4146 | 憷 4147 | 苡 4148 | 邂 4149 | 谕 4150 | 鞘 4151 | 砝 4152 | 寰 4153 | 斡 4154 | 耸 4155 | 恸 4156 | 蚌 4157 | 毂 4158 | 圭 4159 | 髌 4160 | 桓 4161 | 恣 4162 | 乾 4163 | 阂 4164 | 徇 4165 | 碉 4166 | 焘 4167 | 氦 4168 | 眬 4169 | 岱 4170 | 踉 4171 | 枷 4172 | 镀 4173 | 鲷 4174 | 烙 4175 | 皎 4176 | 缇 4177 | 骊 4178 | 嘭 4179 | 瑁 4180 | 莠 4181 | 嵛 4182 | 僮 4183 | 嶝 4184 | 荞 4185 | 簇 4186 | 纰 4187 | 碴 4188 | 呗 4189 | 胤 4190 | 磴 4191 | 瘩 4192 | 缰 4193 | 咧 4194 | 蜴 4195 | 诓 4196 | 楔 4197 | 啬 4198 | 逅 4199 | 颊 4200 | 弛 4201 | 吭 4202 | 哺 4203 | 嗬 4204 | 酯 4205 | 淼 4206 | 膑 4207 | 邛 4208 | 褶 4209 | 幄 4210 | 痉 4211 | 烛 4212 | 迥 4213 | 卲 4214 | 唉 4215 | 涎 4216 | 鸯 4217 | 锜 4218 | 俺 4219 | 斓 4220 | 帚 4221 | 瞟 4222 | 撩 4223 | 崃 4224 | 椋 4225 | 渥 4226 | 嘈 4227 | 沏 4228 | 倜 4229 | 荚 4230 | 婀 4231 | 哟 4232 | 赦 4233 | 缙 4234 | 吟 4235 | 砚 4236 | c 4237 | 唁 4238 | 馗 4239 | 邝 4240 | 犄 4241 | 孀 4242 | 咀 4243 | 箫 4244 | 阜 4245 | 憩 4246 | 喱 4247 | 琰 4248 | 靛 4249 | 茎 4250 | 趸 4251 | 恪 4252 | 夙 4253 | 呲 4254 | 稠 4255 | 蟒 4256 | 泠 4257 | 骅 4258 | 靑 4259 | 喵 4260 | 峁 4261 | 桎 4262 | 奘 4263 | 啰 4264 | 钒 4265 | 仡 4266 | 犊 4267 | 腓 4268 | 嫚 4269 | 楣 4270 | 铣 4271 | 朦 4272 | 哇 4273 | 祢 4274 | 糗 4275 | 觥 4276 | 菩 4277 | 搽 4278 | 轶 4279 | 惰 4280 | 啪 4281 | 塬 4282 | 笙 4283 | 抨 4284 | 岬 4285 | 鲤 4286 | a 4287 | 狈 4288 | 朐 4289 | 垛 4290 | 妯 4291 | 玳 4292 | 晦 4293 | 蹶 4294 | 笃 4295 | 炊 4296 | 逵 4297 | 蜻 4298 | 祯 4299 | -------------------------------------------------------------------------------- /data_utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/data_utils/__init__.py -------------------------------------------------------------------------------- /data_utils/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/data_utils/__init__.pyc -------------------------------------------------------------------------------- /data_utils/audio.py: -------------------------------------------------------------------------------- 1 | """Contains the audio segment class.""" 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | 6 | import numpy as np 7 | import io 8 | import struct 9 | import re 10 | import soundfile 11 | import resampy 12 | from scipy import signal 13 | import random 14 | import copy 15 | 16 | 17 | class AudioSegment(object): 18 | """Monaural audio segment abstraction. 19 | 20 | :param samples: Audio samples [num_samples x num_channels]. 21 | :type samples: ndarray.float32 22 | :param sample_rate: Audio sample rate. 23 | :type sample_rate: int 24 | :raises TypeError: If the sample data type is not float or int. 25 | """ 26 | 27 | def __init__(self, samples, sample_rate): 28 | """Create audio segment from samples. 29 | 30 | Samples are convert float32 internally, with int scaled to [-1, 1]. 31 | """ 32 | self._samples = self._convert_samples_to_float32(samples) 33 | self._sample_rate = sample_rate 34 | if self._samples.ndim >= 2: 35 | self._samples = np.mean(self._samples, 1) 36 | 37 | def __eq__(self, other): 38 | """Return whether two objects are equal.""" 39 | if type(other) is not type(self): 40 | return False 41 | if self._sample_rate != other._sample_rate: 42 | return False 43 | if self._samples.shape != other._samples.shape: 44 | return False 45 | if np.any(self.samples != other._samples): 46 | return False 47 | return True 48 | 49 | def __ne__(self, other): 50 | """Return whether two objects are unequal.""" 51 | return not self.__eq__(other) 52 | 53 | def __str__(self): 54 | """Return human-readable representation of segment.""" 55 | return ("%s: num_samples=%d, sample_rate=%d, duration=%.2fsec, " 56 | "rms=%.2fdB" % (type(self), self.num_samples, self.sample_rate, 57 | self.duration, self.rms_db)) 58 | 59 | @classmethod 60 | def from_file(cls, file): 61 | """Create audio segment from audio file. 62 | 63 | :param filepath: Filepath or file object to audio file. 64 | :type filepath: basestring|file 65 | :return: Audio segment instance. 66 | :rtype: AudioSegment 67 | """ 68 | if isinstance(file, basestring) and re.findall(r".seqbin_\d+$", file): 69 | return cls.from_sequence_file(file) 70 | else: 71 | samples, sample_rate = soundfile.read(file, dtype='float32') 72 | return cls(samples, sample_rate) 73 | 74 | @classmethod 75 | def slice_from_file(cls, file, start=None, end=None): 76 | """Loads a small section of an audio without having to load 77 | the entire file into the memory which can be incredibly wasteful. 78 | 79 | :param file: Input audio filepath or file object. 80 | :type file: basestring|file 81 | :param start: Start time in seconds. If start is negative, it wraps 82 | around from the end. If not provided, this function 83 | reads from the very beginning. 84 | :type start: float 85 | :param end: End time in seconds. If end is negative, it wraps around 86 | from the end. If not provided, the default behvaior is 87 | to read to the end of the file. 88 | :type end: float 89 | :return: AudioSegment instance of the specified slice of the input 90 | audio file. 91 | :rtype: AudioSegment 92 | :raise ValueError: If start or end is incorrectly set, e.g. out of 93 | bounds in time. 94 | """ 95 | sndfile = soundfile.SoundFile(file) 96 | sample_rate = sndfile.samplerate 97 | duration = float(len(sndfile)) / sample_rate 98 | start = 0. if start is None else start 99 | end = 0. if end is None else end 100 | if start < 0.0: 101 | start += duration 102 | if end < 0.0: 103 | end += duration 104 | if start < 0.0: 105 | raise ValueError("The slice start position (%f s) is out of " 106 | "bounds." % start) 107 | if end < 0.0: 108 | raise ValueError("The slice end position (%f s) is out of bounds." % 109 | end) 110 | if start > end: 111 | raise ValueError("The slice start position (%f s) is later than " 112 | "the slice end position (%f s)." % (start, end)) 113 | if end > duration: 114 | raise ValueError("The slice end position (%f s) is out of bounds " 115 | "(> %f s)" % (end, duration)) 116 | start_frame = int(start * sample_rate) 117 | end_frame = int(end * sample_rate) 118 | sndfile.seek(start_frame) 119 | data = sndfile.read(frames=end_frame - start_frame, dtype='float32') 120 | return cls(data, sample_rate) 121 | 122 | @classmethod 123 | def from_sequence_file(cls, filepath): 124 | """Create audio segment from sequence file. Sequence file is a binary 125 | file containing a collection of multiple audio files, with several 126 | header bytes in the head indicating the offsets of each audio byte data 127 | chunk. 128 | 129 | The format is: 130 | 131 | 4 bytes (int, version), 132 | 4 bytes (int, num of utterance), 133 | 4 bytes (int, bytes per header), 134 | [bytes_per_header*(num_utterance+1)] bytes (offsets for each audio), 135 | audio_bytes_data_of_1st_utterance, 136 | audio_bytes_data_of_2nd_utterance, 137 | ...... 138 | 139 | Sequence file name must end with ".seqbin". And the filename of the 5th 140 | utterance's audio file in sequence file "xxx.seqbin" must be 141 | "xxx.seqbin_5", with "5" indicating the utterance index within this 142 | sequence file (starting from 1). 143 | 144 | :param filepath: Filepath of sequence file. 145 | :type filepath: basestring 146 | :return: Audio segment instance. 147 | :rtype: AudioSegment 148 | """ 149 | # parse filepath 150 | matches = re.match(r"(.+\.seqbin)_(\d+)", filepath) 151 | if matches is None: 152 | raise IOError("File type of %s is not supported" % filepath) 153 | filename = matches.group(1) 154 | fileno = int(matches.group(2)) 155 | 156 | # read headers 157 | f = open(filename, 'rb') 158 | version = f.read(4) 159 | num_utterances = struct.unpack("i", f.read(4))[0] 160 | bytes_per_header = struct.unpack("i", f.read(4))[0] 161 | header_bytes = f.read(bytes_per_header * (num_utterances + 1)) 162 | header = [ 163 | struct.unpack("i", header_bytes[bytes_per_header * i: 164 | bytes_per_header * (i + 1)])[0] 165 | for i in range(num_utterances + 1) 166 | ] 167 | 168 | # read audio bytes 169 | f.seek(header[fileno - 1]) 170 | audio_bytes = f.read(header[fileno] - header[fileno - 1]) 171 | f.close() 172 | 173 | # create audio segment 174 | try: 175 | return cls.from_bytes(audio_bytes) 176 | except Exception as e: 177 | samples = np.frombuffer(audio_bytes, dtype='int16') 178 | return cls(samples=samples, sample_rate=8000) 179 | 180 | @classmethod 181 | def from_bytes(cls, bytes): 182 | """Create audio segment from a byte string containing audio samples. 183 | 184 | :param bytes: Byte string containing audio samples. 185 | :type bytes: str 186 | :return: Audio segment instance. 187 | :rtype: AudioSegment 188 | """ 189 | samples, sample_rate = soundfile.read( 190 | io.BytesIO(bytes), dtype='float32') 191 | return cls(samples, sample_rate) 192 | 193 | @classmethod 194 | def concatenate(cls, *segments): 195 | """Concatenate an arbitrary number of audio segments together. 196 | 197 | :param *segments: Input audio segments to be concatenated. 198 | :type *segments: tuple of AudioSegment 199 | :return: Audio segment instance as concatenating results. 200 | :rtype: AudioSegment 201 | :raises ValueError: If the number of segments is zero, or if the 202 | sample_rate of any segments does not match. 203 | :raises TypeError: If any segment is not AudioSegment instance. 204 | """ 205 | # Perform basic sanity-checks. 206 | if len(segments) == 0: 207 | raise ValueError("No audio segments are given to concatenate.") 208 | sample_rate = segments[0]._sample_rate 209 | for seg in segments: 210 | if sample_rate != seg._sample_rate: 211 | raise ValueError("Can't concatenate segments with " 212 | "different sample rates") 213 | if type(seg) is not cls: 214 | raise TypeError("Only audio segments of the same type " 215 | "can be concatenated.") 216 | samples = np.concatenate([seg.samples for seg in segments]) 217 | return cls(samples, sample_rate) 218 | 219 | @classmethod 220 | def make_silence(cls, duration, sample_rate): 221 | """Creates a silent audio segment of the given duration and sample rate. 222 | 223 | :param duration: Length of silence in seconds. 224 | :type duration: float 225 | :param sample_rate: Sample rate. 226 | :type sample_rate: float 227 | :return: Silent AudioSegment instance of the given duration. 228 | :rtype: AudioSegment 229 | """ 230 | samples = np.zeros(int(duration * sample_rate)) 231 | return cls(samples, sample_rate) 232 | 233 | def to_wav_file(self, filepath, dtype='float32'): 234 | """Save audio segment to disk as wav file. 235 | 236 | :param filepath: WAV filepath or file object to save the 237 | audio segment. 238 | :type filepath: basestring|file 239 | :param dtype: Subtype for audio file. Options: 'int16', 'int32', 240 | 'float32', 'float64'. Default is 'float32'. 241 | :type dtype: str 242 | :raises TypeError: If dtype is not supported. 243 | """ 244 | samples = self._convert_samples_from_float32(self._samples, dtype) 245 | subtype_map = { 246 | 'int16': 'PCM_16', 247 | 'int32': 'PCM_32', 248 | 'float32': 'FLOAT', 249 | 'float64': 'DOUBLE' 250 | } 251 | soundfile.write( 252 | filepath, 253 | samples, 254 | self._sample_rate, 255 | format='WAV', 256 | subtype=subtype_map[dtype]) 257 | 258 | def superimpose(self, other): 259 | """Add samples from another segment to those of this segment 260 | (sample-wise addition, not segment concatenation). 261 | 262 | Note that this is an in-place transformation. 263 | 264 | :param other: Segment containing samples to be added in. 265 | :type other: AudioSegments 266 | :raise TypeError: If type of two segments don't match. 267 | :raise ValueError: If the sample rates of the two segments are not 268 | equal, or if the lengths of segments don't match. 269 | """ 270 | if isinstance(other, type(self)): 271 | raise TypeError("Cannot add segments of different types: %s " 272 | "and %s." % (type(self), type(other))) 273 | if self._sample_rate != other._sample_rate: 274 | raise ValueError("Sample rates must match to add segments.") 275 | if len(self._samples) != len(other._samples): 276 | raise ValueError("Segment lengths must match to add segments.") 277 | self._samples += other._samples 278 | 279 | def to_bytes(self, dtype='float32'): 280 | """Create a byte string containing the audio content. 281 | 282 | :param dtype: Data type for export samples. Options: 'int16', 'int32', 283 | 'float32', 'float64'. Default is 'float32'. 284 | :type dtype: str 285 | :return: Byte string containing audio content. 286 | :rtype: str 287 | """ 288 | samples = self._convert_samples_from_float32(self._samples, dtype) 289 | return samples.tostring() 290 | 291 | def gain_db(self, gain): 292 | """Apply gain in decibels to samples. 293 | 294 | Note that this is an in-place transformation. 295 | 296 | :param gain: Gain in decibels to apply to samples. 297 | :type gain: float|1darray 298 | """ 299 | self._samples *= 10.**(gain / 20.) 300 | 301 | def change_speed(self, speed_rate): 302 | """Change the audio speed by linear interpolation. 303 | 304 | Note that this is an in-place transformation. 305 | 306 | :param speed_rate: Rate of speed change: 307 | speed_rate > 1.0, speed up the audio; 308 | speed_rate = 1.0, unchanged; 309 | speed_rate < 1.0, slow down the audio; 310 | speed_rate <= 0.0, not allowed, raise ValueError. 311 | :type speed_rate: float 312 | :raises ValueError: If speed_rate <= 0.0. 313 | """ 314 | if speed_rate <= 0: 315 | raise ValueError("speed_rate should be greater than zero.") 316 | old_length = self._samples.shape[0] 317 | new_length = int(old_length / speed_rate) 318 | old_indices = np.arange(old_length) 319 | new_indices = np.linspace(start=0, stop=old_length, num=new_length) 320 | self._samples = np.interp(new_indices, old_indices, self._samples) 321 | 322 | def normalize(self, target_db=-20, max_gain_db=300.0): 323 | """Normalize audio to be of the desired RMS value in decibels. 324 | 325 | Note that this is an in-place transformation. 326 | 327 | :param target_db: Target RMS value in decibels. This value should be 328 | less than 0.0 as 0.0 is full-scale audio. 329 | :type target_db: float 330 | :param max_gain_db: Max amount of gain in dB that can be applied for 331 | normalization. This is to prevent nans when 332 | attempting to normalize a signal consisting of 333 | all zeros. 334 | :type max_gain_db: float 335 | :raises ValueError: If the required gain to normalize the segment to 336 | the target_db value exceeds max_gain_db. 337 | """ 338 | gain = target_db - self.rms_db 339 | if gain > max_gain_db: 340 | raise ValueError( 341 | "Unable to normalize segment to %f dB because the " 342 | "the probable gain have exceeds max_gain_db (%f dB)" % 343 | (target_db, max_gain_db)) 344 | self.gain_db(min(max_gain_db, target_db - self.rms_db)) 345 | 346 | def normalize_online_bayesian(self, 347 | target_db, 348 | prior_db, 349 | prior_samples, 350 | startup_delay=0.0): 351 | """Normalize audio using a production-compatible online/causal 352 | algorithm. This uses an exponential likelihood and gamma prior to 353 | make online estimates of the RMS even when there are very few samples. 354 | 355 | Note that this is an in-place transformation. 356 | 357 | :param target_db: Target RMS value in decibels. 358 | :type target_bd: float 359 | :param prior_db: Prior RMS estimate in decibels. 360 | :type prior_db: float 361 | :param prior_samples: Prior strength in number of samples. 362 | :type prior_samples: float 363 | :param startup_delay: Default 0.0s. If provided, this function will 364 | accrue statistics for the first startup_delay 365 | seconds before applying online normalization. 366 | :type startup_delay: float 367 | """ 368 | # Estimate total RMS online. 369 | startup_sample_idx = min(self.num_samples - 1, 370 | int(self.sample_rate * startup_delay)) 371 | prior_mean_squared = 10.**(prior_db / 10.) 372 | prior_sum_of_squares = prior_mean_squared * prior_samples 373 | cumsum_of_squares = np.cumsum(self.samples**2) 374 | sample_count = np.arange(self.num_samples) + 1 375 | if startup_sample_idx > 0: 376 | cumsum_of_squares[:startup_sample_idx] = \ 377 | cumsum_of_squares[startup_sample_idx] 378 | sample_count[:startup_sample_idx] = \ 379 | sample_count[startup_sample_idx] 380 | mean_squared_estimate = ((cumsum_of_squares + prior_sum_of_squares) / 381 | (sample_count + prior_samples)) 382 | rms_estimate_db = 10 * np.log10(mean_squared_estimate) 383 | # Compute required time-varying gain. 384 | gain_db = target_db - rms_estimate_db 385 | self.gain_db(gain_db) 386 | 387 | def resample(self, target_sample_rate, filter='kaiser_best'): 388 | """Resample the audio to a target sample rate. 389 | 390 | Note that this is an in-place transformation. 391 | 392 | :param target_sample_rate: Target sample rate. 393 | :type target_sample_rate: int 394 | :param filter: The resampling filter to use one of {'kaiser_best', 395 | 'kaiser_fast'}. 396 | :type filter: str 397 | """ 398 | self._samples = resampy.resample( 399 | self.samples, self.sample_rate, target_sample_rate, filter=filter) 400 | self._sample_rate = target_sample_rate 401 | 402 | def pad_silence(self, duration, sides='both'): 403 | """Pad this audio sample with a period of silence. 404 | 405 | Note that this is an in-place transformation. 406 | 407 | :param duration: Length of silence in seconds to pad. 408 | :type duration: float 409 | :param sides: Position for padding: 410 | 'beginning' - adds silence in the beginning; 411 | 'end' - adds silence in the end; 412 | 'both' - adds silence in both the beginning and the end. 413 | :type sides: str 414 | :raises ValueError: If sides is not supported. 415 | """ 416 | if duration == 0.0: 417 | return self 418 | cls = type(self) 419 | silence = self.make_silence(duration, self._sample_rate) 420 | if sides == "beginning": 421 | padded = cls.concatenate(silence, self) 422 | elif sides == "end": 423 | padded = cls.concatenate(self, silence) 424 | elif sides == "both": 425 | padded = cls.concatenate(silence, self, silence) 426 | else: 427 | raise ValueError("Unknown value for the sides %s" % sides) 428 | self._samples = padded._samples 429 | 430 | def shift(self, shift_ms): 431 | """Shift the audio in time. If `shift_ms` is positive, shift with time 432 | advance; if negative, shift with time delay. Silence are padded to 433 | keep the duration unchanged. 434 | 435 | Note that this is an in-place transformation. 436 | 437 | :param shift_ms: Shift time in millseconds. If positive, shift with 438 | time advance; if negative; shift with time delay. 439 | :type shift_ms: float 440 | :raises ValueError: If shift_ms is longer than audio duration. 441 | """ 442 | if abs(shift_ms) / 1000.0 > self.duration: 443 | raise ValueError("Absolute value of shift_ms should be smaller " 444 | "than audio duration.") 445 | shift_samples = int(shift_ms * self._sample_rate / 1000) 446 | if shift_samples > 0: 447 | # time advance 448 | self._samples[:-shift_samples] = self._samples[shift_samples:] 449 | self._samples[-shift_samples:] = 0 450 | elif shift_samples < 0: 451 | # time delay 452 | self._samples[-shift_samples:] = self._samples[:shift_samples] 453 | self._samples[:-shift_samples] = 0 454 | 455 | def subsegment(self, start_sec=None, end_sec=None): 456 | """Cut the AudioSegment between given boundaries. 457 | 458 | Note that this is an in-place transformation. 459 | 460 | :param start_sec: Beginning of subsegment in seconds. 461 | :type start_sec: float 462 | :param end_sec: End of subsegment in seconds. 463 | :type end_sec: float 464 | :raise ValueError: If start_sec or end_sec is incorrectly set, e.g. out 465 | of bounds in time. 466 | """ 467 | start_sec = 0.0 if start_sec is None else start_sec 468 | end_sec = self.duration if end_sec is None else end_sec 469 | if start_sec < 0.0: 470 | start_sec = self.duration + start_sec 471 | if end_sec < 0.0: 472 | end_sec = self.duration + end_sec 473 | if start_sec < 0.0: 474 | raise ValueError("The slice start position (%f s) is out of " 475 | "bounds." % start_sec) 476 | if end_sec < 0.0: 477 | raise ValueError("The slice end position (%f s) is out of bounds." % 478 | end_sec) 479 | if start_sec > end_sec: 480 | raise ValueError("The slice start position (%f s) is later than " 481 | "the end position (%f s)." % (start_sec, end_sec)) 482 | if end_sec > self.duration: 483 | raise ValueError("The slice end position (%f s) is out of bounds " 484 | "(> %f s)" % (end_sec, self.duration)) 485 | start_sample = int(round(start_sec * self._sample_rate)) 486 | end_sample = int(round(end_sec * self._sample_rate)) 487 | self._samples = self._samples[start_sample:end_sample] 488 | 489 | def random_subsegment(self, subsegment_length, rng=None): 490 | """Cut the specified length of the audiosegment randomly. 491 | 492 | Note that this is an in-place transformation. 493 | 494 | :param subsegment_length: Subsegment length in seconds. 495 | :type subsegment_length: float 496 | :param rng: Random number generator state. 497 | :type rng: random.Random 498 | :raises ValueError: If the length of subsegment is greater than 499 | the origineal segemnt. 500 | """ 501 | rng = random.Random() if rng is None else rng 502 | if subsegment_length > self.duration: 503 | raise ValueError("Length of subsegment must not be greater " 504 | "than original segment.") 505 | start_time = rng.uniform(0.0, self.duration - subsegment_length) 506 | self.subsegment(start_time, start_time + subsegment_length) 507 | 508 | def convolve(self, impulse_segment, allow_resample=False): 509 | """Convolve this audio segment with the given impulse segment. 510 | 511 | Note that this is an in-place transformation. 512 | 513 | :param impulse_segment: Impulse response segments. 514 | :type impulse_segment: AudioSegment 515 | :param allow_resample: Indicates whether resampling is allowed when 516 | the impulse_segment has a different sample 517 | rate from this signal. 518 | :type allow_resample: bool 519 | :raises ValueError: If the sample rate is not match between two 520 | audio segments when resample is not allowed. 521 | """ 522 | if allow_resample and self.sample_rate != impulse_segment.sample_rate: 523 | impulse_segment.resample(self.sample_rate) 524 | if self.sample_rate != impulse_segment.sample_rate: 525 | raise ValueError("Impulse segment's sample rate (%d Hz) is not " 526 | "equal to base signal sample rate (%d Hz)." % 527 | (impulse_segment.sample_rate, self.sample_rate)) 528 | samples = signal.fftconvolve(self.samples, impulse_segment.samples, 529 | "full") 530 | self._samples = samples 531 | 532 | def convolve_and_normalize(self, impulse_segment, allow_resample=False): 533 | """Convolve and normalize the resulting audio segment so that it 534 | has the same average power as the input signal. 535 | 536 | Note that this is an in-place transformation. 537 | 538 | :param impulse_segment: Impulse response segments. 539 | :type impulse_segment: AudioSegment 540 | :param allow_resample: Indicates whether resampling is allowed when 541 | the impulse_segment has a different sample 542 | rate from this signal. 543 | :type allow_resample: bool 544 | """ 545 | target_db = self.rms_db 546 | self.convolve(impulse_segment, allow_resample=allow_resample) 547 | self.normalize(target_db) 548 | 549 | def add_noise(self, 550 | noise, 551 | snr_dB, 552 | allow_downsampling=False, 553 | max_gain_db=300.0, 554 | rng=None): 555 | """Add the given noise segment at a specific signal-to-noise ratio. 556 | If the noise segment is longer than this segment, a random subsegment 557 | of matching length is sampled from it and used instead. 558 | 559 | Note that this is an in-place transformation. 560 | 561 | :param noise: Noise signal to add. 562 | :type noise: AudioSegment 563 | :param snr_dB: Signal-to-Noise Ratio, in decibels. 564 | :type snr_dB: float 565 | :param allow_downsampling: Whether to allow the noise signal to be 566 | downsampled to match the base signal sample 567 | rate. 568 | :type allow_downsampling: bool 569 | :param max_gain_db: Maximum amount of gain to apply to noise signal 570 | before adding it in. This is to prevent attempting 571 | to apply infinite gain to a zero signal. 572 | :type max_gain_db: float 573 | :param rng: Random number generator state. 574 | :type rng: None|random.Random 575 | :raises ValueError: If the sample rate does not match between the two 576 | audio segments when downsampling is not allowed, or 577 | if the duration of noise segments is shorter than 578 | original audio segments. 579 | """ 580 | rng = random.Random() if rng is None else rng 581 | if allow_downsampling and noise.sample_rate > self.sample_rate: 582 | noise = noise.resample(self.sample_rate) 583 | if noise.sample_rate != self.sample_rate: 584 | raise ValueError("Noise sample rate (%d Hz) is not equal to base " 585 | "signal sample rate (%d Hz)." % (noise.sample_rate, 586 | self.sample_rate)) 587 | if noise.duration < self.duration: 588 | raise ValueError("Noise signal (%f sec) must be at least as long as" 589 | " base signal (%f sec)." % 590 | (noise.duration, self.duration)) 591 | noise_gain_db = min(self.rms_db - noise.rms_db - snr_dB, max_gain_db) 592 | noise_new = copy.deepcopy(noise) 593 | noise_new.random_subsegment(self.duration, rng=rng) 594 | noise_new.gain_db(noise_gain_db) 595 | self.superimpose(noise_new) 596 | 597 | @property 598 | def samples(self): 599 | """Return audio samples. 600 | 601 | :return: Audio samples. 602 | :rtype: ndarray 603 | """ 604 | return self._samples.copy() 605 | 606 | @property 607 | def sample_rate(self): 608 | """Return audio sample rate. 609 | 610 | :return: Audio sample rate. 611 | :rtype: int 612 | """ 613 | return self._sample_rate 614 | 615 | @property 616 | def num_samples(self): 617 | """Return number of samples. 618 | 619 | :return: Number of samples. 620 | :rtype: int 621 | """ 622 | return self._samples.shape[0] 623 | 624 | @property 625 | def duration(self): 626 | """Return audio duration. 627 | 628 | :return: Audio duration in seconds. 629 | :rtype: float 630 | """ 631 | return self._samples.shape[0] / float(self._sample_rate) 632 | 633 | @property 634 | def rms_db(self): 635 | """Return root mean square energy of the audio in decibels. 636 | 637 | :return: Root mean square energy in decibels. 638 | :rtype: float 639 | """ 640 | # square root => multiply by 10 instead of 20 for dBs 641 | mean_square = np.mean(self._samples**2) 642 | return 10 * np.log10(mean_square) 643 | 644 | def _convert_samples_to_float32(self, samples): 645 | """Convert sample type to float32. 646 | 647 | Audio sample type is usually integer or float-point. 648 | Integers will be scaled to [-1, 1] in float32. 649 | """ 650 | float32_samples = samples.astype('float32') 651 | if samples.dtype in np.sctypes['int']: 652 | bits = np.iinfo(samples.dtype).bits 653 | float32_samples *= (1. / 2**(bits - 1)) 654 | elif samples.dtype in np.sctypes['float']: 655 | pass 656 | else: 657 | raise TypeError("Unsupported sample type: %s." % samples.dtype) 658 | return float32_samples 659 | 660 | def _convert_samples_from_float32(self, samples, dtype): 661 | """Convert sample type from float32 to dtype. 662 | 663 | Audio sample type is usually integer or float-point. For integer 664 | type, float32 will be rescaled from [-1, 1] to the maximum range 665 | supported by the integer type. 666 | 667 | This is for writing a audio file. 668 | """ 669 | dtype = np.dtype(dtype) 670 | output_samples = samples.copy() 671 | if dtype in np.sctypes['int']: 672 | bits = np.iinfo(dtype).bits 673 | output_samples *= (2**(bits - 1) / 1.) 674 | min_val = np.iinfo(dtype).min 675 | max_val = np.iinfo(dtype).max 676 | output_samples[output_samples > max_val] = max_val 677 | output_samples[output_samples < min_val] = min_val 678 | elif samples.dtype in np.sctypes['float']: 679 | min_val = np.finfo(dtype).min 680 | max_val = np.finfo(dtype).max 681 | output_samples[output_samples > max_val] = max_val 682 | output_samples[output_samples < min_val] = min_val 683 | else: 684 | raise TypeError("Unsupported sample type: %s." % samples.dtype) 685 | return output_samples.astype(dtype) 686 | -------------------------------------------------------------------------------- /data_utils/audio.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/data_utils/audio.pyc -------------------------------------------------------------------------------- /data_utils/audio_featurizer.py: -------------------------------------------------------------------------------- 1 | """Contains the audio featurizer class.""" 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | 6 | import numpy as np 7 | from python_speech_features import mfcc 8 | from python_speech_features import delta 9 | 10 | 11 | class AudioFeaturizer(object): 12 | """Audio featurizer, for extracting features from audio contents of 13 | AudioSegment or SpeechSegment. 14 | 15 | Currently, it supports feature types of linear spectrogram and mfcc. 16 | 17 | :param specgram_type: Specgram feature type. Options: 'linear'. 18 | :type specgram_type: str 19 | :param stride_ms: Striding size (in milliseconds) for generating frames. 20 | :type stride_ms: float 21 | :param window_ms: Window size (in milliseconds) for generating frames. 22 | :type window_ms: float 23 | :param max_freq: When specgram_type is 'linear', only FFT bins 24 | corresponding to frequencies between [0, max_freq] are 25 | returned; when specgram_type is 'mfcc', max_feq is the 26 | highest band edge of mel filters. 27 | :types max_freq: None|float 28 | :param target_sample_rate: Audio are resampled (if upsampling or 29 | downsampling is allowed) to this before 30 | extracting spectrogram features. 31 | :type target_sample_rate: float 32 | :param use_dB_normalization: Whether to normalize the audio to a certain 33 | decibels before extracting the features. 34 | :type use_dB_normalization: bool 35 | :param target_dB: Target audio decibels for normalization. 36 | :type target_dB: float 37 | """ 38 | 39 | def __init__(self, 40 | specgram_type='linear', 41 | stride_ms=10.0, 42 | window_ms=20.0, 43 | max_freq=None, 44 | target_sample_rate=16000, 45 | use_dB_normalization=True, 46 | target_dB=-20): 47 | self._specgram_type = specgram_type 48 | self._stride_ms = stride_ms 49 | self._window_ms = window_ms 50 | self._max_freq = max_freq 51 | self._target_sample_rate = target_sample_rate 52 | self._use_dB_normalization = use_dB_normalization 53 | self._target_dB = target_dB 54 | 55 | def featurize(self, 56 | audio_segment, 57 | allow_downsampling=True, 58 | allow_upsampling=True): 59 | """Extract audio features from AudioSegment or SpeechSegment. 60 | 61 | :param audio_segment: Audio/speech segment to extract features from. 62 | :type audio_segment: AudioSegment|SpeechSegment 63 | :param allow_downsampling: Whether to allow audio downsampling before 64 | featurizing. 65 | :type allow_downsampling: bool 66 | :param allow_upsampling: Whether to allow audio upsampling before 67 | featurizing. 68 | :type allow_upsampling: bool 69 | :return: Spectrogram audio feature in 2darray. 70 | :rtype: ndarray 71 | :raises ValueError: If audio sample rate is not supported. 72 | """ 73 | # upsampling or downsampling 74 | if ((audio_segment.sample_rate > self._target_sample_rate and 75 | allow_downsampling) or 76 | (audio_segment.sample_rate < self._target_sample_rate and 77 | allow_upsampling)): 78 | audio_segment.resample(self._target_sample_rate) 79 | if audio_segment.sample_rate != self._target_sample_rate: 80 | raise ValueError("Audio sample rate is not supported. " 81 | "Turn allow_downsampling or allow up_sampling on.") 82 | # decibel normalization 83 | if self._use_dB_normalization: 84 | audio_segment.normalize(target_db=self._target_dB) 85 | # extract spectrogram 86 | return self._compute_specgram(audio_segment.samples, 87 | audio_segment.sample_rate) 88 | 89 | def _compute_specgram(self, samples, sample_rate): 90 | """Extract various audio features.""" 91 | if self._specgram_type == 'linear': 92 | return self._compute_linear_specgram( 93 | samples, sample_rate, self._stride_ms, self._window_ms, 94 | self._max_freq) 95 | elif self._specgram_type == 'mfcc': 96 | return self._compute_mfcc(samples, sample_rate, self._stride_ms, 97 | self._window_ms, self._max_freq) 98 | else: 99 | raise ValueError("Unknown specgram_type %s. " 100 | "Supported values: linear." % self._specgram_type) 101 | 102 | def _compute_linear_specgram(self, 103 | samples, 104 | sample_rate, 105 | stride_ms=10.0, 106 | window_ms=20.0, 107 | max_freq=None, 108 | eps=1e-14): 109 | """Compute the linear spectrogram from FFT energy.""" 110 | if max_freq is None: 111 | max_freq = sample_rate / 2 112 | if max_freq > sample_rate / 2: 113 | raise ValueError("max_freq must not be greater than half of " 114 | "sample rate.") 115 | if stride_ms > window_ms: 116 | raise ValueError("Stride size must not be greater than " 117 | "window size.") 118 | stride_size = int(0.001 * sample_rate * stride_ms) 119 | window_size = int(0.001 * sample_rate * window_ms) 120 | specgram, freqs = self._specgram_real( 121 | samples, 122 | window_size=window_size, 123 | stride_size=stride_size, 124 | sample_rate=sample_rate) 125 | ind = np.where(freqs <= max_freq)[0][-1] + 1 126 | return np.log(specgram[:ind, :] + eps) 127 | 128 | def _specgram_real(self, samples, window_size, stride_size, sample_rate=16000): 129 | """Compute the spectrogram for samples from a real signal.""" 130 | # extract strided windows 131 | truncate_size = (len(samples) - window_size) % stride_size 132 | samples = samples[:len(samples) - truncate_size] 133 | nshape = (window_size, (len(samples) - window_size) // stride_size + 1) 134 | nstrides = (samples.strides[0], samples.strides[0] * stride_size) 135 | windows = np.lib.stride_tricks.as_strided( 136 | samples, shape=nshape, strides=nstrides) 137 | assert np.all( 138 | windows[:, 1] == samples[stride_size:(stride_size + window_size)]) 139 | # window weighting, squared Fast Fourier Transform (fft), scaling 140 | weighting = np.hanning(window_size)[:, None] 141 | fft = np.fft.rfft(windows * weighting, axis=0) 142 | fft = np.absolute(fft) 143 | fft = fft**2 144 | scale = np.sum(weighting**2) * sample_rate 145 | fft[1:-1, :] *= (2.0 / scale) 146 | fft[(0, -1), :] /= scale 147 | # prepare fft frequency list 148 | freqs = float(sample_rate) / window_size * np.arange(fft.shape[0]) 149 | return fft, freqs 150 | 151 | def _compute_mfcc(self, 152 | samples, 153 | sample_rate, 154 | stride_ms=10.0, 155 | window_ms=20.0, 156 | max_freq=None): 157 | """Compute mfcc from samples.""" 158 | if max_freq is None: 159 | max_freq = sample_rate / 2 160 | if max_freq > sample_rate / 2: 161 | raise ValueError("max_freq must not be greater than half of " 162 | "sample rate.") 163 | if stride_ms > window_ms: 164 | raise ValueError("Stride size must not be greater than " 165 | "window size.") 166 | # compute the 13 cepstral coefficients, and the first one is replaced 167 | # by log(frame energy) 168 | mfcc_feat = mfcc( 169 | signal=samples, 170 | samplerate=sample_rate, 171 | winlen=0.001 * window_ms, 172 | winstep=0.001 * stride_ms, 173 | highfreq=max_freq) 174 | # Deltas 175 | d_mfcc_feat = delta(mfcc_feat, 2) 176 | # Deltas-Deltas 177 | dd_mfcc_feat = delta(d_mfcc_feat, 2) 178 | # transpose 179 | mfcc_feat = np.transpose(mfcc_feat) 180 | d_mfcc_feat = np.transpose(d_mfcc_feat) 181 | dd_mfcc_feat = np.transpose(dd_mfcc_feat) 182 | # concat above three features 183 | concat_mfcc_feat = np.concatenate( 184 | (mfcc_feat, d_mfcc_feat, dd_mfcc_feat)) 185 | return concat_mfcc_feat 186 | -------------------------------------------------------------------------------- /data_utils/audio_featurizer.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/data_utils/audio_featurizer.pyc -------------------------------------------------------------------------------- /data_utils/build_vocab.py: -------------------------------------------------------------------------------- 1 | """Build vocabulary from manifest files. 2 | 3 | Each item in vocabulary file is a character. 4 | """ 5 | 6 | from __future__ import absolute_import 7 | from __future__ import division 8 | 9 | import argparse 10 | import functools 11 | import codecs 12 | import json 13 | from collections import Counter 14 | import os.path 15 | import sys 16 | 17 | sys.path.append(os.path.dirname(__file__) + "/../") 18 | from data_utils.utility import read_manifest 19 | from utils.utility import add_arguments, print_arguments 20 | 21 | parser = argparse.ArgumentParser(description=__doc__) 22 | add_arg = functools.partial(add_arguments, argparser=parser) 23 | # yapf: disable 24 | add_arg('count_threshold', int, 0, "Truncation threshold for char counts.") 25 | add_arg('vocab_path', str, 26 | 'data/aishell/vocab.txt', 27 | "Filepath to write the vocabulary.") 28 | add_arg('manifest_paths', str, 29 | None, 30 | "Filepaths of manifests for building vocabulary. " 31 | "You can provide multiple manifest files.", 32 | nargs='+', 33 | required=True) 34 | # yapf: disable 35 | args = parser.parse_args() 36 | 37 | 38 | def count_manifest(counter, manifest_path): 39 | manifest_jsons = read_manifest(manifest_path) 40 | for line_json in manifest_jsons: 41 | for char in line_json['text']: 42 | counter.update(char) 43 | 44 | 45 | def main(): 46 | print_arguments(args) 47 | 48 | counter = Counter() 49 | for manifest_path in args.manifest_paths: 50 | count_manifest(counter, manifest_path) 51 | 52 | count_sorted = sorted(counter.items(), key=lambda x: x[1], reverse=True) 53 | with codecs.open(args.vocab_path, 'w', 'utf-8') as fout: 54 | for char, count in count_sorted: 55 | if count < args.count_threshold: break 56 | fout.write(char + '\n') 57 | 58 | 59 | if __name__ == '__main__': 60 | main() 61 | -------------------------------------------------------------------------------- /data_utils/compute_mean_std.py: -------------------------------------------------------------------------------- 1 | """Compute mean and std for feature normalizer, and save to file.""" 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | 6 | import sys, os 7 | import argparse 8 | import functools 9 | 10 | sys.path.append(os.path.dirname(__file__) + "/../") 11 | from data_utils.normalizer import FeatureNormalizer 12 | from data_utils.audio_featurizer import AudioFeaturizer 13 | from utils.utility import add_arguments, print_arguments 14 | 15 | parser = argparse.ArgumentParser(description=__doc__) 16 | add_arg = functools.partial(add_arguments, argparser=parser) 17 | # yapf: disable 18 | add_arg('num_samples', int, 2000, "# of samples to for statistics.") 19 | add_arg('specgram_type', str, 20 | 'linear', 21 | "Audio feature type. Options: linear, mfcc.", 22 | choices=['linear', 'mfcc']) 23 | add_arg('manifest_path', str, 24 | 'data/aishell/manifest.train', 25 | "Filepath of manifest to compute normalizer's mean and stddev.") 26 | add_arg('output_path', str, 27 | 'data/aishell/mean_std.npz', 28 | "Filepath of write mean and stddev to (.npz).") 29 | # yapf: disable 30 | args = parser.parse_args() 31 | 32 | 33 | def main(): 34 | print_arguments(args) 35 | 36 | audio_featurizer = AudioFeaturizer(specgram_type=args.specgram_type) 37 | 38 | def augment_and_featurize(audio_segment): 39 | return audio_featurizer.featurize(audio_segment) 40 | 41 | normalizer = FeatureNormalizer( 42 | mean_std_filepath=None, 43 | manifest_path=args.manifest_path, 44 | featurize_func=augment_and_featurize, 45 | num_samples=args.num_samples) 46 | normalizer.write_to_file(args.output_path) 47 | 48 | 49 | if __name__ == '__main__': 50 | main() 51 | -------------------------------------------------------------------------------- /data_utils/normalizer.py: -------------------------------------------------------------------------------- 1 | """Contains feature normalizers.""" 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | 6 | import numpy as np 7 | import random 8 | from data_utils.utility import read_manifest 9 | from data_utils.audio import AudioSegment 10 | 11 | 12 | class FeatureNormalizer(object): 13 | """Feature normalizer. Normalize features to be of zero mean and unit 14 | stddev. 15 | 16 | if mean_std_filepath is provided (not None), the normalizer will directly 17 | initilize from the file. Otherwise, both manifest_path and featurize_func 18 | should be given for on-the-fly mean and stddev computing. 19 | 20 | :param mean_std_filepath: File containing the pre-computed mean and stddev. 21 | :type mean_std_filepath: None|basestring 22 | :param manifest_path: Manifest of instances for computing mean and stddev. 23 | :type meanifest_path: None|basestring 24 | :param featurize_func: Function to extract features. It should be callable 25 | with ``featurize_func(audio_segment)``. 26 | :type featurize_func: None|callable 27 | :param num_samples: Number of random samples for computing mean and stddev. 28 | :type num_samples: int 29 | :param random_seed: Random seed for sampling instances. 30 | :type random_seed: int 31 | :raises ValueError: If both mean_std_filepath and manifest_path 32 | (or both mean_std_filepath and featurize_func) are None. 33 | """ 34 | 35 | def __init__(self, 36 | mean_std_filepath, 37 | manifest_path=None, 38 | featurize_func=None, 39 | num_samples=500, 40 | random_seed=0): 41 | if not mean_std_filepath: 42 | if not (manifest_path and featurize_func): 43 | raise ValueError("If mean_std_filepath is None, meanifest_path " 44 | "and featurize_func should not be None.") 45 | self._rng = random.Random(random_seed) 46 | self._compute_mean_std(manifest_path, featurize_func, num_samples) 47 | else: 48 | self._read_mean_std_from_file(mean_std_filepath) 49 | 50 | def apply(self, features, eps=1e-14): 51 | """Normalize features to be of zero mean and unit stddev. 52 | 53 | :param features: Input features to be normalized. 54 | :type features: ndarray 55 | :param eps: added to stddev to provide numerical stablibity. 56 | :type eps: float 57 | :return: Normalized features. 58 | :rtype: ndarray 59 | """ 60 | return (features - self._mean) / (self._std + eps) 61 | 62 | def write_to_file(self, filepath): 63 | """Write the mean and stddev to the file. 64 | 65 | :param filepath: File to write mean and stddev. 66 | :type filepath: basestring 67 | """ 68 | np.savez(filepath, mean=self._mean, std=self._std) 69 | 70 | def _read_mean_std_from_file(self, filepath): 71 | """Load mean and std from file.""" 72 | npzfile = np.load(filepath) 73 | self._mean = npzfile["mean"] 74 | self._std = npzfile["std"] 75 | 76 | def _compute_mean_std(self, manifest_path, featurize_func, num_samples): 77 | """Compute mean and std from randomly sampled instances.""" 78 | manifest = read_manifest(manifest_path) 79 | print("num_samples: ", num_samples) 80 | sampled_manifest = self._rng.sample(manifest, num_samples) 81 | features = [] 82 | for instance in sampled_manifest: 83 | features.append( 84 | featurize_func( 85 | AudioSegment.from_file(instance["audio_filepath"]))) 86 | features = np.hstack(features) 87 | self._mean = np.mean(features, axis=1).reshape([-1, 1]) 88 | self._std = np.std(features, axis=1).reshape([-1, 1]) 89 | -------------------------------------------------------------------------------- /data_utils/normalizer.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/data_utils/normalizer.pyc -------------------------------------------------------------------------------- /data_utils/process_manifest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | """ Data pre-process """ 5 | 6 | import json 7 | from collections import Counter 8 | 9 | def get_path_trans(manifest_path="data/aishell/manifest.train" ): 10 | '''Get path_to_wav and transcript list from data/manifest.{train,dev,test} 11 | 12 | :param manifest_path: path to manifest file 13 | :type manifest: str 14 | return 15 | ''' 16 | 17 | path_to_wav = [] 18 | transcript = [] 19 | duration = [] 20 | lines = open(manifest_path, "r").readlines() 21 | for line in lines: 22 | man_dict = json.loads(line) 23 | path_to_wav.append(man_dict["audio_filepath"]) 24 | transcript.append(man_dict["text"]) 25 | duration.append(man_dict["duration"]) 26 | return path_to_wav, transcript, duration 27 | 28 | def create_dict(vocab): 29 | '''Creat word dict and map from word to num 30 | 31 | :param vocab: path to vocab.txt 32 | :type vocab: str 33 | return 34 | ''' 35 | 36 | total_words = open(vocab, 'r').readlines() 37 | total_words = [word.strip() for word in total_words] 38 | counter = Counter(total_words) 39 | words = sorted(counter) 40 | word_size = len(words) 41 | word_num_map = dict(zip(words, range(word_size))) 42 | 43 | print "word_size: ", word_size 44 | return word_size, words, word_num_map 45 | 46 | if __name__ == "__main__": 47 | get_path_trans("../data/aishell/manifest.test") 48 | creat_dict("../data/aishell/vocab.txt") 49 | -------------------------------------------------------------------------------- /data_utils/process_manifest.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/data_utils/process_manifest.pyc -------------------------------------------------------------------------------- /data_utils/speech.py: -------------------------------------------------------------------------------- 1 | """Contains the speech segment class.""" 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | 6 | from data_utils.audio import AudioSegment 7 | 8 | 9 | class SpeechSegment(AudioSegment): 10 | """Speech segment abstraction, a subclass of AudioSegment, 11 | with an additional transcript. 12 | 13 | :param samples: Audio samples [num_samples x num_channels]. 14 | :type samples: ndarray.float32 15 | :param sample_rate: Audio sample rate. 16 | :type sample_rate: int 17 | :param transcript: Transcript text for the speech. 18 | :type transript: basestring 19 | :raises TypeError: If the sample data type is not float or int. 20 | """ 21 | 22 | def __init__(self, samples, sample_rate, transcript): 23 | AudioSegment.__init__(self, samples, sample_rate) 24 | self._transcript = transcript 25 | 26 | def __eq__(self, other): 27 | """Return whether two objects are equal. 28 | """ 29 | if not AudioSegment.__eq__(self, other): 30 | return False 31 | if self._transcript != other._transcript: 32 | return False 33 | return True 34 | 35 | def __ne__(self, other): 36 | """Return whether two objects are unequal.""" 37 | return not self.__eq__(other) 38 | 39 | @classmethod 40 | def from_file(cls, filepath, transcript): 41 | """Create speech segment from audio file and corresponding transcript. 42 | 43 | :param filepath: Filepath or file object to audio file. 44 | :type filepath: basestring|file 45 | :param transcript: Transcript text for the speech. 46 | :type transript: basestring 47 | :return: Speech segment instance. 48 | :rtype: SpeechSegment 49 | """ 50 | audio = AudioSegment.from_file(filepath) 51 | return cls(audio.samples, audio.sample_rate, transcript) 52 | 53 | @classmethod 54 | def from_bytes(cls, bytes, transcript): 55 | """Create speech segment from a byte string and corresponding 56 | transcript. 57 | 58 | :param bytes: Byte string containing audio samples. 59 | :type bytes: str 60 | :param transcript: Transcript text for the speech. 61 | :type transript: basestring 62 | :return: Speech segment instance. 63 | :rtype: Speech Segment 64 | """ 65 | audio = AudioSegment.from_bytes(bytes) 66 | return cls(audio.samples, audio.sample_rate, transcript) 67 | 68 | @classmethod 69 | def concatenate(cls, *segments): 70 | """Concatenate an arbitrary number of speech segments together, both 71 | audio and transcript will be concatenated. 72 | 73 | :param *segments: Input speech segments to be concatenated. 74 | :type *segments: tuple of SpeechSegment 75 | :return: Speech segment instance. 76 | :rtype: SpeechSegment 77 | :raises ValueError: If the number of segments is zero, or if the 78 | sample_rate of any two segments does not match. 79 | :raises TypeError: If any segment is not SpeechSegment instance. 80 | """ 81 | if len(segments) == 0: 82 | raise ValueError("No speech segments are given to concatenate.") 83 | sample_rate = segments[0]._sample_rate 84 | transcripts = "" 85 | for seg in segments: 86 | if sample_rate != seg._sample_rate: 87 | raise ValueError("Can't concatenate segments with " 88 | "different sample rates") 89 | if type(seg) is not cls: 90 | raise TypeError("Only speech segments of the same type " 91 | "instance can be concatenated.") 92 | transcripts += seg._transcript 93 | samples = np.concatenate([seg.samples for seg in segments]) 94 | return cls(samples, sample_rate, transcripts) 95 | 96 | @classmethod 97 | def slice_from_file(cls, filepath, transcript, start=None, end=None): 98 | """Loads a small section of an speech without having to load 99 | the entire file into the memory which can be incredibly wasteful. 100 | 101 | :param filepath: Filepath or file object to audio file. 102 | :type filepath: basestring|file 103 | :param start: Start time in seconds. If start is negative, it wraps 104 | around from the end. If not provided, this function 105 | reads from the very beginning. 106 | :type start: float 107 | :param end: End time in seconds. If end is negative, it wraps around 108 | from the end. If not provided, the default behvaior is 109 | to read to the end of the file. 110 | :type end: float 111 | :param transcript: Transcript text for the speech. if not provided, 112 | the defaults is an empty string. 113 | :type transript: basestring 114 | :return: SpeechSegment instance of the specified slice of the input 115 | speech file. 116 | :rtype: SpeechSegment 117 | """ 118 | audio = AudioSegment.slice_from_file(filepath, start, end) 119 | return cls(audio.samples, audio.sample_rate, transcript) 120 | 121 | @classmethod 122 | def make_silence(cls, duration, sample_rate): 123 | """Creates a silent speech segment of the given duration and 124 | sample rate, transcript will be an empty string. 125 | 126 | :param duration: Length of silence in seconds. 127 | :type duration: float 128 | :param sample_rate: Sample rate. 129 | :type sample_rate: float 130 | :return: Silence of the given duration. 131 | :rtype: SpeechSegment 132 | """ 133 | audio = AudioSegment.make_silence(duration, sample_rate) 134 | return cls(audio.samples, audio.sample_rate, "") 135 | 136 | @property 137 | def transcript(self): 138 | """Return the transcript text. 139 | 140 | :return: Transcript text for the speech. 141 | :rtype: basestring 142 | """ 143 | return self._transcript 144 | -------------------------------------------------------------------------------- /data_utils/speech.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/data_utils/speech.pyc -------------------------------------------------------------------------------- /data_utils/utility.py: -------------------------------------------------------------------------------- 1 | """Contains data helper functions.""" 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | 6 | import json 7 | import codecs 8 | import os 9 | import tarfile 10 | import time 11 | import md5 12 | from Queue import Queue 13 | from threading import Thread 14 | from multiprocessing import Process, Manager, Value 15 | 16 | 17 | def read_manifest(manifest_path, max_duration=float('inf'), min_duration=0.0): 18 | """Load and parse manifest file. 19 | 20 | Instances with durations outside [min_duration, max_duration] will be 21 | filtered out. 22 | 23 | :param manifest_path: Manifest file to load and parse. 24 | :type manifest_path: basestring 25 | :param max_duration: Maximal duration in seconds for instance filter. 26 | :type max_duration: float 27 | :param min_duration: Minimal duration in seconds for instance filter. 28 | :type min_duration: float 29 | :return: Manifest parsing results. List of dict. 30 | :rtype: list 31 | :raises IOError: If failed to parse the manifest. 32 | """ 33 | manifest = [] 34 | for json_line in codecs.open(manifest_path, 'r', 'utf-8'): 35 | try: 36 | json_data = json.loads(json_line) 37 | except Exception as e: 38 | raise IOError("Error reading manifest: %s" % str(e)) 39 | if (json_data["duration"] <= max_duration and 40 | json_data["duration"] >= min_duration): 41 | manifest.append(json_data) 42 | return manifest 43 | 44 | 45 | def getfile_insensitive(path): 46 | """Get the actual file path when given insensitive filename.""" 47 | directory, filename = os.path.split(path) 48 | directory, filename = (directory or '.'), filename.lower() 49 | for f in os.listdir(directory): 50 | newpath = os.path.join(directory, f) 51 | if os.path.isfile(newpath) and f.lower() == filename: 52 | return newpath 53 | 54 | 55 | def download_multi(url, target_dir, extra_args): 56 | """Download multiple files from url to target_dir.""" 57 | if not os.path.exists(target_dir): os.makedirs(target_dir) 58 | print("Downloading %s ..." % url) 59 | ret_code = os.system("wget -c " + url + ' ' + extra_args + " -P " + 60 | target_dir) 61 | return ret_code 62 | 63 | 64 | def download(url, md5sum, target_dir): 65 | """Download file from url to target_dir, and check md5sum.""" 66 | if not os.path.exists(target_dir): os.makedirs(target_dir) 67 | filepath = os.path.join(target_dir, url.split("/")[-1]) 68 | m1 = md5.new() 69 | m1.update(filepath) 70 | print ("adsadas", m1.hexdigest()) 71 | if not (os.path.exists(filepath) and m1.hexdigest() == md5sum): 72 | print("Downloading %s ..." % url) 73 | os.system("wget -c " + url + " -P " + target_dir) 74 | print("\nMD5 Chesksum %s ..." % filepath) 75 | if not m1.hexdigest() == md5sum: 76 | raise RuntimeError("MD5 checksum failed.") 77 | else: 78 | print("File exists, skip downloading. (%s)" % filepath) 79 | return filepath 80 | 81 | 82 | def unpack(filepath, target_dir, rm_tar=False): 83 | """Unpack the file to the target_dir.""" 84 | print("Unpacking %s ..." % filepath) 85 | tar = tarfile.open(filepath) 86 | tar.extractall(target_dir) 87 | tar.close() 88 | if rm_tar == True: 89 | os.remove(filepath) 90 | 91 | 92 | class XmapEndSignal(): 93 | pass 94 | 95 | 96 | def xmap_readers_mp(mapper, reader, process_num, buffer_size, order=False): 97 | """A multiprocessing pipeline wrapper for the data reader. 98 | 99 | :param mapper: Function to map sample. 100 | :type mapper: callable 101 | :param reader: Given data reader. 102 | :type reader: callable 103 | :param process_num: Number of processes in the pipeline 104 | :type process_num: int 105 | :param buffer_size: Maximal buffer size. 106 | :type buffer_size: int 107 | :return: The wrappered reader and cleanup callback 108 | :rtype: tuple 109 | """ 110 | end_flag = XmapEndSignal() 111 | 112 | read_workers = [] 113 | handle_workers = [] 114 | flush_workers = [] 115 | 116 | read_exit_flag = Value('i', 0) 117 | handle_exit_flag = Value('i', 0) 118 | flush_exit_flag = Value('i', 0) 119 | 120 | # define a worker to read samples from reader to in_queue with order flag 121 | def order_read_worker(reader, in_queue): 122 | for order_id, sample in enumerate(reader()): 123 | if read_exit_flag.value == 1: break 124 | in_queue.put((order_id, sample)) 125 | in_queue.put(end_flag) 126 | # the reading worker should not exit until all handling work exited 127 | while handle_exit_flag.value == 0 or read_exit_flag.value == 0: 128 | time.sleep(0.001) 129 | 130 | # define a worker to handle samples from in_queue by mapper and put results 131 | # to out_queue with order 132 | def order_handle_worker(in_queue, out_queue, mapper, out_order): 133 | ins = in_queue.get() 134 | while not isinstance(ins, XmapEndSignal): 135 | if handle_exit_flag.value == 1: break 136 | order_id, sample = ins 137 | result = mapper(sample) 138 | while order_id != out_order[0]: 139 | time.sleep(0.001) 140 | out_queue.put(result) 141 | out_order[0] += 1 142 | ins = in_queue.get() 143 | in_queue.put(end_flag) 144 | out_queue.put(end_flag) 145 | # wait for exit of flushing worker 146 | while flush_exit_flag.value == 0 or handle_exit_flag.value == 0: 147 | time.sleep(0.001) 148 | read_exit_flag.value = 1 149 | handle_exit_flag.value = 1 150 | 151 | # define a thread worker to flush samples from Manager.Queue to Queue 152 | # for acceleration 153 | def flush_worker(in_queue, out_queue): 154 | finish = 0 155 | while finish < process_num and flush_exit_flag.value == 0: 156 | sample = in_queue.get() 157 | if isinstance(sample, XmapEndSignal): 158 | finish += 1 159 | else: 160 | out_queue.put(sample) 161 | out_queue.put(end_flag) 162 | handle_exit_flag.value = 1 163 | flush_exit_flag.value = 1 164 | 165 | def cleanup(): 166 | # first exit flushing workers 167 | flush_exit_flag.value = 1 168 | for w in flush_workers: 169 | w.join() 170 | # next exit handling workers 171 | handle_exit_flag.value = 1 172 | for w in handle_workers: 173 | w.join() 174 | # last exit reading workers 175 | read_exit_flag.value = 1 176 | for w in read_workers: 177 | w.join() 178 | 179 | def xreader(): 180 | # prepare shared memory 181 | manager = Manager() 182 | in_queue = manager.Queue(buffer_size) 183 | out_queue = manager.Queue(buffer_size) 184 | out_order = manager.list([0]) 185 | 186 | # start a read worker in a process 187 | target = order_read_worker 188 | p = Process(target=target, args=(reader, in_queue)) 189 | p.daemon = True 190 | p.start() 191 | read_workers.append(p) 192 | 193 | # start handle_workers with multiple processes 194 | target = order_handle_worker 195 | args = (in_queue, out_queue, mapper, out_order) 196 | workers = [ 197 | Process(target=target, args=args) for _ in xrange(process_num) 198 | ] 199 | for w in workers: 200 | w.daemon = True 201 | w.start() 202 | handle_workers.append(w) 203 | 204 | # start a thread to read data from slow Manager.Queue 205 | flush_queue = Queue(buffer_size) 206 | t = Thread(target=flush_worker, args=(out_queue, flush_queue)) 207 | t.daemon = True 208 | t.start() 209 | flush_workers.append(t) 210 | 211 | # get results 212 | sample = flush_queue.get() 213 | while not isinstance(sample, XmapEndSignal): 214 | yield sample 215 | sample = flush_queue.get() 216 | 217 | return xreader, cleanup 218 | -------------------------------------------------------------------------------- /data_utils/utility.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/data_utils/utility.pyc -------------------------------------------------------------------------------- /data_utils/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | 5 | import os 6 | from collections import Counter 7 | 8 | import numpy as np 9 | import scipy.io.wavfile as wav 10 | from python_speech_features import mfcc 11 | from data_utils.audio_featurizer import AudioFeaturizer 12 | from data_utils.speech import SpeechSegment 13 | from data_utils.normalizer import FeatureNormalizer 14 | from conf.hyparam import Config 15 | 16 | ''' 17 | To help creat train set and get batch from data 18 | ''' 19 | 20 | def next_batch(start_idx=0, 21 | batch_size=1, 22 | n_input=None, 23 | n_context=None, 24 | labels=None, 25 | wav_files=None, 26 | word_num_map=None, 27 | specgram_type='mfcc'): 28 | """ Get data batch for training 29 | 30 | :param start_idx: 31 | :param batch_size: 32 | :param n_input: 33 | :param n_context: 34 | :param labels: 35 | :param wav_files: 36 | :param word_num_map: 37 | :param specgram_type 38 | :return: 39 | """ 40 | filesize = len(labels) 41 | end_idx = min(filesize, start_idx + batch_size) 42 | idx_list = range(start_idx, end_idx) 43 | txt_labels = [labels[i] for i in idx_list] 44 | wav_files = [wav_files[i] for i in idx_list] 45 | audio_features, audio_features_len, text_vector, text_vector_len = get_audio_mfcc_features(None, 46 | wav_files, 47 | n_input, 48 | n_context, 49 | word_num_map, 50 | txt_labels, 51 | specgram_type) 52 | 53 | start_idx += batch_size 54 | # confirm start_idx 55 | if start_idx >= filesize: 56 | start_idx = -1 57 | 58 | # use 0 padding when serveral inputs 59 | audio_features, audio_features_len = pad_sequences(audio_features) 60 | sparse_labels = sparse_tuple_from(text_vector) 61 | 62 | return start_idx, audio_features, audio_features_len, sparse_labels, wav_files 63 | 64 | 65 | def get_audio_mfcc_features(txt_files, wav_files, n_input, 66 | n_context, word_num_map, txt_labels=None, 67 | specgram_type='mfcc', mean_std_filepath='data/aishell/mean_std.npz'): 68 | """ Get MFCC/linear specgram features. The dim of MFCC is 39, contains 13 mfcc + 13 delta1 + 13 delta2. 69 | Linear specgram contains 161 features in different frequency section. 70 | 71 | :param txt_files: 72 | :param wav_files: 73 | :param n_input: 74 | :param n_context: 75 | :param word_num_map: 76 | :param txt_labels: 77 | :return: 78 | """ 79 | audio_features = [] 80 | audio_features_len = [] 81 | text_vector = [] 82 | text_vector_len = [] 83 | if txt_files != None: 84 | txt_labels = txt_files 85 | get_feature = AudioFeaturizer(specgram_type) 86 | normalizer = FeatureNormalizer(mean_std_filepath) 87 | for txt_obj, wav_file in zip(txt_labels, wav_files): 88 | # Turn inputs into features 89 | if specgram_type == 'mfcc': 90 | audio_data = audiofile_to_input_vector(wav_file, n_input, n_context) # get mfcc feature ( ???, 741 ) 91 | elif specgram_type == 'linear': 92 | speech_segment = SpeechSegment.from_file(wav_file, "") 93 | specgram = get_feature.featurize(speech_segment) 94 | audio_data = normalizer.apply(specgram) 95 | audio_data = np.transpose(audio_data) # get linear specgram feature, (?, 161) 96 | audio_data = audio_data.astype('float32') 97 | 98 | audio_features.append(audio_data) 99 | audio_features_len.append(np.int32(len(audio_data))) 100 | 101 | target = [] 102 | if txt_files != None: # txt_obj是文件 103 | target = trans_text_ch_to_vector(txt_obj, word_num_map) 104 | else: 105 | target = trans_text_ch_to_vector(None, word_num_map, txt_obj) # txt_obj是labels 106 | text_vector.append(target) 107 | text_vector_len.append(len(target)) 108 | 109 | audio_features = np.asarray(audio_features) 110 | audio_features_len = np.asarray(audio_features_len) 111 | text_vector = np.asarray(text_vector) 112 | text_vector_len = np.asarray(text_vector_len) 113 | return audio_features, audio_features_len, text_vector, text_vector_len 114 | 115 | 116 | def sparse_tuple_from(sequences, dtype=np.int32): 117 | """ Turn dense matrix to sparse matrix 118 | 119 | :param sequences: 120 | :param dtype: 121 | :return: 122 | """ 123 | indices = [] 124 | values = [] 125 | 126 | for n, seq in enumerate(sequences): 127 | indices.extend(zip([n] * len(seq), range(len(seq)))) 128 | values.extend(seq) 129 | 130 | indices = np.asarray(indices, dtype=np.int64) 131 | values = np.asarray(values, dtype=dtype) 132 | shape = np.asarray([len(sequences), indices.max(0)[1] + 1], dtype=np.int64) 133 | 134 | return indices, values, shape 135 | 136 | 137 | def trans_text_ch_to_vector(txt_file, word_num_map, txt_label=None): 138 | """ Trans chinese chars to vector 139 | 140 | :param txt_file: 141 | :param word_num_map: 142 | :param txt_label: 143 | :return: 144 | """ 145 | words_size = len(word_num_map) 146 | 147 | to_num = lambda word: word_num_map.get(word.encode('utf-8'), words_size) 148 | 149 | if txt_file != None: 150 | txt_label = get_ch_lable(txt_file) 151 | 152 | labels_vector = list(map(to_num, txt_label)) 153 | return labels_vector 154 | 155 | 156 | def get_ch_lable(txt_file): 157 | labels = "" 158 | with open(txt_file, 'rb') as f: 159 | for label in f: 160 | labels = labels + label.decode('gb2312') 161 | return labels 162 | 163 | 164 | def trans_tuple_to_texts_ch(tuple, words): 165 | """ Trans vector to chars 166 | 167 | :param tuple: 168 | :param words: 169 | :return: 170 | """ 171 | indices = tuple[0] 172 | values = tuple[1] 173 | results = [''] * tuple[2][0] 174 | for i in range(len(indices)): 175 | index = indices[i][0] 176 | c = values[i] 177 | c = ' ' if c == 0 else words[c] # chr(c + FIRST_INDEX) 178 | results[index] = results[index] + c 179 | 180 | return results 181 | 182 | 183 | def trans_array_to_text_ch(value, words): 184 | results = '' 185 | for i in range(len(value)): 186 | results += words[value[i]] # chr(value[i] + FIRST_INDEX) 187 | return results.replace('`', ' ') 188 | 189 | 190 | def audiofile_to_input_vector(audio_filename, n_input, n_context): 191 | """ Compute MFCC features with n_context 192 | 193 | :param audio_filename: 194 | :param n_input: 195 | :param n_context: 196 | :return: 197 | """ 198 | fs, audio = wav.read(audio_filename) 199 | 200 | # get mfcc features with dim 39 201 | get_feature = AudioFeaturizer("mfcc") 202 | speech_segment = SpeechSegment.from_file(audio_filename, "") 203 | orig_inputs = get_feature.featurize(speech_segment) # (39, ?) 204 | orig_inputs = np.transpose(orig_inputs) # trans to time major (?, 39) 205 | 206 | 207 | train_inputs = np.zeros((orig_inputs.shape[0], n_input + 2 * n_input * n_context)) #(***/2, 195) 208 | empty_mfcc = np.zeros((n_input)) 209 | 210 | # Prepare input data, consist of three parts, 211 | # output is (past hyparam.n_context * 39 + current + future hyparam.n_context * 39) 212 | time_slices = range(train_inputs.shape[0]) 213 | context_past_min = time_slices[0] + n_context 214 | context_future_max = time_slices[-1] - n_context 215 | for time_slice in time_slices: 216 | # padding with 0 for the first of 9,mfcc features 217 | need_empty_past = max(0, (context_past_min - time_slice)) 218 | empty_source_past = list(empty_mfcc for empty_slots in range(need_empty_past)) 219 | data_source_past = orig_inputs[ max(0, time_slice - n_context):time_slice] 220 | 221 | # padding with 0 for the last of 9,mfcc features 222 | need_empty_future = max(0, (time_slice - context_future_max)) 223 | empty_source_future = list(empty_mfcc for empty_slots in range(need_empty_future)) 224 | data_source_future = orig_inputs[time_slice + 1:time_slice + n_context + 1] 225 | 226 | if need_empty_past: 227 | past = np.concatenate((empty_source_past, data_source_past)) 228 | else: 229 | past = data_source_past 230 | 231 | if need_empty_future: 232 | future = np.concatenate((data_source_future, empty_source_future)) 233 | else: 234 | future = data_source_future 235 | 236 | past = np.reshape(past, n_context * 39) 237 | now = orig_inputs[time_slice] 238 | future = np.reshape(future, n_context * n_input) 239 | train_inputs[time_slice] = np.concatenate((past, now, future)) 240 | 241 | # Tran data to Norm distribution, minus mean value then over the varr 242 | train_inputs = (train_inputs - np.mean(train_inputs)) / np.std(train_inputs) 243 | 244 | # shape of train_inputs: (shape(orig_inputs)/2, n_context * 2 * 39 + 39) 245 | return train_inputs 246 | 247 | 248 | def pad_sequences(sequences, maxlen=None, dtype=np.float32, 249 | padding='post', truncating='post', value=0.): 250 | """ Padding data with 0 251 | 252 | :param sequences: 253 | :param maxlen: 254 | :param dtype: 255 | :param padding: 256 | :param truncating: 257 | :param value: 258 | :return: 259 | """ 260 | sequences_each_len = np.asarray([len(s) for s in sequences], dtype=np.int64) 261 | 262 | nb_samples = len(sequences) 263 | if maxlen is None: 264 | maxlen = np.max(sequences_each_len) 265 | 266 | sample_shape = tuple() 267 | for s in sequences: 268 | if len(s) > 0: 269 | sample_shape = np.asarray(s).shape[1:] 270 | break 271 | 272 | x = (np.ones((nb_samples, maxlen) + sample_shape) * value).astype(dtype) 273 | for idx, s in enumerate(sequences): 274 | if len(s) == 0: 275 | continue 276 | if truncating == 'pre': 277 | trunc = s[-maxlen:] 278 | elif truncating == 'post': 279 | trunc = s[:maxlen] 280 | else: 281 | raise ValueError('Truncating type "%s" not understood' % truncating) 282 | 283 | # check `trunc` has expected shape 284 | trunc = np.asarray(trunc, dtype=dtype) 285 | if trunc.shape[1:] != sample_shape: 286 | raise ValueError('Shape of sample %s of sequence at position %s is different from expected shape %s' % 287 | (trunc.shape[1:], idx, sample_shape)) 288 | 289 | if padding == 'post': 290 | x[idx, :len(trunc)] = trunc 291 | elif padding == 'pre': 292 | x[idx, -len(trunc):] = trunc 293 | else: 294 | raise ValueError('Padding type "%s" not understood' % padding) 295 | return x, sequences_each_len 296 | 297 | 298 | if __name__ == "__main__": 299 | 300 | print("") 301 | -------------------------------------------------------------------------------- /data_utils/utils.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/data_utils/utils.pyc -------------------------------------------------------------------------------- /demo_cache/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/demo_cache/__init__.py -------------------------------------------------------------------------------- /demo_client.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CUDA_VISIBLE_DEVICES=0 python -u deploy_demo/demo_client.py --host_ip '10.3.27.97' --host_port 8086 4 | -------------------------------------------------------------------------------- /demo_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CUDA_VISIBLE_DEVICES=0 \ 4 | python deploy_demo/demo_server.py \ 5 | --host_ip 10.3.27.97 \ 6 | --host_port 8086 7 | -------------------------------------------------------------------------------- /deploy_demo/_init_paths.py: -------------------------------------------------------------------------------- 1 | """Set up paths for DS2""" 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | 6 | import os.path 7 | import sys 8 | 9 | 10 | def add_path(path): 11 | if path not in sys.path: 12 | sys.path.insert(0, path) 13 | 14 | 15 | this_dir = os.path.dirname(__file__) 16 | 17 | # Add project path to PYTHONPATH 18 | proj_path = os.path.join(this_dir, '..') 19 | add_path(proj_path) 20 | -------------------------------------------------------------------------------- /deploy_demo/_init_paths.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/deploy_demo/_init_paths.pyc -------------------------------------------------------------------------------- /deploy_demo/assert_zero.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | """ Check zero inputs for wav_files to avoid core dumped error """ 5 | 6 | import os 7 | import wave 8 | from time import sleep 9 | import numpy as np 10 | 11 | SUCCESS = 0 12 | FAIL = 1 13 | 14 | def ZCR(curFrame): 15 | # Zero crossing rate 16 | tmp1 = curFrame[:-1] 17 | tmp2 = curFrame[1:] 18 | sings = (tmp1 * tmp2 <= 0) 19 | diffs = (tmp1 - tmp2) > 0.02 20 | zcr = np.sum(sings * diffs) 21 | return zcr 22 | 23 | 24 | def STE(curFrame): 25 | # short time energy 26 | amp = np.sum(np.abs(curFrame)) 27 | return amp 28 | 29 | 30 | class Vad(object): 31 | def __init__(self): 32 | # 初始短时能量高门限 33 | self.amp1 = 140 34 | # 初始短时能量低门限 35 | self.amp2 = 120 36 | # 初始短时过零率高门限 37 | self.zcr1 = 10 38 | # 初始短时过零率低门限 39 | self.zcr2 = 5 40 | # 允许最大静音长度 41 | self.maxsilence = 100 42 | # 语音的最短长度 43 | self.minlen = 40 44 | # 偏移值 45 | self.offsets = 40 46 | self.offsete = 40 47 | # 能量最大值 48 | self.max_en = 20000 49 | # 初始状态为静音 50 | self.status = 0 51 | self.count = 0 52 | self.silence = 0 53 | self.frame_len = 256 54 | self.frame_inc = 128 55 | self.cur_status = 0 56 | self.frames = [] 57 | # 数据开始偏移 58 | self.frames_start = [] 59 | self.frames_start_num = 0 60 | # 数据结束偏移 61 | self.frames_end = [] 62 | self.frames_end_num = 0 63 | # 缓存数据 64 | self.cache_frames = [] 65 | self.cache = "" 66 | # 最大缓存长度 67 | self.cache_frames_num = 0 68 | self.end_flag = False 69 | self.wait_flag = False 70 | self.on = True 71 | self.callback = None 72 | self.callback_res = [] 73 | self.callback_kwargs = {} 74 | 75 | def clean(self): 76 | self.frames = [] 77 | # 数据开始偏移 78 | self.frames_start = [] 79 | self.frames_start_num = 0 80 | # 数据结束偏移 81 | self.frames_end = [] 82 | self.frames_end_num = 0 83 | # 缓存数据 84 | self.cache_frames = [] 85 | # 最大缓存长度 86 | self.cache_frames_num = 0 87 | self.end_flag = False 88 | self.wait_flag = False 89 | 90 | def go(self): 91 | self.wait_flag = False 92 | 93 | def wait(self): 94 | self.wait_flag = True 95 | 96 | def stop(self): 97 | self.on = False 98 | 99 | def add(self, frame, wait=True): 100 | if wait: 101 | print 'wait' 102 | frame = self.cache + frame 103 | 104 | while len(frame) > self.frame_len: 105 | frame_block = frame[:self.frame_len] 106 | self.cache_frames.append(frame_block) 107 | frame = frame[self.frame_len:] 108 | if wait: 109 | self.cache = frame 110 | else: 111 | self.cache = "" 112 | self.cache_frames.append(-1) 113 | 114 | def run(self,hasNum): 115 | print "开始执行音频端点检测" 116 | is_zero = True 117 | step = self.frame_len - self.frame_inc 118 | num = 0 119 | while 1: 120 | # 开始端点 121 | # 获得音频文件数字信号 122 | if self.wait_flag: 123 | sleep(1) 124 | continue 125 | if len(self.cache_frames) < 2: 126 | sleep(0.05) 127 | continue 128 | 129 | if self.cache_frames[1] == -1: 130 | print '----------------没有声音--------------' 131 | break 132 | # 从缓存中读取音频数据 133 | record_stream = "".join(self.cache_frames[:2]) 134 | wave_data = np.fromstring(record_stream, dtype=np.int16) 135 | wave_data = wave_data * 1.0 / self.max_en 136 | data = wave_data[np.arange(0, self.frame_len)] 137 | speech_data = self.cache_frames.pop(0) 138 | # 获得音频过零率 139 | zcr = ZCR(data) 140 | # 获得音频的短时能量, 平方放大 141 | amp = STE(data) ** 2 142 | # 返回当前音频数据状态 143 | res = self.speech_status(amp, zcr) 144 | 145 | if res == 2: 146 | hasNum += 1 147 | 148 | if hasNum > 10: 149 | is_zero = False 150 | print '+++++++++++++++++++++++++有声音++++++++++++++++++++++++' 151 | break 152 | num = num + 1 153 | # 一段一段进行检测 154 | self.frames_start.append(speech_data) 155 | self.frames_start_num += 1 156 | if self.frames_start_num == self.offsets: 157 | # 开始音频开始的缓存部分 158 | self.frames_start.pop(0) 159 | self.frames_start_num -= 1 160 | if self.end_flag: 161 | # 当音频结束后进行后部缓存 162 | self.frames_end_num += 1 163 | # 下一段语音开始,或达到缓存阀值 164 | if res == 2 or self.frames_end_num == self.offsete: 165 | speech_stream = b"".join(self.frames + self.frames_end) 166 | self.callback_res.append(self.callback(speech_stream, **self.callback_kwargs)) 167 | 168 | # 数据环境初始化 169 | # self.clean() 170 | self.end_flag = False 171 | 172 | self.frames = [] 173 | self.frames_end_num = 0 174 | self.frames_end = [] 175 | 176 | self.frames_end.append(speech_data) 177 | if res == 2: 178 | if self.cur_status in [0, 1]: 179 | # 添加开始偏移数据到数据缓存 180 | self.frames.append(b"".join(self.frames_start)) 181 | # 添加当前的语音数据 182 | self.frames.append(speech_data) 183 | if res == 3: 184 | print '检测音频结束' 185 | self.frames.append(speech_data) 186 | # 开启音频结束标志 187 | self.end_flag = True 188 | 189 | self.cur_status = res 190 | return is_zero 191 | # return self.callback_res 192 | 193 | def speech_status(self, amp, zcr): 194 | status = 0 195 | # 0= 静音, 1= 可能开始, 2=确定进入语音段 196 | if self.cur_status in [0, 1]: 197 | # 确定进入语音段 198 | if amp > self.amp1: 199 | status = 2 200 | self.silence = 0 201 | self.count += 1 202 | # 可能处于语音段 203 | elif amp > self.amp2 or zcr > self.zcr2: 204 | status = 1 205 | self.count += 1 206 | # 静音状态 207 | else: 208 | status = 0 209 | self.count = 0 210 | self.count = 0 211 | # 2 = 语音段 212 | elif self.cur_status == 2: 213 | # 保持在语音段 214 | if amp > self.amp2 or zcr > self.zcr2: 215 | self.count += 1 216 | status = 2 217 | # 语音将结束 218 | else: 219 | # 静音还不够长,尚未结束 220 | self.silence += 1 221 | if self.silence < self.maxsilence: 222 | self.count += 1 223 | status = 2 224 | # 语音长度太短认为是噪声 225 | elif self.count < self.minlen: 226 | status = 0 227 | self.silence = 0 228 | self.count = 0 229 | # 语音结束 230 | else: 231 | status = 3 232 | self.silence = 0 233 | self.count = 0 234 | return status 235 | 236 | 237 | def read_file_data(filename): 238 | """ 239 | 输入:需要读取的文件名 240 | 返回:(声道,量化位数,采样率,数据) 241 | """ 242 | read_file = wave.open(filename, "r") 243 | params = read_file.getparams() 244 | nchannels, sampwidth, framerate, nframes = params[:4] 245 | data = read_file.readframes(nframes) 246 | return nchannels, sampwidth, framerate, data 247 | 248 | class FileParser(Vad): 249 | def __init__(self): 250 | self.block_size = 256 251 | Vad.__init__(self) 252 | def read_file(self, filename): 253 | if not os.path.isfile(filename): 254 | print "文件%s不存在" % filename 255 | return FAIL 256 | datas = read_file_data(filename)[-1] 257 | self.add(datas, False) 258 | 259 | if __name__ == "__main__": 260 | stream_test = FileParser() 261 | 262 | filename = '20180723021835_10.3.10.194.wav' 263 | result = stream_test.read_file(filename) 264 | if result != FAIL: 265 | print stream_test.run(0) 266 | if not stream_test.run(0): 267 | print "有声音" 268 | -------------------------------------------------------------------------------- /deploy_demo/assert_zero.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/deploy_demo/assert_zero.pyc -------------------------------------------------------------------------------- /deploy_demo/demo_client.py: -------------------------------------------------------------------------------- 1 | """Client-end for the ASR demo.""" 2 | from pynput import keyboard 3 | import struct 4 | import socket 5 | import sys 6 | import argparse 7 | import pyaudio 8 | 9 | parser = argparse.ArgumentParser(description=__doc__) 10 | parser.add_argument( 11 | "--host_ip", 12 | default="localhost", 13 | type=str, 14 | help="Server IP address. (default: %(default)s)") 15 | parser.add_argument( 16 | "--host_port", 17 | default=8086, 18 | type=int, 19 | help="Server Port. (default: %(default)s)") 20 | args = parser.parse_args() 21 | 22 | is_recording = False 23 | enable_trigger_record = True 24 | 25 | 26 | def on_press(key): 27 | """On-press keyboard callback function.""" 28 | global is_recording, enable_trigger_record 29 | if key == keyboard.Key.space: 30 | if (not is_recording) and enable_trigger_record: 31 | sys.stdout.write("Start Recording ... ") 32 | sys.stdout.flush() 33 | is_recording = True 34 | 35 | 36 | def on_release(key): 37 | """On-release keyboard callback function.""" 38 | global is_recording, enable_trigger_record 39 | if key == keyboard.Key.esc: 40 | return False 41 | elif key == keyboard.Key.space: 42 | if is_recording == True: 43 | is_recording = False 44 | 45 | 46 | data_list = [] 47 | 48 | 49 | def callback(in_data, frame_count, time_info, status): 50 | """Audio recorder's stream callback function.""" 51 | global data_list, is_recording, enable_trigger_record 52 | if is_recording: 53 | data_list.append(in_data) 54 | enable_trigger_record = False 55 | elif len(data_list) > 0: 56 | # Connect to server and send data 57 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 58 | sock.connect((args.host_ip, args.host_port)) 59 | sent = ''.join(data_list) 60 | sock.sendall(struct.pack('>i', len(sent)) + sent) 61 | print('Speech[length=%d] Sent.' % len(sent)) 62 | # Receive data from the server and shut down 63 | received = sock.recv(1024) 64 | print "Recognition Results: {}".format(received) 65 | sock.close() 66 | data_list = [] 67 | enable_trigger_record = True 68 | return (in_data, pyaudio.paContinue) 69 | 70 | 71 | def main(): 72 | # prepare audio recorder 73 | p = pyaudio.PyAudio() 74 | stream = p.open( 75 | format=pyaudio.paInt16, 76 | channels=1, 77 | rate=16000, 78 | input=True, 79 | stream_callback=callback) 80 | stream.start_stream() 81 | 82 | # prepare keyboard listener 83 | with keyboard.Listener( 84 | on_press=on_press, on_release=on_release) as listener: 85 | listener.join() 86 | 87 | # close up 88 | stream.stop_stream() 89 | stream.close() 90 | p.terminate() 91 | 92 | 93 | if __name__ == "__main__": 94 | main() 95 | -------------------------------------------------------------------------------- /deploy_demo/demo_server.py: -------------------------------------------------------------------------------- 1 | """Server-end for the ASR demo.""" 2 | import os 3 | import time 4 | import random 5 | import re 6 | import argparse 7 | import functools 8 | from time import gmtime, strftime 9 | import SocketServer 10 | import struct 11 | import wave 12 | 13 | import _init_paths 14 | import assert_zero 15 | from data_utils import process_manifest 16 | from model_utils import init_model 17 | from conf.hyparam import Config 18 | from utils.utility import add_arguments, print_arguments 19 | 20 | parser = argparse.ArgumentParser(description=__doc__) 21 | add_arg = functools.partial(add_arguments, argparser=parser) 22 | conf = Config() 23 | # yapf: disable 24 | add_arg('host_port', int, 8086, "Server's IP port.") 25 | add_arg('beam_size', int, conf.beam_size, "Beam search width.") 26 | add_arg('num_conv_layers', int, 2, "# of convolution layers.") 27 | add_arg('num_brnn_layers', int, conf.n_brnn_layers, "# of recurrent layers.") 28 | add_arg('rnn_layer_size', int, conf.n_cell_brnn, "# of recurrent cells per layer.") 29 | add_arg('alpha', float, conf.alpha, "Coef of LM for beam search.") 30 | add_arg('beta', float, conf.beta, "Coef of WC for beam search.") 31 | add_arg('cutoff_prob', float, conf.cutoff_prob, "Cutoff probability for pruning.") 32 | add_arg('cutoff_top_n', int, conf.cutoff_top_n, "Cutoff number for pruning.") 33 | add_arg('use_gpu', bool, True, "Use GPU or not.") 34 | add_arg('host_ip', str, 35 | 'localhost', 36 | "Server's IP address.") 37 | add_arg('speech_save_dir', str, 38 | 'demo_cache', 39 | "Directory to save demo audios.") 40 | add_arg('warmup_manifest', str, 41 | 'data/aishell/manifest.dev', 42 | "Filepath of manifest to warm up.") 43 | add_arg('mean_std_path', str, 44 | 'data/aishell/mean_std.npz', 45 | "Filepath of normalizer's mean & std.") 46 | add_arg('vocab_path', str, 47 | 'data/aishell/vocab.txt', 48 | "Filepath of vocabulary.") 49 | add_arg('model_path', str, 50 | conf.savedir + conf.savefile, 51 | "If None, the training starts from scratch, " 52 | "otherwise, it resumes from the pre-trained model.") 53 | add_arg('lang_model_path', str, 54 | conf.lang_model_path, 55 | "Filepath for language model.") 56 | add_arg('use lm decoder', bool, 57 | conf.use_lm_decoder, 58 | "Decoding method. Options: ctc_beam_search, ctc_greedy", 59 | choices = ['ctc_beam_search', 'ctc_greedy']) 60 | add_arg('specgram_type', str, 61 | conf.specgram_type, 62 | "Audio feature type. Options: linear, mfcc.", 63 | choices=['linear', 'mfcc']) 64 | # yapf: disable 65 | args = parser.parse_args() 66 | 67 | 68 | class AsrTCPServer(SocketServer.TCPServer): 69 | """The ASR TCP Server.""" 70 | 71 | def __init__(self, 72 | server_address, 73 | RequestHandlerClass, 74 | speech_save_dir, 75 | audio_process_handler, 76 | bind_and_activate=True): 77 | self.speech_save_dir = speech_save_dir 78 | self.audio_process_handler = audio_process_handler 79 | SocketServer.TCPServer.__init__( 80 | self, server_address, RequestHandlerClass, bind_and_activate=True) 81 | 82 | 83 | class AsrRequestHandler(SocketServer.BaseRequestHandler): 84 | """The ASR request handler.""" 85 | 86 | def handle(self): 87 | # receive data through TCP socket 88 | chunk = self.request.recv(1024) 89 | target_len = struct.unpack('>i', chunk[:4])[0] 90 | data = chunk[4:] 91 | while len(data) < target_len: 92 | chunk = self.request.recv(1024) 93 | data += chunk 94 | # write to file 95 | filename = self._write_to_file(data) 96 | 97 | print("Received utterance[length=%d] from %s, saved to %s." % 98 | (len(data), self.client_address[0], filename)) 99 | start_time = time.time() 100 | transcript = self.server.audio_process_handler(filename) 101 | finish_time = time.time() 102 | print("Response Time: %f, Transcript: %s" % 103 | (finish_time - start_time, transcript)) 104 | if transcript == None: 105 | transcript = "" 106 | self.request.sendall(transcript) 107 | 108 | def _write_to_file(self, data): 109 | # prepare save dir and filename 110 | if not os.path.exists(self.server.speech_save_dir): 111 | os.mkdir(self.server.speech_save_dir) 112 | timestamp = strftime("%Y%m%d%H%M%S", gmtime()) 113 | out_filename = os.path.join( 114 | self.server.speech_save_dir, 115 | timestamp + "_" + self.client_address[0] + ".wav") 116 | # write to wav file 117 | file = wave.open(out_filename, 'wb') 118 | file.setnchannels(1) 119 | file.setsampwidth(2) 120 | file.setframerate(16000) 121 | file.writeframes(data) 122 | file.close() 123 | return out_filename 124 | 125 | 126 | def warm_up_test(audio_process_handler, 127 | manifest_path, 128 | num_test_cases, 129 | random_seed=0): 130 | """Warming-up test.""" 131 | manifest = read_manifest(manifest_path) 132 | rng = random.Random(random_seed) 133 | samples = rng.sample(manifest, num_test_cases) 134 | for idx, sample in enumerate(samples): 135 | print("Warm-up Test Case %d: %s", idx, sample['audio_filepath']) 136 | start_time = time.time() 137 | transcript = audio_process_handler(sample['audio_filepath']) 138 | finish_time = time.time() 139 | print("Response Time: %f, Transcript: %s" % 140 | (finish_time - start_time, transcript)) 141 | 142 | 143 | def start_server(): 144 | """Start the ASR server""" 145 | # prepare data generator 146 | wav_files, text_labels, _ = process_manifest.get_path_trans() 147 | words_size, words, word_num_map = process_manifest.create_dict("data/aishell/vocab.txt") 148 | deepspeech2 = init_model.DeepSpeech2(wav_files, text_labels, words_size, words, word_num_map) 149 | online_model = deepspeech2.init_online_model() 150 | # prepare ASR inference handler 151 | def file_to_transcript(filename): 152 | stream_test = assert_zero.FileParser() 153 | read_file = stream_test.read_file(filename) 154 | if read_file == 1 or stream_test.run(0): 155 | print "recive a empty wav file" 156 | return "" 157 | else: 158 | result_transcript = deepspeech2.predict(re.split('',filename), ["deepspeech2"]) 159 | return result_transcript 160 | 161 | # warming up with utterrances sampled from Librispeech 162 | print('-----------------------------------------------------------') 163 | print('Warming up ...') 164 | deepspeech2.test() 165 | print('-----------------------------------------------------------') 166 | 167 | # start the server 168 | server = AsrTCPServer( 169 | server_address=(args.host_ip, args.host_port), 170 | RequestHandlerClass=AsrRequestHandler, 171 | speech_save_dir=args.speech_save_dir, 172 | audio_process_handler=file_to_transcript) 173 | print("ASR Server Started.") 174 | server.serve_forever() 175 | 176 | 177 | def main(): 178 | print_arguments(args) 179 | start_server() 180 | 181 | 182 | if __name__ == "__main__": 183 | main() 184 | -------------------------------------------------------------------------------- /example/aishell/run_data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # shell script to generate manifests.{train,dev,test} mean_std.npz vocab.txt 4 | 5 | cd ../.. > /dev/null 6 | 7 | # download data, generate manifests 8 | PYTHONPATH=.:$PYTHONPATH python data/aishell/aishell.py \ 9 | --manifest_prefix='data/aishell/manifest' \ 10 | --target_dir='/media/nlp/23ACE59C56A55BF3/wav_file/aishell/' 11 | 12 | if [ $? -ne 0 ]; then 13 | echo "Prepare Aishell failed. Terminated." 14 | exit 1 15 | fi 16 | 17 | # build vocabulary 18 | python data_utils/build_vocab.py \ 19 | --count_threshold=0 \ 20 | --vocab_path='data/aishell/vocab.txt' \ 21 | --manifest_paths 'data/aishell/manifest.train' 'data/aishell/manifest.dev' 22 | 23 | if [ $? -ne 0 ]; then 24 | echo "Build vocabulary failed. Terminated." 25 | exit 1 26 | fi 27 | 28 | # compute mean and stddev for normalizer 29 | python data_utils/compute_mean_std.py \ 30 | --manifest_path='data/aishell/manifest.train' \ 31 | --num_samples=2000 \ 32 | --specgram_type='linear' \ 33 | --output_path='data/aishell/mean_std.npz' 34 | 35 | if [ $? -ne 0 ]; then 36 | echo "Compute mean and stddev failed. Terminated." 37 | exit 1 38 | fi 39 | 40 | echo "Aishell data preparation done." 41 | exit 0 42 | -------------------------------------------------------------------------------- /img/arc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/img/arc.png -------------------------------------------------------------------------------- /model_utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/model_utils/__init__.py -------------------------------------------------------------------------------- /model_utils/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/model_utils/__init__.pyc -------------------------------------------------------------------------------- /model_utils/init_model.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | from __future__ import absolute_import 4 | from __future__ import division 5 | 6 | import time 7 | import math 8 | import numpy as np 9 | import tensorflow as tf 10 | from tensorflow.python.ops import ctc_ops 11 | 12 | from data_utils import utils 13 | from conf.hyparam import Config 14 | from model_utils import network 15 | from utils.decoder.model import LM_decoder 16 | 17 | class DeepSpeech2(object): 18 | ''' Class to init model 19 | 20 | :param wav_files: path to wav files 21 | :type wav_files: str 22 | :param text_labels: transcript for wav files 23 | :type text_labels: list 24 | :param words_size: the size of vocab 25 | :type words_size: int 26 | :param words : a list for vocab 27 | :type words: list 28 | :param word_num_map: a map dict from word to num 29 | :type word_num_map: dict 30 | return 31 | ''' 32 | def __init__(self, wav_files, text_labels, words_size, words, word_num_map): 33 | self.hyparam = Config() 34 | self.wav_files = wav_files 35 | self.text_labels = text_labels 36 | self.words_size = words_size 37 | self.words = words 38 | self.word_num_map = word_num_map 39 | # mfcc features contains 39 * 2 * 2 + 39 = 195 dims , linear specgram 161 dim which 161 = (8000-0)/50 + 1 40 | self.n_dim = self.hyparam.n_input + 2 * self.hyparam.n_input * self.hyparam.n_context if self.hyparam.specgram_type == 'mfcc' else 161 41 | 42 | def add_placeholders(self): 43 | # input tensor for log filter or MFCC features 44 | self.input_tensor = tf.placeholder( tf.float32, 45 | [None, None, self.n_dim], 46 | name='input') 47 | self.text = tf.sparse_placeholder(tf.int32, name='text') 48 | self.seq_length = tf.placeholder(tf.int32, [None], name='seq_length') 49 | self.keep_dropout = tf.placeholder(tf.float32) 50 | 51 | def deepspeech2(self): 52 | ''' 53 | BUild a network with CNN-BRNN-Lookahead CNN -FC. 54 | ''' 55 | batch_x = self.input_tensor 56 | seq_length = self.seq_length 57 | n_character = self.words_size + 1 58 | keep_dropout = self.keep_dropout 59 | n_input = self.hyparam.n_input 60 | n_context = self.hyparam.n_context 61 | 62 | batch_x_shape = tf.shape(batch_x) 63 | batch_x = tf.transpose(batch_x, [1, 0, 2]) 64 | batch_x = tf.expand_dims(batch_x, -1) 65 | batch_x = tf.reshape(batch_x, 66 | [batch_x_shape[0], -1, self.n_dim, 1] ) # shape (batch_size, ?, n_dim, 1) 67 | 68 | with tf.variable_scope('conv_1'): 69 | # usage: conv2d(batch_x, filter_shape, strides, pool_size, hyparam, use_dropout=False) 70 | # Output is [batch_size, height, width. out_channel] 71 | conv_1 = network.conv2d(batch_x, 72 | [1, n_input / 3, 1, 1], # filter: [height, width, in_channel, out_channel] 73 | [1, 1, n_input/5 , 1], # strides: [1, height, width, 1] 74 | 2, self.hyparam, use_dropout=False) # shape (8, ?, 19, 1) 75 | conv_1 = tf.squeeze(conv_1, [-1]) 76 | conv_1 = tf.transpose(conv_1, [1, 0, 2]) 77 | 78 | with tf.variable_scope('birnn_1'): 79 | birnn_1 = network.BiRNN(conv_1, seq_length, batch_x_shape, self.hyparam ) 80 | with tf.variable_scope('birnn_2'): 81 | birnn_2 = network.BiRNN(birnn_1, seq_length, batch_x_shape, self.hyparam ) 82 | with tf.variable_scope('birnn_3'): 83 | birnn_3 = network.BiRNN(birnn_2, seq_length, batch_x_shape, self.hyparam, use_dropout=True) 84 | birnn_3 = tf.reshape(birnn_3, [batch_x_shape[0], -1, 2*self.hyparam.n_cell_brnn]) 85 | birnn_3 = tf.expand_dims(birnn_3, -1) 86 | with tf.variable_scope('lcnn_1'): 87 | # Lookahead CNN combines n time-steps in furture 88 | # lcnn_1 = network.lookahead_cnn(birnn_3, [2, 2*self.hyparam.n_cell_brnn, 1, 2*self.hyparam.n_cell_brnn], 2, seq_length, self.hyparam, use_dropout=True) 89 | lcnn_1 = network.conv2d(birnn_3, 90 | [1, n_input, 1, 1], 91 | [1, 1, n_input/5, 1], 92 | 2, self.hyparam, use_dropout=True) 93 | lcnn_1 = tf.squeeze(lcnn_1,[-1]) 94 | width_lcnn1 = 14 # use to compute lcnn_1[-1], computed by pool_size * n_input / 5 95 | lcnn_1 = tf.reshape(lcnn_1, [-1, int(math.ceil(2*self.hyparam.n_cell_brnn/width_lcnn1))]) 96 | 97 | with tf.variable_scope('fc'): 98 | b_fc = self.variable_on_device('b_fc', [n_character], tf.random_normal_initializer(stddev=self.hyparam.b_stddev)) 99 | h_fc = self.variable_on_device('h_fc', 100 | [int(math.ceil(2*self.hyparam.n_cell_brnn/width_lcnn1)), n_character], 101 | tf.random_normal_initializer(stddev=self.hyparam.h_stddev)) 102 | layer_fc = tf.add(tf.matmul(lcnn_1, h_fc), b_fc) 103 | # turn it to 3 dim, [n_steps, hyparam.batch_size, n_character] 104 | layer_fc = tf.reshape(layer_fc, [-1, batch_x_shape[0], n_character]) 105 | 106 | self.logits = layer_fc 107 | 108 | def loss(self): 109 | """ Define loss 110 | return 111 | """ 112 | # ctc loss 113 | with tf.name_scope('loss'): 114 | self.avg_loss = tf.reduce_mean(ctc_ops.ctc_loss(self.text, self.logits, self.seq_length)) 115 | tf.summary.scalar('loss',self.avg_loss) 116 | # [optimizer] 117 | with tf.name_scope('train'): 118 | self.optimizer = tf.train.AdamOptimizer(learning_rate=self.hyparam.learning_rate).minimize(self.avg_loss) 119 | 120 | with tf.name_scope("decode"): 121 | self.decoded, log_prob = ctc_ops.ctc_beam_search_decoder(self.logits, self.seq_length, merge_repeated=False) 122 | 123 | with tf.name_scope("ctc_beam_search_decode"): 124 | self.prob = tf.nn.softmax(self.logits, dim=0) 125 | self.prob = tf.transpose(self.prob, [1, 0, 2]) # keep the same dim with decoder {batch_size, time_step, n_character} 126 | self.decoder = LM_decoder(self.hyparam.alpha, self.hyparam.beta, self.hyparam.lang_model_path, self.words) 127 | 128 | with tf.name_scope("accuracy"): 129 | self.distance = tf.edit_distance(tf.cast(self.decoded[0], tf.int32), self.text) 130 | # compute label error rate (accuracy) 131 | self.label_err = tf.reduce_mean(self.distance, name='label_error_rate') 132 | tf.summary.scalar('accuracy', self.label_err) 133 | 134 | 135 | def get_feed_dict(self, dropout=None): 136 | """ Define feed dict 137 | 138 | :param dropout: 139 | :return: 140 | """ 141 | feed_dict = {self.input_tensor: self.audio_features, 142 | self.text: self.sparse_labels, 143 | self.seq_length: self.audio_features_len} 144 | 145 | if dropout != None: 146 | feed_dict[self.keep_dropout] = dropout 147 | else: 148 | feed_dict[self.keep_dropout] = self.hyparam.keep_dropout_rate 149 | 150 | return feed_dict 151 | 152 | def init_session(self): 153 | self.savedir = self.hyparam.savedir 154 | self.saver = tf.train.Saver(max_to_keep=1) 155 | gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.7) 156 | self.sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options)) 157 | # if no models , init it 158 | self.sess.run(tf.global_variables_initializer()) 159 | 160 | ckpt = tf.train.latest_checkpoint(self.savedir) 161 | print "ckpt:", ckpt 162 | self.startepo = 0 163 | if ckpt != None: 164 | self.saver.restore(self.sess, ckpt) 165 | ind = ckpt.rfind("-") 166 | self.startepo = int(ckpt[ind + 1:]) 167 | print(self.startepo) 168 | print() 169 | 170 | def add_summary(self): 171 | self.merged = tf.summary.merge_all() 172 | self.writer = tf.summary.FileWriter(self.hyparam.tensorboardfile, self.sess.graph) 173 | 174 | def init_decode(self): 175 | self.prob = tf.nn.softmax(self.logits, dim=0) 176 | self.prob = tf.transpose(self.prob, [1, 0, 2]) # keep the same dim with decoder {batch_size, time_step, n_character} 177 | self.decoder = LM_decoder(self.hyparam.alpha, self.hyparam.beta, self.hyparam.lang_model_path, self.words) 178 | 179 | def lm_decode(self, probality): 180 | result_transcripts = self.decoder.decode_batch_beam_search( 181 | probs_split=probality, 182 | beam_alpha=self.hyparam.alpha, 183 | beam_beta=self.hyparam.beta, 184 | beam_size=self.hyparam.beam_size, 185 | cutoff_prob=self.hyparam.cutoff_prob, 186 | cutoff_top_n=self.hyparam.cutoff_top_n, 187 | vocab_list=self.words, 188 | num_processes=self.hyparam.num_proc_bsearch) 189 | results = "" if result_transcripts == None else result_transcripts 190 | return results[0].encode('utf-8') 191 | 192 | def train(self): 193 | epochs = 120 194 | section = '\n{0:=^40}\n' 195 | print section.format('Start training...') 196 | 197 | train_start = time.time() 198 | for epoch in range(epochs): 199 | epoch_start = time.time() 200 | if epoch < self.startepo: 201 | continue 202 | 203 | print "Current epoch :", epoch, " the total epochs is ", epochs 204 | n_batches_epoch = int(np.ceil(len(self.text_labels) / self.hyparam.batch_size)) 205 | print "CUrrent epoch contains batchs: ", n_batches_epoch, " the batch size is :", self.hyparam.batch_size 206 | 207 | train_cost = 0 208 | train_err = 0 209 | next_idx = 0 210 | 211 | for batch in range(n_batches_epoch): 212 | next_idx, self.audio_features, self.audio_features_len, self.sparse_labels, wav_files = utils.next_batch( 213 | next_idx, 214 | self.hyparam.batch_size, 215 | self.hyparam.n_input, 216 | self.hyparam.n_context, 217 | self.text_labels, 218 | self.wav_files, 219 | self.word_num_map, 220 | specgram_type=self.hyparam.specgram_type) 221 | 222 | batch_cost, _ = self.sess.run([self.avg_loss, self.optimizer], feed_dict=self.get_feed_dict()) 223 | train_cost += batch_cost 224 | 225 | if (batch + 1) % 70 == 0: 226 | rs = self.sess.run(self.merged, feed_dict=self.get_feed_dict()) 227 | self.writer.add_summary(rs, batch) 228 | 229 | print 'Current batch :', batch, ' Loss: ', train_cost / (batch + 1) 230 | 231 | d, train_err = self.sess.run([self.decoded[0], self.label_err], feed_dict=self.get_feed_dict(dropout=1.0)) 232 | dense_decoded = tf.sparse_tensor_to_dense(d, default_value=-1).eval(session=self.sess) 233 | dense_labels = utils.trans_tuple_to_texts_ch(self.sparse_labels, self.words) 234 | 235 | print 'error rate: ', train_err 236 | for orig, decoded_array in zip(dense_labels, dense_decoded): 237 | # convert to strings 238 | decoded_str = utils.trans_array_to_text_ch(decoded_array, self.words) 239 | print 'Orinal inputs: ', orig 240 | print 'Transcript: ', decoded_str 241 | break 242 | 243 | epoch_duration = time.time() - epoch_start 244 | log = 'Epoch {}/{}, Loss: {:.3f}, error rate: {:.3f}, time: {:.2f} sec' 245 | print(log.format(epoch, epochs, train_cost, train_err, epoch_duration)) 246 | self.saver.save(self.sess, self.savedir + self.hyparam.savefile, global_step=epoch) 247 | 248 | train_duration = time.time() - train_start 249 | print('Training complete, total duration: {:.2f} min'.format(train_duration / 60)) 250 | self.sess.close() 251 | 252 | def test(self): 253 | index = 0 254 | next_idx = 20 255 | 256 | for index in range(10): 257 | next_idx, self.audio_features, self.audio_features_len, self.sparse_labels, wav_files = utils.next_batch( 258 | next_idx, 259 | 1, 260 | self.hyparam.n_input, 261 | self.hyparam.n_context, 262 | self.text_labels, 263 | self.wav_files, 264 | self.word_num_map, 265 | specgram_type=self.hyparam.specgram_type) 266 | 267 | print 'Load wav file: ', wav_files[0] 268 | print 'Recognizing......' 269 | 270 | prob, d, train_ler = self.sess.run([self.prob, self.decoded[0], self.label_err], feed_dict=self.get_feed_dict(dropout=1.0)) 271 | dense_labels = utils.trans_tuple_to_texts_ch(self.sparse_labels, self.words) 272 | 273 | if self.hyparam.use_lm_decoder: 274 | result_transcripts = self.lm_decode(prob) 275 | print "Orinal text: ", dense_labels[0] 276 | print "Transcript: ", result_transcripts 277 | else: 278 | dense_decoded = tf.sparse_tensor_to_dense(d, default_value=-1).eval(session=self.sess) 279 | print "dense_decoded", np.shape(dense_decoded), dense_decoded 280 | for orig, decoded_array in zip(dense_labels, dense_decoded): 281 | # turn to string 282 | decoded_str = utils.trans_array_to_text_ch(decoded_array, self.words) 283 | print "Orinal text:", orig 284 | print "Transcript: ", decoded_str 285 | break 286 | # self.sess.close() 287 | 288 | def recon_wav_file(self, wav_files, txt_labels): 289 | self.audio_features, self.audio_features_len, text_vector, text_vector_len = utils.get_audio_mfcc_features( 290 | None, 291 | wav_files, 292 | self.hyparam.n_input, 293 | self.hyparam.n_context, 294 | self.word_num_map, 295 | txt_labels, 296 | specgram_type=self.hyparam.specgram_type) 297 | self.sparse_labels = utils.sparse_tuple_from(text_vector) 298 | prob, d, train_ler = self.sess.run([self.prob, self.decoded[0], self.label_err], feed_dict=self.get_feed_dict(dropout=1.0)) 299 | if self.hyparam.use_lm_decoder: 300 | result_transcripts = self.lm_decode(prob) 301 | else: 302 | dense_decoded = tf.sparse_tensor_to_dense(d, default_value=-1).eval(session=self.sess) 303 | result_transcripts = utils.trans_array_to_text_ch(dense_decoded[0], self.words).encode('utf-8') 304 | # print "Transcript: ", result_transcripts 305 | return result_transcripts 306 | 307 | # self.sess.close() 308 | 309 | def build_train(self): 310 | self.add_placeholders() 311 | self.deepspeech2() 312 | self.loss() 313 | self.init_session() 314 | self.add_summary() 315 | self.train() 316 | 317 | def build_test(self): 318 | self.add_placeholders() 319 | self.deepspeech2() 320 | self.loss() 321 | self.init_session() 322 | self.init_decode() 323 | self.test() 324 | 325 | def init_online_model(self): 326 | self.add_placeholders() 327 | self.deepspeech2() 328 | self.loss() 329 | self.init_session() 330 | 331 | def predict(self, wav_files, txt_labels): 332 | transcript = self.recon_wav_file(wav_files, txt_labels) 333 | return transcript 334 | 335 | def close_onlie(self): 336 | self.sess.close() 337 | 338 | def variable_on_device(self, name, shape, initializer): 339 | # with tf.device('/gpu:0'): 340 | var = tf.get_variable(name=name, shape=shape, initializer=initializer) 341 | return var 342 | 343 | -------------------------------------------------------------------------------- /model_utils/init_model.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/model_utils/init_model.pyc -------------------------------------------------------------------------------- /model_utils/network.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | """ Deep Network wraper for BRNN CNN Lookahead CNN """ 5 | import tensorflow as tf 6 | import numpy as np 7 | 8 | scale = tf.Variable(tf.ones([1])) 9 | offset = tf.Variable(tf.zeros([1])) 10 | variance_epsilon = 0.001 11 | 12 | def conv2d(batch_x, filter_shape, strides, pool_size, hyparam, use_dropout=False): 13 | ''' Convolution Network wraper for tf.conv2d, contains conv relu act and pooling 14 | 15 | :param batch_x: input tensor with shape [batch_size, time_steps, features_dim, in_channel] 16 | :type batch_x: tensorflow tensor 17 | :param filter_shape: a list to control filtr shape, [height, width, in_channel, out_channel] 18 | :type filter_shape: list 19 | :param strides: a list to control conv strides, [1, height, width, 1] 20 | :type strides: list 21 | :param pool_size: pooling size, default value is 2 22 | :type pool_size: int 23 | :param hyparam: hyparam config class 24 | :type hyparam: class 25 | :param use_dropout: decide wether use dropout 26 | :type use_dropout: bool 27 | return a tensor with shape [batch_size, height, width. out_channel] 28 | ''' 29 | if hyparam.use_bn: 30 | batch_mean, batch_var = tf.nn.moments(batch_x, [0, 1, 2]) 31 | batch_x = tf.nn.batch_normalization(batch_x, batch_mean, batch_var, offset, scale, variance_epsilon) 32 | 33 | filter = tf.get_variable("filter", 34 | shape=filter_shape, 35 | regularizer=tf.contrib.layers.l2_regularizer(0.0001), 36 | initializer=tf.truncated_normal_initializer(stddev=0.01), 37 | dtype=tf.float32) 38 | conv = tf.nn.conv2d(batch_x, filter, strides, padding='SAME' ) 39 | conv = tf.nn.relu(conv) 40 | 41 | conv = tf.nn.max_pool(conv, 42 | ksize=[1, 1, pool_size, 1], 43 | strides=[1, 1, pool_size, 1], 44 | padding='SAME') 45 | if use_dropout: 46 | conv = tf.nn.dropout(conv, 0.5) 47 | 48 | return conv 49 | 50 | # Ongoing... 51 | def lookahead_cnn(inputs, filter_shape, pool_size, seq_length, hyparam, use_dropout=True): 52 | ''' Lookahead CNN combines 2 future inputs. 53 | 54 | :param inputs: input tensor with shape [batch_size, time_steps, features_dim, in_channel] 55 | :type inputs: tensorflow tensor 56 | :param filter_shape: a list to control filtr shape, [height, width, in_channel, out_channel] 57 | :type filter_shape: list 58 | :param strides: a list to control conv strides, [1, height, width, 1] 59 | :type strides: list 60 | :param pool_size: pooling size, default value is 2 61 | :type pool_size: int 62 | :param hyparam: hyparam config class 63 | :type hyparam: class 64 | :param use_dropout: decide wether use dropout 65 | :type use_dropout: bool 66 | return a tensor with shape [batch_size, height, width. out_channel] 67 | ''' 68 | # combine 2 ahead inputs 69 | 70 | lcnn_inputs = [] 71 | h = tf.get_variable('h', shape=[3, hyparam.n_cell_dim], 72 | initializer=tf.random_normal_initializer(stddev=hyparam.h_stddev)) 73 | b = tf.get_variable('b', shape=[hyparam.n_cell_dim], 74 | initializer=tf.random_normal_initializer(stddev=hyparam.b_stddev)) 75 | if np.shape(inputs)[1] < 3: 76 | print "Too short to use lookahead_cnn, the inputs should larger than 2" 77 | return inputs 78 | else: 79 | # range only receive a interger, so I don't know how to iter it to add the outputs of time step t, t+1,t+2 80 | for j in range(hyparam.batch_size): 81 | lcnn_inputs = [[inputs[j][i], inputs[j][i+1], inputs[j][i+2]] for i in range(np.shape(inputs)[1]-2) ] 82 | lcnn_inputs.append([inputs[j][-2], inputs[j][-1], np.zeros(np.shape(inputs[j][-1])).tolist()]) 83 | lcnn_inputs.append([inputs[j][-1]. np.zeros(np.shape(inputs[j][-1])).tolist(), np.zeros(np.shape(inputs[j][-1])).tolist()]) 84 | 85 | lcnn_inputs = tf.add( tf.matmul(lcnn_inputs, h), b) 86 | 87 | # need reshape inputs with [batch_size, height, withdth, 1] 88 | 89 | lcnn_layer = conv2d(lcnn_inputs, filter_shape, pool_size, use_dropout) 90 | 91 | return lcnn_layer 92 | 93 | def BiRNN(inputs, seq_length, batch_x_shape, hyparam, use_dropout=False): 94 | '''BiRNN wraper with time major 95 | 96 | :param inputs: input tensor with time_major, [time_steps, batch_size, features_dim] 97 | :type inputs: tensor 98 | :param seq_length: length of input audio 99 | :type seq_length: tensor 100 | :param batch_x_shape: shape of inputs in dim 0 101 | :type batch_x_shape: tensor 102 | :param hyparam: model config in class hyparam 103 | :type hyparam: class 104 | :param use_dropout: wether use dropout 105 | :type use_dropout: bool 106 | return a tensor with [time_steps, batch_size, features_dim] 107 | ''' 108 | 109 | if hyparam.use_bn: 110 | batch_mean, batch_var = tf.nn.moments(inputs, [0, 1, 2]) 111 | batch_x = tf.nn.batch_normalization(inputs, batch_mean, batch_var, offset, scale, variance_epsilon) 112 | 113 | # forward 114 | lstm_fw_cell = tf.contrib.rnn.BasicLSTMCell(hyparam.n_cell_brnn, forget_bias=1.0, state_is_tuple=True) 115 | lstm_fw_cell = tf.contrib.rnn.DropoutWrapper(lstm_fw_cell, input_keep_prob=hyparam.keep_dropout_rate) 116 | 117 | # backward 118 | lstm_bw_cell = tf.contrib.rnn.BasicLSTMCell(hyparam.n_cell_brnn, forget_bias=1.0, state_is_tuple=True) 119 | lstm_bw_cell = tf.contrib.rnn.DropoutWrapper(lstm_bw_cell, input_keep_prob=hyparam.keep_dropout_rate) 120 | 121 | outputs, output_states = tf.nn.bidirectional_dynamic_rnn(cell_fw=lstm_fw_cell, 122 | cell_bw=lstm_bw_cell, 123 | inputs=inputs, 124 | dtype=tf.float32, 125 | time_major=True, # if time_major is True, the input shape should be [time_steps, batch_size, ...] 126 | sequence_length=seq_length) 127 | 128 | 129 | 130 | outputs = tf.concat(outputs, 2) 131 | outputs = tf.reshape(outputs, [-1,batch_x_shape[0] , 2 * hyparam.n_cell_brnn]) 132 | 133 | if use_dropout : 134 | outputs = tf.nn.dropout(outputs, 0.5) 135 | 136 | return outputs 137 | -------------------------------------------------------------------------------- /model_utils/network.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/model_utils/network.pyc -------------------------------------------------------------------------------- /models/lm/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/models/lm/__init__.py -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | import os 5 | 6 | from data_utils import process_manifest 7 | from model_utils import init_model 8 | from conf.hyparam import Config 9 | 10 | conf = Config() 11 | os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' 12 | 13 | wav_files, text_labels, _ = process_manifest.get_path_trans() 14 | 15 | words_size, words, word_num_map = process_manifest.create_dict(conf.vocab_path) 16 | 17 | deepspeech2 = init_model.DeepSpeech2(wav_files, text_labels, words_size, words, word_num_map) 18 | 19 | deepspeech2.build_test() 20 | -------------------------------------------------------------------------------- /test_build.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | import os 5 | import re 6 | from data_utils import process_manifest 7 | from model_utils import init_model 8 | from conf.hyparam import Config 9 | 10 | conf = Config() 11 | os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' 12 | 13 | wav_files, text_labels, _ = process_manifest.get_path_trans() 14 | 15 | words_size, words, word_num_map = process_manifest.create_dict(conf.vocab_path) 16 | 17 | deepspeech2 = init_model.DeepSpeech2(wav_files, text_labels, words_size, words, word_num_map) 18 | filename = "demo_cache/20180730080730_10.3.27.97.wav" 19 | 20 | deepspeech2.recon_wav_file_test(re.split("", filename),["你好"]) 21 | -------------------------------------------------------------------------------- /train.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | """Trainer for DeepSpeech2 model.""" 5 | from __future__ import absolute_import 6 | from __future__ import division 7 | from __future__ import print_function 8 | 9 | from data_utils import process_manifest 10 | from model_utils import init_model 11 | from conf.hyparam import Config 12 | 13 | conf = Config() 14 | wav_files, text_labels, _ = process_manifest.get_path_trans() 15 | 16 | words_size, words, word_num_map = process_manifest.create_dict(conf.vocab_path) 17 | 18 | 19 | deepspeech2 = init_model.DeepSpeech2(wav_files, text_labels, words_size, words, word_num_map) 20 | 21 | deepspeech2.build_train() 22 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/utils/__init__.py -------------------------------------------------------------------------------- /utils/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/utils/__init__.pyc -------------------------------------------------------------------------------- /utils/decoder/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/utils/decoder/__init__.py -------------------------------------------------------------------------------- /utils/decoder/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/utils/decoder/__init__.pyc -------------------------------------------------------------------------------- /utils/decoder/model.py: -------------------------------------------------------------------------------- 1 | """Contains DeepSpeech2 model.""" 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | 6 | import sys 7 | import os 8 | import time 9 | import logging 10 | import gzip 11 | import copy 12 | import numpy as np 13 | import inspect 14 | from utils.decoder.swig_wrapper import Scorer 15 | from utils.decoder.swig_wrapper import ctc_greedy_decoder 16 | from utils.decoder.swig_wrapper import ctc_beam_search_decoder_batch 17 | 18 | 19 | class LM_decoder(object): 20 | 21 | def __init__(self, beam_alpha, beam_beta, language_model_path, 22 | vocab_list): 23 | """Initialize the external scorer. 24 | 25 | :param beam_alpha: Parameter associated with language model. 26 | :type beam_alpha: float 27 | :param beam_beta: Parameter associated with word count. 28 | :type beam_beta: float 29 | :param language_model_path: Filepath for language model. If it is 30 | empty, the external scorer will be set to 31 | None, and the decoding method will be pure 32 | beam search without scorer. 33 | :type language_model_path: basestring|None 34 | :param vocab_list: List of tokens in the vocabulary, for decoding. 35 | :type vocab_list: list 36 | """ 37 | if language_model_path != '': 38 | print("begin to initialize the external scorer " 39 | "for decoding") 40 | self._ext_scorer = Scorer(beam_alpha, beam_beta, 41 | language_model_path, vocab_list) 42 | lm_char_based = self._ext_scorer.is_character_based() 43 | lm_max_order = self._ext_scorer.get_max_order() 44 | lm_dict_size = self._ext_scorer.get_dict_size() 45 | print("language model: " 46 | "is_character_based = %d," % lm_char_based + 47 | " max_order = %d," % lm_max_order + 48 | " dict_size = %d" % lm_dict_size) 49 | print("end initializing scorer") 50 | else: 51 | self._ext_scorer = None 52 | print("no language model provided, " 53 | "decoding by pure beam search without scorer.") 54 | 55 | def decode_batch_beam_search(self, probs_split, beam_alpha, beam_beta, 56 | beam_size, cutoff_prob, cutoff_top_n, 57 | vocab_list, num_processes): 58 | """Decode by beam search for a batch of probs matrix input. 59 | 60 | :param probs_split: List of 2-D probability matrix, and each consists 61 | of prob vectors for one speech utterancce. 62 | :param probs_split: List of matrix 63 | :param beam_alpha: Parameter associated with language model. 64 | :type beam_alpha: float 65 | :param beam_beta: Parameter associated with word count. 66 | :type beam_beta: float 67 | :param beam_size: Width for Beam search. 68 | :type beam_size: int 69 | :param cutoff_prob: Cutoff probability in pruning, 70 | default 1.0, no pruning. 71 | :type cutoff_prob: float 72 | :param cutoff_top_n: Cutoff number in pruning, only top cutoff_top_n 73 | characters with highest probs in vocabulary will be 74 | used in beam search, default 40. 75 | :type cutoff_top_n: int 76 | :param vocab_list: List of tokens in the vocabulary, for decoding. 77 | :type vocab_list: list 78 | :param num_processes: Number of processes (CPU) for decoder. 79 | :type num_processes: int 80 | :return: List of transcription texts. 81 | :rtype: List of basestring 82 | """ 83 | if self._ext_scorer != None: 84 | self._ext_scorer.reset_params(beam_alpha, beam_beta) 85 | # beam search decode 86 | num_processes = min(num_processes, np.shape(probs_split)[0]) 87 | beam_search_results = ctc_beam_search_decoder_batch( 88 | probs_split=probs_split, 89 | vocabulary=vocab_list, 90 | beam_size=beam_size, 91 | num_processes=num_processes, 92 | ext_scoring_func=self._ext_scorer, 93 | cutoff_prob=cutoff_prob, 94 | cutoff_top_n=cutoff_top_n) 95 | 96 | results = [result[0][1] for result in beam_search_results] 97 | return results 98 | 99 | def _adapt_feeding_dict(self, feeding_dict): 100 | """Adapt feeding dict according to network struct. 101 | 102 | To remove impacts from padding part, we add scale_sub_region layer and 103 | sub_seq layer. For sub_seq layer, 'sequence_offset' and 104 | 'sequence_length' fields are appended. For each scale_sub_region layer 105 | 'convN_index_range' field is appended. 106 | 107 | :param feeding_dict: Feeding is a map of field name and tuple index 108 | of the data that reader returns. 109 | :type feeding_dict: dict|list 110 | :return: Adapted feeding dict. 111 | :rtype: dict|list 112 | """ 113 | adapted_feeding_dict = copy.deepcopy(feeding_dict) 114 | if isinstance(feeding_dict, dict): 115 | adapted_feeding_dict["sequence_offset"] = len(adapted_feeding_dict) 116 | adapted_feeding_dict["sequence_length"] = len(adapted_feeding_dict) 117 | for i in xrange(self._num_conv_layers): 118 | adapted_feeding_dict["conv%d_index_range" %i] = \ 119 | len(adapted_feeding_dict) 120 | elif isinstance(feeding_dict, list): 121 | adapted_feeding_dict.append("sequence_offset") 122 | adapted_feeding_dict.append("sequence_length") 123 | for i in xrange(self._num_conv_layers): 124 | adapted_feeding_dict.append("conv%d_index_range" % i) 125 | else: 126 | raise ValueError("Type of feeding_dict is %s, not supported." % 127 | type(feeding_dict)) 128 | 129 | return adapted_feeding_dict 130 | 131 | def _adapt_data(self, data): 132 | """Adapt data according to network struct. 133 | 134 | For each convolution layer in the conv_group, to remove impacts from 135 | padding data, we can multiply zero to the padding part of the outputs 136 | of each batch normalization layer. We add a scale_sub_region layer after 137 | each batch normalization layer to reset the padding data. 138 | For rnn layers, to remove impacts from padding data, we can truncate the 139 | padding part before output data feeded into the first rnn layer. We use 140 | sub_seq layer to achieve this. 141 | 142 | :param data: Data from data_provider. 143 | :type data: list|function 144 | :return: Adapted data. 145 | :rtype: list|function 146 | """ 147 | 148 | def adapt_instance(instance): 149 | if len(instance) < 2 or len(instance) > 3: 150 | raise ValueError("Size of instance should be 2 or 3.") 151 | padded_audio = instance[0] 152 | text = instance[1] 153 | # no padding part 154 | if len(instance) == 2: 155 | audio_len = padded_audio.shape[1] 156 | else: 157 | audio_len = instance[2] 158 | adapted_instance = [padded_audio, text] 159 | # Stride size for conv0 is (3, 2) 160 | # Stride size for conv1 to convN is (1, 2) 161 | # Same as the network, hard-coded here 162 | padded_conv0_h = (padded_audio.shape[0] - 1) // 2 + 1 163 | padded_conv0_w = (padded_audio.shape[1] - 1) // 3 + 1 164 | valid_w = (audio_len - 1) // 3 + 1 165 | adapted_instance += [ 166 | [0], # sequence offset, always 0 167 | [valid_w], # valid sequence length 168 | # Index ranges for channel, height and width 169 | # Please refer scale_sub_region layer to see details 170 | [1, 32, 1, padded_conv0_h, valid_w + 1, padded_conv0_w] 171 | ] 172 | pre_padded_h = padded_conv0_h 173 | for i in xrange(self._num_conv_layers - 1): 174 | padded_h = (pre_padded_h - 1) // 2 + 1 175 | pre_padded_h = padded_h 176 | adapted_instance += [ 177 | [1, 32, 1, padded_h, valid_w + 1, padded_conv0_w] 178 | ] 179 | return adapted_instance 180 | 181 | if isinstance(data, list): 182 | return map(adapt_instance, data) 183 | elif inspect.isgeneratorfunction(data): 184 | 185 | def adapted_reader(): 186 | for instance in data(): 187 | yield map(adapt_instance, instance) 188 | 189 | return adapted_reader 190 | else: 191 | raise ValueError("Type of data is %s, not supported." % type(data)) 192 | 193 | def _create_parameters(self, model_path=None): 194 | """Load or create model parameters.""" 195 | if model_path is None: 196 | self._parameters = paddle.parameters.create(self._loss) 197 | else: 198 | self._parameters = paddle.parameters.Parameters.from_tar( 199 | gzip.open(model_path)) 200 | 201 | def _create_network(self, vocab_size, num_conv_layers, num_rnn_layers, 202 | rnn_layer_size, use_gru, share_rnn_weights): 203 | """Create data layers and model network.""" 204 | # paddle.data_type.dense_array is used for variable batch input. 205 | # The size 161 * 161 is only an placeholder value and the real shape 206 | # of input batch data will be induced during training. 207 | audio_data = paddle.layer.data( 208 | name="audio_spectrogram", 209 | type=paddle.data_type.dense_array(161 * 161)) 210 | text_data = paddle.layer.data( 211 | name="transcript_text", 212 | type=paddle.data_type.integer_value_sequence(vocab_size)) 213 | seq_offset_data = paddle.layer.data( 214 | name='sequence_offset', 215 | type=paddle.data_type.integer_value_sequence(1)) 216 | seq_len_data = paddle.layer.data( 217 | name='sequence_length', 218 | type=paddle.data_type.integer_value_sequence(1)) 219 | index_range_datas = [] 220 | for i in xrange(num_rnn_layers): 221 | index_range_datas.append( 222 | paddle.layer.data( 223 | name='conv%d_index_range' % i, 224 | type=paddle.data_type.dense_vector(6))) 225 | 226 | self._log_probs, self._loss = deep_speech_v2_network( 227 | audio_data=audio_data, 228 | text_data=text_data, 229 | seq_offset_data=seq_offset_data, 230 | seq_len_data=seq_len_data, 231 | index_range_datas=index_range_datas, 232 | dict_size=vocab_size, 233 | num_conv_layers=num_conv_layers, 234 | num_rnn_layers=num_rnn_layers, 235 | rnn_size=rnn_layer_size, 236 | use_gru=use_gru, 237 | share_rnn_weights=share_rnn_weights) 238 | -------------------------------------------------------------------------------- /utils/decoder/model.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/utils/decoder/model.pyc -------------------------------------------------------------------------------- /utils/decoder/swig_wrapper.py: -------------------------------------------------------------------------------- 1 | """Wrapper for various CTC decoders in SWIG.""" 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | 6 | import swig_decoders 7 | 8 | 9 | class Scorer(swig_decoders.Scorer): 10 | """Wrapper for Scorer. 11 | 12 | :param alpha: Parameter associated with language model. Don't use 13 | language model when alpha = 0. 14 | :type alpha: float 15 | :param beta: Parameter associated with word count. Don't use word 16 | count when beta = 0. 17 | :type beta: float 18 | :model_path: Path to load language model. 19 | :type model_path: basestring 20 | """ 21 | 22 | def __init__(self, alpha, beta, model_path, vocabulary): 23 | swig_decoders.Scorer.__init__(self, alpha, beta, model_path, vocabulary) 24 | 25 | 26 | def ctc_greedy_decoder(probs_seq, vocabulary): 27 | """Wrapper for ctc best path decoder in swig. 28 | 29 | :param probs_seq: 2-D list of probability distributions over each time 30 | step, with each element being a list of normalized 31 | probabilities over vocabulary and blank. 32 | :type probs_seq: 2-D list 33 | :param vocabulary: Vocabulary list. 34 | :type vocabulary: list 35 | :return: Decoding result string. 36 | :rtype: basestring 37 | """ 38 | result = swig_decoders.ctc_greedy_decoder(probs_seq.tolist(), vocabulary) 39 | return result.decode('utf-8') 40 | 41 | 42 | def ctc_beam_search_decoder(probs_seq, 43 | vocabulary, 44 | beam_size, 45 | cutoff_prob=1.0, 46 | cutoff_top_n=40, 47 | ext_scoring_func=None): 48 | """Wrapper for the CTC Beam Search Decoder. 49 | 50 | :param probs_seq: 2-D list of probability distributions over each time 51 | step, with each element being a list of normalized 52 | probabilities over vocabulary and blank. 53 | :type probs_seq: 2-D list 54 | :param vocabulary: Vocabulary list. 55 | :type vocabulary: list 56 | :param beam_size: Width for beam search. 57 | :type beam_size: int 58 | :param cutoff_prob: Cutoff probability in pruning, 59 | default 1.0, no pruning. 60 | :type cutoff_prob: float 61 | :param cutoff_top_n: Cutoff number in pruning, only top cutoff_top_n 62 | characters with highest probs in vocabulary will be 63 | used in beam search, default 40. 64 | :type cutoff_top_n: int 65 | :param ext_scoring_func: External scoring function for 66 | partially decoded sentence, e.g. word count 67 | or language model. 68 | :type external_scoring_func: callable 69 | :return: List of tuples of log probability and sentence as decoding 70 | results, in descending order of the probability. 71 | :rtype: list 72 | """ 73 | beam_results = swig_decoders.ctc_beam_search_decoder( 74 | probs_seq.tolist(), vocabulary, beam_size, cutoff_prob, cutoff_top_n, 75 | ext_scoring_func) 76 | beam_results = [(res[0], res[1].decode('utf-8')) for res in beam_results] 77 | return beam_results 78 | 79 | 80 | def ctc_beam_search_decoder_batch(probs_split, 81 | vocabulary, 82 | beam_size, 83 | num_processes, 84 | cutoff_prob=1.0, 85 | cutoff_top_n=40, 86 | ext_scoring_func=None): 87 | """Wrapper for the batched CTC beam search decoder. 88 | 89 | :param probs_seq: 3-D list with each element as an instance of 2-D list 90 | of probabilities used by ctc_beam_search_decoder(). 91 | :type probs_seq: 3-D list 92 | :param vocabulary: Vocabulary list. 93 | :type vocabulary: list 94 | :param beam_size: Width for beam search. 95 | :type beam_size: int 96 | :param num_processes: Number of parallel processes. 97 | :type num_processes: int 98 | :param cutoff_prob: Cutoff probability in vocabulary pruning, 99 | default 1.0, no pruning. 100 | :type cutoff_prob: float 101 | :param cutoff_top_n: Cutoff number in pruning, only top cutoff_top_n 102 | characters with highest probs in vocabulary will be 103 | used in beam search, default 40. 104 | :type cutoff_top_n: int 105 | :param num_processes: Number of parallel processes. 106 | :type num_processes: int 107 | :param ext_scoring_func: External scoring function for 108 | partially decoded sentence, e.g. word count 109 | or language model. 110 | :type external_scoring_function: callable 111 | :return: List of tuples of log probability and sentence as decoding 112 | results, in descending order of the probability. 113 | :rtype: list 114 | """ 115 | probs_split = [probs_seq.tolist() for probs_seq in probs_split] 116 | 117 | batch_beam_results = swig_decoders.ctc_beam_search_decoder_batch( 118 | probs_split, vocabulary, beam_size, num_processes, cutoff_prob, 119 | cutoff_top_n, ext_scoring_func) 120 | batch_beam_results = [ 121 | [(res[0], res[1].decode("utf-8")) for res in beam_results] 122 | for beam_results in batch_beam_results 123 | ] 124 | return batch_beam_results 125 | -------------------------------------------------------------------------------- /utils/decoder/swig_wrapper.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/utils/decoder/swig_wrapper.pyc -------------------------------------------------------------------------------- /utils/utility.py: -------------------------------------------------------------------------------- 1 | """Contains common utility functions.""" 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | 6 | ''' Help to show args ''' 7 | import distutils.util 8 | 9 | def print_arguments(args): 10 | """Print argparse's arguments. 11 | 12 | Usage: 13 | 14 | .. code-block:: python 15 | 16 | parser = argparse.ArgumentParser() 17 | parser.add_argument("name", default="Jonh", type=str, help="User name.") 18 | args = parser.parse_args() 19 | print_arguments(args) 20 | 21 | :param args: Input argparse.Namespace for printing. 22 | :type args: argparse.Namespace 23 | """ 24 | print("----------- Configuration Arguments -----------") 25 | for arg, value in sorted(vars(args).iteritems()): 26 | print("%s: %s" % (arg, value)) 27 | print("------------------------------------------------") 28 | 29 | 30 | def add_arguments(argname, type, default, help, argparser, **kwargs): 31 | """Add argparse's argument. 32 | 33 | Usage: 34 | 35 | .. code-block:: python 36 | 37 | parser = argparse.ArgumentParser() 38 | add_argument("name", str, "Jonh", "User name.", parser) 39 | args = parser.parse_args() 40 | """ 41 | type = distutils.util.strtobool if type == bool else type 42 | argparser.add_argument( 43 | "--" + argname, 44 | default=default, 45 | type=type, 46 | help=help + ' Default: %(default)s.', 47 | **kwargs) 48 | -------------------------------------------------------------------------------- /utils/utility.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pelhans/ZASR_tensorflow/f7851a9ecab10a75dad22f5c013c7716c0407497/utils/utility.pyc --------------------------------------------------------------------------------