├── .gitignore ├── LICENSE ├── README.md ├── ailearn ├── RL │ ├── Environment.py │ ├── TabularRL.py │ └── __init__.py ├── Swarm │ ├── AFSA.py │ ├── EAS.py │ ├── Evaluation.py │ ├── FA.py │ ├── ModifiedPSO.py │ ├── PSO.py │ └── __init__.py ├── __init__.py ├── crawler.py ├── example │ ├── Reinforcement learning.py │ └── Swarm.py ├── ml.py ├── nn │ ├── __init__.py │ ├── activations.py │ └── losses.py ├── text.py └── utils │ ├── __init__.py │ ├── distances.py │ ├── metrics.py │ └── utilities.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /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 | 此为ailearn人工智能算法包。包含了Swarm、RL、nn、utils四个模块。 2 | 3 | - Swarm模块当中,实现了粒子群算法、人工鱼群算法、萤火虫算法和进化策略,以及一些对智能算法进行评估的常用待优化的函数。 4 | - RL模块包括两部分,TabularRL部分和Environment部分。TabularRL部分集成了一些经典的强化学习算法,包括Q学习、Q(Lambda)、Sarsa、Sarsa(lambda)、Dyna-Q等。Environment部分集成了一些强化学习经典的测试环境,如FrozenLake问题、CliffWalking问题、GridWorld问题等。 5 | - nn模块包括一些常用的激活函数及损失函数。 6 | - utils模块包括一些常用的功能,包括距离度量、评估函数、PCA算法、标签值与one-hot编码的相互转换、Friedman检测等等。 7 | 8 | 安装方式(在终端中输入): 9 | 10 | ```shell 11 | pip install ailearn 12 | ``` 13 | 14 | 更新方式(在终端中输入): 15 | 16 | ```shell 17 | pip install ailearn --upgrade 18 | ``` 19 | 20 | 更新历史: 21 | 22 | - 2018.4.10 0.1.3 第一个版本,首次实现了粒子群算法和人工鱼群算法,首次集成到pip当中。 23 | - 2018.4.16 0.1.4 加入了进化策略的实现,添加了Evaluation模块。 24 | - 2018.4.18 0.1.5 添加了TabularRL模块和Environment模块。 25 | - 2018.4.19 0.1.8 将TabularRL模块和Environment模块整合为RL模块,添加了项目的相关描述,更新了相关协议 26 | - 2018.4.25 0.1.9 输出信息由中文改为英文,并更新了一些已知错误 27 | - 2019.1.15 0.2.0 添加了utils模块,加入了一些常用的功能,包括距离度量、评估函数、PCA算法、标签值与one-hot编码的相互转换、Friedman检测等等;添加了nn模块,加入了一些常用的激活函数及损失函数;更新了Swarm模块的算法,使它们更新得更快。 28 | - 2021.4.6 0.2.1 添加了爬虫工具,增加了RL模块与Swarm模块的示例;添加强化学习经典环境Windy GridWorld环境。 29 | 30 | 31 | 32 | 其他更新: 33 | 34 | - 35 | 36 | 37 | 38 | 项目网址: 39 | 40 | https://pypi.org/project/ailearn/ 41 | 42 | https://github.com/zhaoxy-ml/ailearn/ 43 | -------------------------------------------------------------------------------- /ailearn/RL/Environment.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | # -*- coding: utf-8 -*- 17 | 18 | 19 | # CliffWalking环境 20 | class CliffWalking: 21 | def __init__(self): 22 | self.state = 0 23 | self.n_actions = 4 24 | self.n_states = 48 25 | 26 | def reset(self): 27 | self.state = 0 28 | return self.state 29 | 30 | def step(self, a): 31 | if a == 0: # 往左走 32 | if not (self.state % 12 == 0): 33 | self.state -= 1 34 | elif a == 1: # 往上走 35 | if not (self.state > 35): 36 | self.state += 12 37 | elif a == 2: # 向右走 38 | if not ((self.state + 1) % 12 == 0): 39 | self.state += 1 40 | else: # 往下走 41 | if not (self.state < 12): 42 | self.state -= 12 43 | if 0 < self.state < 11: 44 | reward = -100 45 | else: 46 | reward = -1 47 | if 0 < self.state < 12: 48 | done = True 49 | else: 50 | done = False 51 | return self.state, reward, done, None 52 | 53 | 54 | # FrozenLake环境 55 | class FrozenLake: 56 | def __init__(self, n): 57 | self.state = 0 58 | self.n = n 59 | self.n_actions = 4 60 | self.n_states = n * n 61 | 62 | def reset(self): 63 | self.state = 0 64 | return self.state 65 | 66 | def step(self, a): 67 | # 执行动作 68 | if a == 0: # 往左走 69 | if not (self.state % self.n == 0): 70 | self.state -= 1 71 | elif a == 1: # 往上走 72 | if not (self.state < self.n): 73 | self.state -= self.n 74 | elif a == 2: # 向右走 75 | if not ((self.state + 1) % self.n == 0): 76 | self.state += 1 77 | else: # 向下走 78 | if not (self.state + 1 > self.n * (self.n - 1)): 79 | self.state += self.n 80 | # 其他操作 81 | if self.n == 4: 82 | if self.state == 5 or self.state == 7 or self.state == 11 or self.state == 12: 83 | reward = -10 84 | done = True 85 | elif self.state == 15: 86 | reward = 10 87 | done = True 88 | else: 89 | reward = -1 90 | done = False 91 | return self.state, reward, done, None 92 | elif self.n == 5: 93 | if self.state == 6 or self.state == 8 or self.state == 10 or self.state == 19 or self.state == 22: 94 | reward = -15 95 | done = True 96 | elif self.state == 24: 97 | reward = 15 98 | done = True 99 | else: 100 | reward = -1 101 | done = False 102 | return self.state, reward, done, None 103 | elif self.n == 6: 104 | if self.state == 13 or self.state == 14 or self.state == 16 or self.state == 25 or self.state == 27 or self.state == 29 or self.state == 30: 105 | reward = -20 106 | done = True 107 | elif self.state == 35: 108 | reward = 20 109 | done = True 110 | else: 111 | reward = -1 112 | done = False 113 | return self.state, reward, done, None 114 | elif self.n == 7: 115 | if self.state == 16 or self.state == 20 or self.state == 21 or self.state == 25 or self.state == 29 or self.state == 32 or self.state == 39 or self.state == 41 or self.state == 44: 116 | reward = -25 117 | done = True 118 | elif self.state == 48: 119 | reward = 25 120 | done = True 121 | else: 122 | reward = -1 123 | done = False 124 | return self.state, reward, done, None 125 | elif self.n == 8: 126 | if self.state == 16 or self.state == 25 or self.state == 27 or self.state == 31 or self.state == 37 or self.state == 38 or self.state == 42 or self.state == 44 or self.state == 54 or self.state == 61: 127 | reward = -30 128 | done = True 129 | elif self.state == 63: 130 | reward = 30 131 | done = True 132 | else: 133 | reward = -1 134 | done = False 135 | return self.state, reward, done, None 136 | elif self.n == 9: 137 | if self.state == 28 or self.state == 38 or self.state == 40 or self.state == 44 or self.state == 51 or self.state == 52 or self.state == 54 or self.state == 55 or self.state == 57 or self.state == 59 or self.state == 70 or self.state == 78: 138 | reward = -35 139 | done = True 140 | elif self.state == 80: 141 | reward = 35 142 | done = True 143 | else: 144 | reward = -1 145 | done = False 146 | return self.state, reward, done, None 147 | 148 | 149 | # GridWorld环境 150 | class GridWorld: 151 | def __init__(self, n): 152 | self.state = 0 153 | self.n = n 154 | self.n_actions = 4 155 | self.n_states = n * n 156 | 157 | def reset(self): 158 | self.state = 0 159 | return self.state 160 | 161 | def step(self, a): 162 | # 执行动作 163 | if a == 0: # 往左走 164 | if self.state == (self.n - 2) * (self.n + 1) / 2: 165 | reward = -5 166 | elif not (self.state % self.n == 0): 167 | reward = -1 168 | self.state -= 1 169 | else: 170 | reward = -5 171 | elif a == 1: # 往上走 172 | if self.n ** 2 / 2 - 1 < self.state < (self.n + 1) * self.n / 2 - 1 or ( 173 | self.n + 1) * self.n / 2 < self.state < (self.n + 2) * self.n / 2: 174 | reward = -5 175 | elif not (self.state < self.n): 176 | reward = -1 177 | self.state -= self.n 178 | else: 179 | reward = -5 180 | elif a == 2: # 向右走 181 | if self.state == (self.n - 2) * (self.n + 1) / 2 + 1: 182 | reward = -5 183 | elif not ((self.state + 1) % self.n == 0): 184 | reward = -1 185 | self.state += 1 186 | else: 187 | reward = -5 188 | else: # 向下走 189 | if self.n * (self.n - 4) / 2 - 1 < self.state < self.n * (self.n - 3) / 2 - 1 or ( 190 | self.n - 3) * self.n / 2 < self.state < (self.n - 2) * self.n / 2: 191 | reward = -5 192 | elif not (self.state + 1 > self.n * (self.n - 1)): 193 | reward = -1 194 | self.state += self.n 195 | else: 196 | reward = -5 197 | # 其他操作 198 | done = False 199 | if self.state == self.n ** 2 - 1: 200 | reward = self.n ** 2 201 | done = True 202 | return self.state, reward, done, None 203 | 204 | 205 | # WindyGridWorld环境 206 | class WindyGridWorld: 207 | def __init__(self): 208 | self.state = 30 209 | self.n_actions = 4 210 | self.n_states = 70 211 | 212 | def reset(self): 213 | self.state = 30 214 | return self.state 215 | 216 | def step(self, a): 217 | # 风力处理 218 | if self.state % 10 == 3 or self.state % 10 == 4 or self.state % 10 == 5 or self.state % 10 == 8: 219 | self.state += 10 220 | elif self.state % 10 == 6 or self.state % 10 == 7: 221 | self.state += 20 222 | if self.state > 70: 223 | self.state = 60 + self.state % 10 224 | # 执行动作 225 | if a == 0: # 往左走 226 | if not self.state % 10 == 0: 227 | self.state -= 1 228 | elif a == 1: # 往上走 229 | if not self.state >= 60: 230 | self.state += 10 231 | elif a == 2: # 向右走 232 | if not self.state % 10 == 9: 233 | self.state += 1 234 | else: # 向下走 235 | if not self.state < 10: 236 | self.state -= 10 237 | # 其他操作 238 | if self.state == 37: 239 | reward = 0 240 | done = True 241 | else: 242 | reward = -1 243 | done = False 244 | return self.state, reward, done, None 245 | 246 | def render(self): 247 | for i in range(6, -1, -1): 248 | a = [] 249 | for j in range(10): 250 | a.append('x') 251 | if i == 3: 252 | a[0] = 'S' 253 | a[7] = 'G' 254 | if self.state // 10 == i: 255 | a[self.state % 10] = 'o' 256 | print(' '.join(a)) 257 | -------------------------------------------------------------------------------- /ailearn/RL/TabularRL.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import numpy as np 3 | import pandas as pd 4 | import warnings 5 | 6 | np.random.seed(1) 7 | 8 | 9 | class TabularRL(object): 10 | def __init__(self, n_actions, n_states, learning_rate=0.1, reward_decay=0.9, method='e_greedy', e_greedy=0.01): 11 | self.n_actions = n_actions 12 | self.n_states = n_states 13 | self.lr = learning_rate 14 | self.gamma = reward_decay 15 | self.method = method 16 | self.epsilon = e_greedy 17 | self.q_table = np.zeros([n_states, n_actions]) 18 | if self.method != 'e_greedy' and self.method != 'Boltzmann': 19 | warnings.warn('No such method, using e_greedy method.') 20 | self.method = 'e_greedy' 21 | 22 | def choose_action(self, s): 23 | if self.method == 'Boltzmann': 24 | q_value = self.q_table[s, :] 25 | exp_q_value = np.exp(q_value) 26 | action_prob = exp_q_value / np.sum(exp_q_value) 27 | return np.random.choice(self.n_actions, p=action_prob) 28 | else: 29 | if np.random.uniform() < self.epsilon: 30 | a = np.random.choice(self.n_actions) 31 | else: 32 | action_list = [] 33 | max = np.max(self.q_table[s, :]) 34 | for i in range(self.n_actions): 35 | if self.q_table[s, i] == max: 36 | action_list.append(i) 37 | a = np.random.choice(action_list) 38 | return a 39 | 40 | def learn(self, *args): 41 | pass 42 | 43 | 44 | class QLearning(TabularRL): 45 | def learn(self, s, a, r, s_, done): 46 | q_predict = self.q_table[s, a] 47 | if done: 48 | q_target = r 49 | else: 50 | q_target = r + self.gamma * np.max(self.q_table[s_, :]) 51 | self.q_table[s, a] += self.lr * (q_target - q_predict) 52 | 53 | 54 | class SarsaLearning(TabularRL): 55 | def learn(self, s, a, r, s_, a_, done): 56 | q_predict = self.q_table[s, a] 57 | if done: 58 | q_target = r 59 | else: 60 | q_target = r + self.gamma * self.q_table[s_, a_] 61 | self.q_table[s, a] += self.lr * (q_target - q_predict) 62 | 63 | 64 | class SarsaLambda(TabularRL): 65 | def __init__(self, n_actions, n_states, learning_rate=0.1, reward_decay=0.9, method='e_greedy', e_greedy=0.01, 66 | trace_decay=0.9, style='replacing'): 67 | super(SarsaLambda, self).__init__(n_actions, n_states, learning_rate, reward_decay, method, e_greedy) 68 | self.lambda_ = trace_decay 69 | self.eligibility_trace = self.q_table.copy() 70 | self.style = style 71 | if self.style != 'replacing' and self.style != 'accumulating': 72 | warnings.warn('No such style, using replacing updating method.') 73 | self.style = 'replacing' 74 | 75 | def learn(self, s, a, r, s_, a_, done): 76 | q_predict = self.q_table[s, a] 77 | if done: 78 | q_target = r 79 | else: 80 | q_target = r + self.gamma * self.q_table[s_, a_] 81 | error = q_target - q_predict 82 | if self.style == 'replacing': 83 | self.eligibility_trace[s, :] *= 0 84 | self.eligibility_trace[s, a] = 1 85 | else: 86 | self.eligibility_trace[s, a] += 1 87 | self.q_table += self.lr * error * self.eligibility_trace 88 | self.eligibility_trace *= self.gamma * self.lambda_ 89 | 90 | 91 | class QLambda(TabularRL): 92 | def __init__(self, n_actions, n_states, learning_rate=0.1, reward_decay=0.9, method='e_greedy', e_greedy=0.01, 93 | trace_decay=0.9, style='replacing'): 94 | super(QLambda, self).__init__(n_actions, n_states, learning_rate, reward_decay, method, e_greedy) 95 | self.lambda_ = trace_decay 96 | self.eligibility_trace = self.q_table.copy() 97 | self.style = style 98 | if self.style != 'replacing' and self.style != 'accumulating': 99 | warnings.warn('No such style, using replacing updating method.') 100 | self.style = 'replacing' 101 | 102 | def learn(self, s, a, r, s_, done): 103 | q_predict = self.q_table[s, a] 104 | if done: 105 | q_target = r 106 | else: 107 | q_target = r + self.gamma * np.max(self.q_table[s_, :]) 108 | error = q_target - q_predict 109 | if self.style == 'replacing': 110 | self.eligibility_trace[s, :] *= 0 111 | self.eligibility_trace[s, a] = 1 112 | else: 113 | self.eligibility_trace[s, a] += 1 114 | self.q_table += self.lr * error * self.eligibility_trace 115 | self.eligibility_trace *= self.gamma * self.lambda_ 116 | 117 | 118 | class EnvModel(object): 119 | def __init__(self, n_actions): 120 | self.n_actions = n_actions 121 | self.database = pd.DataFrame(columns=n_actions, dtype=np.object) 122 | 123 | def store_transition(self, s, a, r, s_, done): 124 | if s not in self.database.index: 125 | self.database = self.database.append( 126 | pd.Series( 127 | [None] * len(self.n_actions), 128 | index=self.database.columns, 129 | name=s 130 | )) 131 | self.database.set_value(s, a, (r, s_, done)) 132 | 133 | def sample_s_a(self): 134 | s = np.random.choice(self.database.index) 135 | a = np.random.choice(self.database.ix[s].dropna().index) # filter out the None value 136 | return s, a 137 | 138 | def get_r_s_(self, s, a): 139 | r, s_, done = self.database.ix[s, a] 140 | return r, s_, done 141 | 142 | 143 | class DynaQ(TabularRL): 144 | def __init__(self, n_actions, n_states, learning_rate=0.1, reward_decay=0.9, method='e_greedy', e_greedy=0.01, 145 | learning_n=5): 146 | super(DynaQ, self).__init__(n_actions, n_states, learning_rate, reward_decay, method, e_greedy) 147 | self.model = EnvModel(list(range(n_actions))) 148 | self.learning_n = learning_n 149 | 150 | def learn(self, s, a, r, s_, done): 151 | self.model.store_transition(s, a, r, s_, done) 152 | q_predict = self.q_table[s, a] 153 | if done: 154 | q_target = r 155 | else: 156 | q_target = r + self.gamma * np.max(self.q_table[s_, :]) 157 | self.q_table[s, a] += self.lr * (q_target - q_predict) 158 | for i in range(self.learning_n): 159 | s, a = self.model.sample_s_a() 160 | r, s_, done = self.model.get_r_s_(s, a) 161 | q_predict = self.q_table[s, a] 162 | if done: 163 | q_target = r 164 | else: 165 | q_target = r + self.gamma * np.max(self.q_table[s_, :]) 166 | self.q_table[s, a] += self.lr * (q_target - q_predict) 167 | -------------------------------------------------------------------------------- /ailearn/RL/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # -*- coding: utf-8 -*- -------------------------------------------------------------------------------- /ailearn/Swarm/AFSA.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS-IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # -*- coding: utf-8 -*- 17 | import numpy as np 18 | 19 | 20 | # 人工鱼群算法 21 | class AFSA: 22 | def __init__(self, func=None, param_len=1, size=50, x_min=-10., x_max=10., visual=1., step=0.5, delta=1, 23 | try_number=5): 24 | self.func = func # 计算适应性系数的方法 25 | self.param_len = param_len # 参数个数 26 | self.size = size # 有多少鱼 27 | self.history = [] # 每次迭代的最优值 28 | # 最小位移 29 | if type(x_min) != list: 30 | self.x_min = [x_min] * self.param_len 31 | else: 32 | assert len(x_min) == self.param_len # 参数个数错误 33 | self.x_min = x_min 34 | # 最大位移 35 | if type(x_max) != list: 36 | self.x_max = [x_max] * self.param_len 37 | else: 38 | assert len(x_max) == self.param_len # 参数个数错误 39 | self.x_max = x_max 40 | self.visual = visual # 鱼的视野 41 | self.step = step # 鱼的步长 42 | self.delta = delta # 拥挤因子 43 | self.try_number = try_number # 觅食行为的尝试次数 44 | self.x = None # 位移 45 | self.score = None # 每条鱼的分数 46 | self.best_all_x = None # 全局最优位置 47 | self.best_all_score = None # 全局最优分数 48 | self._init_fit() 49 | 50 | def _init_fit(self): 51 | self.x = np.zeros([self.size, self.param_len]) # 形状为[鱼的个数,参数个数] 52 | self.score = np.zeros(self.size) 53 | for i in range(self.size): 54 | for j in range(self.param_len): 55 | self.x[i][j] = np.random.uniform(self.x_min[j], self.x_max[j]) 56 | self.score[i] = self.func(*self.x[i]) 57 | self.best_all_score = np.max(self.score) # 全局最优分数 58 | self.best_all_x = self.x[np.argmax(self.score)] # 全局最优位置 59 | 60 | def solve(self, epoch=50, verbose=False): 61 | self.history = [] 62 | for _ in range(epoch): # 一共迭代_次 63 | for i in range(self.size): # 对于第i条鱼 64 | # 检测周围有没有鱼 65 | fishes = [] 66 | fish_idx = [] 67 | fish_num = 0 68 | for j in range(self.size): # 搜索附近有多少鱼 69 | if np.sum(np.square(self.x[i] - self.x[j])) < self.visual: 70 | fish_num += 1 71 | fishes.append(self.x[j]) 72 | fish_idx.append(j) 73 | if fish_num > 1: # 如果周围有鱼 74 | fishes = np.array(fishes) 75 | # 聚群行为 76 | centre = np.mean(fishes, 0) 77 | if self.func(*centre) / fish_num > self.delta * self.score[i]: 78 | self.x[i] += (centre - self.x[i]) / (np.sum( 79 | np.square(centre - self.x[i])) + 10e-8) * self.step * np.random.rand() 80 | self.x[i] = np.clip(self.x[i], self.x_min, self.x_max) 81 | self.score[i] = self.func(*self.x[i]) 82 | # 更新局部最优值、局部最优位置 83 | if self.score[i] > self.best_all_score: 84 | self.best_all_score = self.score[i] 85 | self.best_all_x = self.x[i].copy() 86 | continue 87 | # 追尾行为 88 | best_index = fish_idx[np.argmax(self.score[fish_idx])] 89 | if self.score[best_index] / fish_num > self.delta * self.score[i]: 90 | self.x[i] += (self.x[best_index] - self.x[i]) / np.sum( 91 | np.square(self.x[best_index] - self.x[i])) * self.step * np.random.rand() 92 | self.x[i] = np.clip(self.x[i], self.x_min, self.x_max) 93 | self.score[i] = self.func(*self.x[i]) 94 | # 更新局部最优值、局部最优位置 95 | if self.score[i] > self.best_all_score: 96 | self.best_all_score = self.score[i] 97 | self.best_all_x = self.x[i].copy() 98 | continue 99 | # 觅食行为 100 | find = False 101 | x = None 102 | for j in range(self.try_number): 103 | x = self.x[i] + self.visual * np.random.uniform(-1, 1, self.param_len) 104 | x = np.clip(x, self.x_min, self.x_max) 105 | if self.func(*x) > self.score[i]: 106 | find = True 107 | break 108 | if find is True: 109 | self.x[i] += (x - self.x[i]) / np.sum(np.square(x - self.x[i])) * self.step * np.random.rand() 110 | else: 111 | self.x[i] += self.visual * np.random.uniform(-1, 1) 112 | self.x[i] = np.clip(self.x[i], self.x_min, self.x_max) 113 | self.score[i] = self.func(*self.x[i]) 114 | # 更新局部最优值、局部最优位置 115 | if self.score[i] > self.best_all_score: 116 | self.best_all_score = self.score[i] 117 | self.best_all_x = self.x[i].copy() 118 | if verbose: 119 | # print('已完成第%i次寻找,最优参数值为' % (_ + 1), self.best_all_x, '目前最优适合度为%.4f' % self.best_all_score) 120 | print('Number of iterations: %i. The optimal parameters:' % (_ + 1), self.best_all_x, 121 | 'Best fitness: %.4f' % self.best_all_score) 122 | self.history.append(self.best_all_score) 123 | self.history = np.array(self.history) 124 | return self.best_all_x 125 | -------------------------------------------------------------------------------- /ailearn/Swarm/EAS.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # -*- coding: utf-8 -*- 16 | # 若干进化算法的Python实现 17 | import numpy as np 18 | 19 | 20 | # 进化策略 21 | class ES: 22 | def __init__(self, func=None, param_len=1, size=50, x_min=-10., x_max=10., alpha=0.5): 23 | self.func = func # 计算适应性系数的方法 24 | self.param_len = param_len # 参数个数 25 | self.size = size # 有多少元素 26 | self.alpha = alpha # 淘汰率 27 | self.history = [] # 每次迭代的最优值 28 | assert np.floor(self.size * (1 - self.alpha)) > 1 29 | # 最小数值 30 | if type(x_min) != list: 31 | self.x_min = [x_min] * self.param_len 32 | else: 33 | assert len(x_min) == self.param_len 34 | self.x_min = x_min 35 | # 最大数值 36 | if type(x_max) != list: 37 | self.x_max = [x_max] * self.param_len 38 | else: 39 | assert len(x_max) == self.param_len 40 | self.x_max = x_max 41 | self.x = None # 数值 42 | self.v = None # 变化幅度 43 | self.best_all_x = None # 全局最优位置 44 | self.best_all_score = None # 全局最优分数 45 | self.score = None # 得分 46 | self._init_fit() 47 | 48 | def _init_fit(self): 49 | self.x = np.zeros([self.size, self.param_len]) # 形状为[微粒个数,参数个数] 50 | self.v = np.random.uniform(size=[self.size, self.param_len]) 51 | self.score = np.zeros(self.size) 52 | for i in range(self.size): 53 | for j in range(self.param_len): 54 | self.x[i][j] = np.random.uniform(self.x_min[j], self.x_max[j]) 55 | self.best_all_x = np.zeros(self.param_len) # 全局最优位置 56 | self.best_all_score = -np.inf # 全局最优分数 57 | 58 | def solve(self, epoch=50, verbose=False): 59 | self.history = [] 60 | for _ in range(epoch): # 一共迭代_次 61 | self.score = np.zeros(self.size) 62 | # 计算适应度 63 | for i in range(self.size): # 对于第i个微粒 64 | self.score[i] = self.func(*self.x[i]) 65 | # 更新局部最优值、局部最优位置 66 | if np.max(self.score) > self.best_all_score: 67 | self.best_all_score = np.max(self.score) 68 | self.best_all_x = self.x[np.argmax(self.score)] 69 | # 优胜劣汰 70 | for i in range(int(self.size * self.alpha)): 71 | idx = np.argmin(self.score) 72 | self.x = np.delete(self.x, idx, axis=0) 73 | self.v = np.delete(self.v, idx, axis=0) 74 | self.score = np.delete(self.score, idx, axis=0) 75 | # 繁殖 76 | x = np.zeros([self.size, self.param_len]) 77 | v = np.zeros([self.size, self.param_len]) 78 | for i in range(self.size): 79 | a, b = np.random.choice(self.x.shape[0], 2, False) 80 | mean = (self.x[a] + self.x[b]) / 2 81 | v[i] = np.abs(np.random.normal((self.v[a] + self.v[b]) / 2, 0.1)) 82 | x[i] = np.clip(np.random.normal(mean, v[i]), self.x_min, self.x_max) 83 | self.x = x.copy() 84 | self.v = v.copy() 85 | if verbose: 86 | print('Number of iterations: %i.' % (_ + 1), 'Best fitness: %.4f' % self.best_all_score) 87 | self.history.append(self.best_all_score) 88 | self.history = np.array(self.history) 89 | return self.best_all_x 90 | -------------------------------------------------------------------------------- /ailearn/Swarm/Evaluation.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # -*- coding: utf-8 -*- 16 | # 智能算法的若干不同的评价函数 17 | import numpy as np 18 | 19 | 20 | # Ackley函数 21 | class Ackley: 22 | def __init__(self, a=20, b=0.2, c=2 * np.pi): 23 | self.min = -32.768 24 | self.max = 32.768 25 | self.a = a 26 | self.b = b 27 | self.c = c 28 | 29 | def func(self, *x): 30 | x = np.array(x) 31 | mean1 = np.mean(np.square(x)) 32 | mean2 = np.mean(np.cos(self.c * x)) 33 | result = -self.a * np.exp(-self.b * np.sqrt(mean1)) - np.exp(mean2) + self.a + np.e 34 | return -result 35 | 36 | 37 | # Griewank函数 38 | class Griewank: 39 | def __init__(self): 40 | self.min = -600 41 | self.max = 600 42 | 43 | def func(self, *x): 44 | x = np.array(x) 45 | first = np.sum(np.square(x)) / 4000 46 | second = 1 47 | for i in range(x.shape[0]): 48 | second *= np.cos(x[i] / np.sqrt(i + 1)) 49 | result = first - second + 1 50 | return -result 51 | 52 | 53 | # Rastrigin函数 54 | class Rastrigin: 55 | def __init__(self): 56 | self.min = -5.12 57 | self.max = 5.12 58 | 59 | def func(self, *x): 60 | x = np.array(x) 61 | sum = np.sum(np.square(x) - 10 * np.cos(2 * np.pi * x)) 62 | result = 10 * x.shape[0] + sum 63 | return -result 64 | 65 | 66 | # Rosenbrock函数 67 | class Rosenbrock: 68 | def __init__(self): 69 | self.min = -5 70 | self.max = 10 71 | 72 | def func(self, x1, x2): 73 | result = np.square(1 - x1) + 100 * np.square(x2 - np.square(x1)) 74 | return -result 75 | 76 | 77 | # Schwefel函数 78 | class Schwefel: 79 | def __init__(self): 80 | self.min = -500 81 | self.max = 500 82 | 83 | def func(self, *x): 84 | x = np.array(x) 85 | sum = np.sum(x * np.sin(np.sqrt(np.abs(x)))) 86 | result = 418.9829 * x.shape[0] - sum 87 | return -result 88 | 89 | 90 | # Sphere函数 91 | class Sphere: 92 | def __init__(self): 93 | self.min = -100 94 | self.max = 100 95 | 96 | def func(self, *x): 97 | x = np.array(x) 98 | result = np.sum(np.square(x)) 99 | return -result 100 | 101 | 102 | # Schwefel's Problem 1.2 103 | class Schwefel_1: 104 | def __init__(self): 105 | self.min = -100 106 | self.max = 100 107 | 108 | def func(self, *x): 109 | x = np.array(x) 110 | a = np.zeros_like(x) 111 | for i in range(x.shape[0]): 112 | a[i] = x[:i].sum() ** 2 113 | return -a.sum() 114 | 115 | 116 | # Schwefel's Problem 2.22 117 | class Schwefel_2: 118 | def __init__(self): 119 | self.min = -10 120 | self.max = 10 121 | 122 | def func(self, *x): 123 | x = np.array(x) 124 | return -np.sum(np.abs(x)) - np.prod(np.abs(x)) 125 | 126 | 127 | # Branins函数 128 | class Branins: 129 | def __init__(self): 130 | self.min = [-5, 0] 131 | self.max = [10, 15] 132 | 133 | def func(self, x1, x2): 134 | result = (x2 - (5.1 / (4 * np.pi ** 2)) + 5 / np.pi * x1 - 6) ** 2 + 10 * (1 - 1 / 8 / np.pi) * np.cos(x1) + 10 135 | return -result 136 | -------------------------------------------------------------------------------- /ailearn/Swarm/FA.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS-IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # -*- coding: utf-8 -*- 17 | import numpy as np 18 | 19 | 20 | # 萤火虫算法 21 | class FA: 22 | def __init__(self, func=None, param_len=1, size=50, x_min=-10., x_max=10., beta=2, alpha=0.5, gamma=0.9): 23 | self.func = func # 计算适应性系数的方法 24 | self.param_len = param_len # 参数个数 25 | self.size = size # 有多少萤火虫 26 | self.history = [] # 每次迭代的最优值 27 | # 最小位移 28 | if type(x_min) != list: 29 | self.x_min = [x_min] * self.param_len 30 | else: 31 | assert len(x_min) == self.param_len # 参数个数错误 32 | self.x_min = x_min 33 | # 最大位移 34 | if type(x_max) != list: 35 | self.x_max = [x_max] * self.param_len 36 | else: 37 | assert len(x_max) == self.param_len # 参数个数错误 38 | self.x_max = x_max 39 | self.alpha = alpha # 步长 40 | self.beta = beta # 最大吸引度 41 | self.gamma = gamma # 光强吸收系数 42 | self.x = None # 位移 43 | self.score = None # 每个萤火虫的分数 44 | self.best_all_x = None # 全局最优位置 45 | self.best_all_score = None # 全局最优分数 46 | self._init_fit() 47 | 48 | def _init_fit(self): 49 | self.x = np.zeros([self.size, self.param_len]) # 形状为[萤火虫的个数,参数个数] 50 | self.score = np.zeros(self.size) 51 | for i in range(self.size): 52 | for j in range(self.param_len): 53 | self.x[i][j] = np.random.uniform(self.x_min[j], self.x_max[j]) 54 | self.score[i] = self.func(*self.x[i]) 55 | self.best_all_score = np.max(self.score) # 全局最优分数 56 | self.best_all_x = self.x[np.argmax(self.score)] # 全局最优位置 57 | 58 | def solve(self, epoch=50, verbose=False): 59 | self.history = [] 60 | for _ in range(epoch): # 一共迭代_次 61 | # 计算距离 62 | distance = np.zeros([self.size, self.size]) 63 | for i in range(self.size): 64 | for j in range(self.size - 1, i, -1): 65 | distance[i][j] = np.linalg.norm(self.x[i] - self.x[j], 2) 66 | distance[j][i] = distance[i][j] 67 | for i in range(self.size): # 对于第i个萤火虫 68 | # 对于最亮的萤火虫,做随机运动 69 | if np.argmax(self.score) == i: 70 | self.x[i] += self.alpha * np.random.uniform(-0.5, 0.5) 71 | self.x[i] = np.clip(self.x[i], self.x_min, self.x_max) 72 | self.score[i] = self.func(*self.x[i]) 73 | # 更新局部最优值、局部最优位置 74 | if self.score[i] > self.best_all_score: 75 | self.best_all_score = self.score[i] 76 | self.best_all_x = self.x[i].copy() 77 | continue 78 | lightness = np.zeros(self.size) 79 | # 查找对自己相对亮度最大的萤火虫 80 | for j in range(self.size): 81 | if j == i: 82 | lightness[j] = -np.inf 83 | else: 84 | lightness[j] = self.score[j] * np.exp(-self.gamma * distance[i][j]) 85 | idx = np.argmax(lightness) 86 | # 更新萤火虫的位置 87 | self.x[i] += self.beta * np.exp(-self.gamma * np.square(distance[i][idx])) * ( 88 | self.x[idx] - self.x[i]) + self.alpha * np.random.uniform(-0.5, 0.5) 89 | self.score[i] = self.func(*self.x[i]) 90 | self.x[i] = np.clip(self.x[i], self.x_min, self.x_max) 91 | # 更新局部最优值、局部最优位置 92 | if self.score[i] > self.best_all_score: 93 | self.best_all_score = self.score[i] 94 | self.best_all_x = self.x[i].copy() 95 | if verbose: 96 | # print('已完成第%i次寻找,最优参数值为' % (_ + 1), self.best_all_x, '目前最优适合度为%.4f' % self.best_all_score) 97 | print('Number of iterations: %i. The optimal parameters:' % (_ + 1), self.best_all_x, 98 | 'Best fitness: %.4f' % self.best_all_score) 99 | self.history.append(self.best_all_score) 100 | self.history = np.array(self.history) 101 | return self.best_all_x 102 | -------------------------------------------------------------------------------- /ailearn/Swarm/ModifiedPSO.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS-IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # -*- coding: utf-8 -*- 17 | 18 | import numpy as np 19 | 20 | 21 | # 改进的粒子群算法 22 | class ModifiedPSO: 23 | def __init__(self, func=None, param_len=1, size=50, w=0.9, c1=2., c2=2., x_min=-10., x_max=10., v_min=-0.5, 24 | v_max=0.5, r1=None, r2=None): 25 | self.func = func # 计算适应性系数的方法 26 | self.param_len = param_len # 参数个数 27 | self.size = size # 有多少微粒 28 | self.w = w # 惯性系数 29 | self.c1 = c1 # 认知系数 30 | self.c2 = c2 # 社会系数 31 | self.history = [] # 每次迭代的最优值 32 | # 最小位移 33 | if type(x_min) != list: 34 | self.x_min = [x_min] * self.param_len 35 | else: 36 | assert len(x_min) == self.param_len # 参数个数错误 37 | self.x_min = x_min 38 | # 最大位移 39 | if type(x_max) != list: 40 | self.x_max = [x_max] * self.param_len 41 | else: 42 | assert len(x_max) == self.param_len # 参数个数错误 43 | self.x_max = x_max 44 | # 最小速度 45 | if type(v_min) != list: 46 | self.v_min = [v_min] * self.param_len 47 | else: 48 | assert len(v_min) == self.param_len # 参数个数错误 49 | self.v_min = v_min 50 | # 最大速度 51 | if type(v_max) != list: 52 | self.v_max = [v_max] * self.param_len 53 | else: 54 | assert len(v_max) == self.param_len # 参数个数错误 55 | self.v_max = v_max 56 | self.r1 = r1 # 随机数1 57 | self.r2 = r2 # 随机数2 58 | self.x = None # 位移 59 | self.v = None # 速度 60 | self.best_all_x = None # 全局最优位置 61 | self.best_all_score = None # 全局最优分数 62 | self.best_each_x = None # 局部最优位置 63 | self.best_each_score = None # 局部最优分数 64 | self._init_fit() 65 | 66 | def _init_fit(self): 67 | self.x = np.zeros([self.size, self.param_len]) # 形状为[微粒个数,参数个数] 68 | self.v = np.zeros([self.size, self.param_len]) 69 | for i in range(self.size): 70 | for j in range(self.param_len): 71 | self.x[i][j] = np.random.uniform(self.x_min[j], self.x_max[j]) 72 | self.v[i][j] = np.random.uniform(self.v_min[j], self.v_max[j]) 73 | self.best_all_x = np.zeros(self.param_len) # 全局最优位置 74 | self.best_all_score = -np.inf # 全局最优分数 75 | self.best_each_x = self.x.copy() # 局部最优位置 76 | self.best_each_score = np.full(self.size, -np.inf) # 局部最优分数 77 | 78 | def solve(self, epoch=50, verbose=False): 79 | self.history = [] 80 | r1 = self.r1 81 | r2 = self.r2 82 | for _ in range(epoch): # 一共迭代_次 83 | # 配置随机变量 84 | if r1 is None: 85 | r1 = np.random.uniform(0, 1) 86 | if r2 is None: 87 | r2 = np.random.uniform(0, 1) 88 | # 计算适应度 89 | for i in range(self.size): # 对于第i个微粒 90 | fitness = self.func(*self.x[i]) 91 | # 更新局部最优值、局部最优位置 92 | if fitness > self.best_each_score[i]: 93 | self.best_each_score[i] = fitness 94 | self.best_each_x[i] = self.x[i].copy() 95 | if fitness > self.best_all_score: 96 | self.best_all_score = fitness 97 | self.best_all_x = self.x[i].copy() 98 | # 更新微粒的速度和位置 99 | self.v = self.w * self.v + self.c1 * r1 * (self.best_each_x - self.x) + self.c2 * r2 * ( 100 | self.best_all_x - self.x) 101 | for j in range(self.param_len): 102 | self.v[:, j] = np.clip(self.v[:, j], self.v_min[j], self.v_max[j]) 103 | self.x = self.x + self.v 104 | for j in range(self.param_len): 105 | self.x[:, j] = np.clip(self.x[:, j], self.x_min[j], self.x_max[j]) 106 | if verbose: 107 | # print('已完成第%i次寻找,最优参数值为' % (_ + 1), self.best_all_x, '目前最优适合度为%.4f' % self.best_all_score) 108 | print('Number of iterations: %i.' % (_ + 1), 'Best fitness: %.4f' % self.best_all_score) 109 | self.history.append(self.best_all_score) 110 | self.history = np.array(self.history) 111 | return self.best_all_x 112 | -------------------------------------------------------------------------------- /ailearn/Swarm/PSO.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS-IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # -*- coding: utf-8 -*- 17 | 18 | import numpy as np 19 | 20 | 21 | # 粒子群算法 22 | class PSO: 23 | def __init__(self, func=None, param_len=1, size=50, c1=2., c2=2., x_min=-10., x_max=10., v_min=-0.5, v_max=0.5, 24 | r1=None, r2=None): 25 | self.func = func # 计算适应性系数的方法 26 | self.param_len = param_len # 参数个数 27 | self.size = size # 有多少微粒 28 | self.c1 = c1 # 认知系数 29 | self.c2 = c2 # 社会系数 30 | self.history = [] # 每次迭代的最优值 31 | # 最小位移 32 | if type(x_min) != list: 33 | self.x_min = [x_min] * self.param_len 34 | else: 35 | assert len(x_min) == self.param_len # 参数个数错误 36 | self.x_min = x_min 37 | # 最大位移 38 | if type(x_max) != list: 39 | self.x_max = [x_max] * self.param_len 40 | else: 41 | assert len(x_max) == self.param_len # 参数个数错误 42 | self.x_max = x_max 43 | # 最小速度 44 | if type(v_min) != list: 45 | self.v_min = [v_min] * self.param_len 46 | else: 47 | assert len(v_min) == self.param_len # 参数个数错误 48 | self.v_min = v_min 49 | # 最大速度 50 | if type(v_max) != list: 51 | self.v_max = [v_max] * self.param_len 52 | else: 53 | assert len(v_max) == self.param_len # 参数个数错误 54 | self.v_max = v_max 55 | self.r1 = r1 # 随机数1 56 | self.r2 = r2 # 随机数2 57 | self.x = None # 位移 58 | self.v = None # 速度 59 | self.best_all_x = None # 全局最优位置 60 | self.best_all_score = None # 全局最优分数 61 | self.best_each_x = None # 局部最优位置 62 | self.best_each_score = None # 局部最优分数 63 | self._init_fit() 64 | 65 | def _init_fit(self): 66 | self.x = np.zeros([self.size, self.param_len]) # 形状为[微粒个数,参数个数] 67 | self.v = np.zeros([self.size, self.param_len]) 68 | for i in range(self.size): 69 | for j in range(self.param_len): 70 | self.x[i][j] = np.random.uniform(self.x_min[j], self.x_max[j]) 71 | self.v[i][j] = np.random.uniform(self.v_min[j], self.v_max[j]) 72 | self.best_all_x = np.zeros(self.param_len) # 全局最优位置 73 | self.best_all_score = -np.inf # 全局最优分数 74 | self.best_each_x = self.x.copy() # 局部最优位置 75 | self.best_each_score = np.full(self.size, -np.inf) # 局部最优分数 76 | 77 | def solve(self, epoch=50, verbose=False): 78 | self.history = [] 79 | r1 = self.r1 80 | r2 = self.r2 81 | for _ in range(epoch): # 一共迭代_次 82 | # 配置随机变量 83 | if r1 is None: 84 | r1 = np.random.uniform(0, 1) 85 | if r2 is None: 86 | r2 = np.random.uniform(0, 1) 87 | # 计算适应度 88 | for i in range(self.size): # 对于第i个微粒 89 | fitness = self.func(*self.x[i]) 90 | # 更新局部最优值、局部最优位置 91 | if fitness > self.best_each_score[i]: 92 | self.best_each_score[i] = fitness 93 | self.best_each_x[i] = self.x[i].copy() 94 | if fitness > self.best_all_score: 95 | self.best_all_score = fitness 96 | self.best_all_x = self.x[i].copy() 97 | # 更新微粒的速度和位置 98 | self.v = self.v + self.c1 * r1 * (self.best_each_x - self.x) + self.c2 * r2 * (self.best_all_x - self.x) 99 | for j in range(self.param_len): 100 | self.v[:, j] = np.clip(self.v[:, j], self.v_min[j], self.v_max[j]) 101 | self.x = self.x + self.v 102 | for j in range(self.param_len): 103 | self.x[:, j] = np.clip(self.x[:, j], self.x_min[j], self.x_max[j]) 104 | if verbose: 105 | # print('已完成第%i次寻找,最优参数值为' % (_ + 1), self.best_all_x, '目前最优适合度为%.4f' % self.best_all_score) 106 | print('Number of iterations: %i.' % (_ + 1), 'Best fitness: %.4f' % self.best_all_score) 107 | self.history.append(self.best_all_score) 108 | self.history = np.array(self.history) 109 | return self.best_all_x 110 | -------------------------------------------------------------------------------- /ailearn/Swarm/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # -*- coding: utf-8 -*- 16 | 17 | from .ModifiedPSO import ModifiedPSO 18 | from .AFSA import AFSA 19 | from .FA import FA 20 | from .PSO import PSO 21 | from .Evaluation import * 22 | from .EAS import ES -------------------------------------------------------------------------------- /ailearn/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # -*- coding: utf-8 -*- -------------------------------------------------------------------------------- /ailearn/crawler.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from bs4 import BeautifulSoup 3 | import random 4 | 5 | ualist = [ 6 | "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:23.0) Gecko/20131011 Firefox/23.0", 7 | "Mozilla/5.0 (X11; Linux i586; rv:31.0) Gecko/20100101 Firefox/31.0", 8 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1623.0 Safari/537.36", 9 | "Mozilla/5.0 (Windows; U; Windows NT 6.0; nb-NO) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5", 10 | "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.67 Safari/537.36", 11 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36", 12 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36", 13 | "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.14 (KHTML, like Gecko) Chrome/24.0.1292.0 Safari/537.14", 14 | "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; Media Center PC 6.0; InfoPath.3; MS-RTC LM 8; Zune 4.7)", 15 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36", 16 | "Mozilla/5.0 (Windows NT 6.2; rv:21.0) Gecko/20130326 Firefox/21.0", 17 | "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36", 18 | "Mozilla/5.0 (X11; Linux x86_64; rv:28.0) Gecko/20100101 Firefox/28.0", 19 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36' 20 | ] 21 | 22 | 23 | def get_html(url, headers=None): 24 | if headers is not None: 25 | html = requests.get(url, headers=headers) 26 | else: 27 | html = requests.get(url, headers={'User-Agent': random.choice(ualist)}) 28 | html.encoding = 'utf-8' 29 | return html 30 | 31 | 32 | def get_soup(html, features='html.parser'): 33 | ''' 34 | 35 | :param html: class:`Response ` object 36 | :param features: lxml, html.parser or html5lib 37 | :return: 38 | ''' 39 | obj = BeautifulSoup(html.text, features=features) 40 | return obj 41 | 42 | 43 | def get_soup_from_url(url, headers=None, features='html.parser'): 44 | return get_soup(get_html(url, headers=headers), features=features) 45 | -------------------------------------------------------------------------------- /ailearn/example/Reinforcement learning.py: -------------------------------------------------------------------------------- 1 | # Use Q-Learning for solving the FrozenLake problem 2 | from ailearn.RL.TabularRL import QLearning 3 | from ailearn.RL.Environment import FrozenLake 4 | 5 | env = FrozenLake(4) 6 | agent = QLearning(env.n_actions, env.n_states) 7 | MAX_EPISODES = 200 8 | 9 | for episode in range(MAX_EPISODES): 10 | s = env.reset() 11 | total_reward = 0 12 | while True: 13 | a = agent.choose_action(s) 14 | s_, r, done, _ = env.step(a) 15 | agent.learn(s, a, r, s_, done) 16 | s = s_ 17 | total_reward = total_reward + r 18 | if done: 19 | break 20 | print('episode:', episode, 'total_reward:', total_reward) 21 | -------------------------------------------------------------------------------- /ailearn/example/Swarm.py: -------------------------------------------------------------------------------- 1 | # Using PSO for optimization 2 | from ailearn.Swarm import PSO 3 | 4 | 5 | def func(x, y): 6 | return -(x ** 2 + y ** 2) 7 | 8 | 9 | p = PSO(func=func, param_len=2, x_min=[-5, -5], x_max=[5, 5]) 10 | x, y = p.solve() 11 | print(x, y) 12 | -------------------------------------------------------------------------------- /ailearn/ml.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS-IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | import warnings 16 | from .utils import to_categorical 17 | from .nn.activations import * 18 | from sklearn.metrics import r2_score 19 | 20 | 21 | # 核方法(A:[m1,n],B:[m2,n],K:[m1,m2]) 22 | def Kernel(A, B=None, kernel='linear', gamma=0.1, q=0.1, degree=3): 23 | ''' 24 | :param A: 矩阵1 ,形状为[m1,n] 25 | :param B: 矩阵2,形状为[m2,n],取值为None时B=A.copy() 26 | :param kernel: 核的类型,['linear','polynomial'('poly'),'sigmoid','rbf','laplace'] 27 | :param gamma: 多项式核、Sigmoid核、高斯核、拉普拉斯核中的参数 28 | :param q: 多项式核、Sigmoid核、高斯核中的参数 29 | :param degree: 多项式核中的参数 30 | :return: 核运算得到的矩阵[m1,m2] 31 | ''' 32 | A = np.array(A, np.float) 33 | if B is None: 34 | B = A.copy() 35 | B = np.array(B, np.float) 36 | if kernel == 'linear': # 线性核 37 | my_kernel = A.dot(B.T) 38 | elif kernel == 'polynomial' or kernel == 'poly': # 多项式核 39 | my_kernel = (gamma * A.dot(B.T) + q) ** degree 40 | elif kernel == 'sigmoid': # Sigmoid核 41 | my_kernel = np.tanh(gamma * A.dot(B.T) - q) 42 | elif kernel == 'rbf': # 高斯核 43 | rA = np.sum(np.square(A), 1, keepdims=True) 44 | rB = np.sum(np.square(B), 1, keepdims=True) 45 | sq_dists = rA - 2 * A.dot(B.T) + np.transpose(rB) # x^2-2*x*y+y^2 46 | my_kernel = np.exp(-gamma * np.abs(sq_dists)) 47 | elif kernel == 'laplace': # 拉普拉斯核 48 | A, B = np.array(A), np.array(B) 49 | rA, rB = np.expand_dims(A, 1), np.expand_dims(B, 0) 50 | my_kernel = np.exp(-gamma * np.sum(np.abs(rA - rB), axis=2)) 51 | else: 52 | print('kernel error!') 53 | return 54 | return my_kernel 55 | 56 | 57 | # 主成分分析 58 | def PCA(x, feature_num=None, svd=False): 59 | ''' 60 | :param x: 样本矩阵,形状为[样本个数,特征维数] 61 | :param feature_num: 保留的特征数,为None时等于特征维数 62 | :param svd: 是否使用svd进行PCA 63 | :return: 计算后得到的矩阵,形状为[样本个数,feature_num] 64 | ''' 65 | x = np.array(x, np.float) 66 | if len(x.shape) != 1 and len(x.shape) != 2: 67 | warnings.warn('数据维度不正确,不执行操作!') 68 | return None 69 | if len(x.shape) == 1: 70 | x = np.expand_dims(x, 0) 71 | if feature_num is None: 72 | feature_num = x.shape[1] 73 | x -= x.mean(0, keepdims=True) 74 | if svd: 75 | U, S, VT = np.linalg.svd(x) 76 | index_sort = np.argsort(S) # 对奇异值进行排序 77 | index = index_sort[-1:-(feature_num + 1):-1] 78 | return x.dot(VT[index]) # 乘上最大的feature_num个奇异值组成的特征向量 79 | else: 80 | eigval, eigvec = np.linalg.eig(x.transpose().dot(x) / x.shape[0]) 81 | index_sort = np.argsort(eigval) # 对特征值进行排序 82 | index = index_sort[-1:-(feature_num + 1):-1] 83 | return x.dot(eigvec[:, index]) # 乘上最大的feature_num个特征组成的特征向量 84 | 85 | 86 | # 核化主成分分析 87 | def KernelPCA(x, feature_num=None, kernel='rbf', gamma=0.1, q=0.1, degree=3): 88 | # x:样本 89 | # feature_num:保留的特征数 90 | x = np.array(x, np.float) 91 | if len(x.shape) != 1 and len(x.shape) != 2: 92 | warnings.warn('数据维度不正确,不执行操作!') 93 | return None 94 | if len(x.shape) == 1: 95 | x = np.expand_dims(x, 0) 96 | if feature_num is None: 97 | feature_num = x.shape[1] 98 | K = Kernel(x, kernel=kernel, gamma=gamma, q=q, degree=degree) 99 | one_m = np.ones([x.shape[0], x.shape[0]]) / x.shape[0] 100 | K = K - one_m.dot(K) - K.dot(one_m) + one_m.dot(K).dot(one_m) 101 | eigval, eigvec = np.linalg.eig(K) 102 | index_sort = np.argsort(eigval) # 对特征值进行排序 103 | index = index_sort[-1:-(feature_num + 1):-1] 104 | lambdas = eigval[index] 105 | alphas = eigvec[:, index] # 乘上最大的feature_num个特征组成的特征向量 106 | return K.dot(alphas / np.sqrt(lambdas)) 107 | 108 | 109 | # ELM分类器 110 | class ELMClassifier: 111 | def __init__(self, n_hidden=150, activation='leaky_relu', kernel=None, gamma=0.1, q=0.1, degree=3): 112 | self.n_hidden = n_hidden # 不使用核时,隐层节点个数 113 | self.activation = activation.lower() # 不使用核时,激活函数 114 | self.w1, self.b, self.w2 = None, None, None # 参数值 115 | self.kernel = kernel # 核的类型,[None,'linear','polynomial'('poly'),'sigmoid','rbf','laplace'] 116 | self.gamma = gamma # 多项式核、Sigmoid核、高斯核、拉普拉斯核中的参数 117 | self.q = q # 多项式核、Sigmoid核、高斯核中的参数 118 | self.degree = degree # 多项式核中的参数 119 | self.x_train = None # 训练数据 120 | 121 | def fit(self, x_train, y_train): 122 | x_train, y_train = np.array(x_train), np.array(y_train) 123 | self.x_train = x_train.copy() 124 | if self.kernel == None: 125 | self.w1 = np.random.uniform(-1, 1, (x_train.shape[1], self.n_hidden)) 126 | self.b = np.random.uniform(-1, 1, self.n_hidden) 127 | H = eval('%s(x_train.dot(self.w1) + self.b)' % self.activation) 128 | self.w2 = np.linalg.pinv(H).dot(to_categorical(y_train)) 129 | else: 130 | H = Kernel(x_train, self.x_train, kernel=self.kernel, gamma=self.gamma, q=self.q, degree=self.degree) 131 | self.w2 = np.linalg.pinv(H).dot(to_categorical(y_train)) 132 | 133 | def predict(self, x_test): 134 | x_test = np.array(x_test) 135 | if self.kernel == None: 136 | x_test = eval(' %s(x_test.dot(self.w1) + self.b).dot(self.w2)' % self.activation) 137 | else: 138 | x_test = Kernel(x_test, self.x_train, kernel=self.kernel, gamma=self.gamma, q=self.q, 139 | degree=self.degree).dot(self.w2) 140 | return np.argmax(x_test, axis=1) 141 | 142 | def score(self, x_test, y_test): 143 | x_test, y_test = np.array(x_test), np.array(y_test) 144 | y_pred = self.predict(x_test) 145 | return np.mean(np.equal(y_test, y_pred)) 146 | 147 | # 保存模型 148 | def save(self, filepath): 149 | np.savez(filepath, n_hidden=self.n_hidden, activation=self.activation, w1=self.w1, b=self.b, w2=self.w2, 150 | kernel=self.kernel, gamma=self.gamma, q=self.q, degree=self.degree, x_train=self.x_train) 151 | 152 | # 读取模型 153 | def load(self, filepath): 154 | if '.npz' not in filepath: 155 | filepath = filepath + '.npz' 156 | params = np.load(filepath) 157 | self.n_hidden = params['n_hidden'] 158 | self.activation = params['activation'] 159 | self.w1, self.b, self.w2 = params['w1'], params['b'], params['w2'] 160 | self.kernel = params['kernel'] 161 | self.gamma, self.q, self.degree = params['gamma'], params['q'], params['degree'] 162 | self.x_train = params['x_train'] 163 | 164 | 165 | # ELM回归器 166 | class ELMRegressor: 167 | def __init__(self, n_hidden=150, activation='leaky_relu', kernel=None, gamma=0.1, q=0.1, degree=3): 168 | self.n_hidden = n_hidden # 不使用核时,隐层节点个数 169 | self.activation = activation.lower() # 不使用核时,激活函数 170 | self.w1, self.b, self.w2 = None, None, None # 参数值 171 | self.kernel = kernel # 核的类型,[None,'linear','polynomial'('poly'),'sigmoid','rbf','laplace'] 172 | self.gamma = gamma # 多项式核、Sigmoid核、高斯核、拉普拉斯核中的参数 173 | self.q = q # 多项式核、Sigmoid核、高斯核中的参数 174 | self.degree = degree # 多项式核中的参数 175 | self.x_train = None # 训练数据 176 | 177 | def fit(self, x_train, y_train): 178 | x_train, y_train = np.array(x_train), np.array(y_train).reshape(-1, 1) 179 | self.x_train = x_train.copy() 180 | if self.kernel == None: 181 | self.w1 = np.random.uniform(-1, 1, (x_train.shape[1], self.n_hidden)) 182 | self.b = np.random.uniform(-1, 1, self.n_hidden) 183 | H = eval('%s(x_train.dot(self.w1) + self.b)' % self.activation) 184 | self.w2 = np.linalg.pinv(H).dot(y_train) 185 | else: 186 | H = Kernel(x_train, self.x_train, kernel=self.kernel, gamma=self.gamma, q=self.q, degree=self.degree) 187 | self.w2 = np.linalg.pinv(H).dot(y_train) 188 | 189 | def predict(self, x_test): 190 | x_test = np.array(x_test) 191 | if self.kernel == None: 192 | return eval(' %s(x_test.dot(self.w1) + self.b).dot(self.w2)' % self.activation) 193 | else: 194 | return Kernel(x_test, self.x_train, kernel=self.kernel, gamma=self.gamma, q=self.q, 195 | degree=self.degree).dot(self.w2) 196 | 197 | def score(self, x_test, y_test): 198 | x_test, y_test = np.array(x_test), np.array(y_test) 199 | y_pred = self.predict(x_test) 200 | return r2_score(y_test, y_pred) 201 | 202 | # 保存模型 203 | def save(self, filepath): 204 | np.savez(filepath, n_hidden=self.n_hidden, activation=self.activation, w1=self.w1, b=self.b, w2=self.w2, 205 | kernel=self.kernel, gamma=self.gamma, q=self.q, degree=self.degree, x_train=self.x_train) 206 | 207 | # 读取模型 208 | def load(self, filepath): 209 | if '.npz' not in filepath: 210 | filepath = filepath + '.npz' 211 | params = np.load(filepath) 212 | self.n_hidden = params['n_hidden'] 213 | self.activation = params['activation'] 214 | self.w1, self.b, self.w2 = params['w1'], params['b'], params['w2'] 215 | self.kernel = params['kernel'] 216 | self.gamma, self.q, self.degree = params['gamma'], params['q'], params['degree'] 217 | self.x_train = params['x_train'] 218 | 219 | 220 | -------------------------------------------------------------------------------- /ailearn/nn/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # -*- coding: utf-8 -*- 16 | 17 | from .activations import * 18 | from .losses import * 19 | -------------------------------------------------------------------------------- /ailearn/nn/activations.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS-IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # -*- coding: utf-8 -*- 17 | import numpy as np 18 | 19 | 20 | # relu激活函数 21 | def relu(x): 22 | x = np.array(x) 23 | return np.maximum(0, x) 24 | 25 | 26 | # tanh激活函数 27 | def tanh(x): 28 | x = np.array(x) 29 | return np.tanh(x) 30 | 31 | 32 | # sigmoid激活函数 33 | def sigmoid(x): 34 | x = np.array(x) 35 | return 1 / (1 + np.exp(-x)) 36 | 37 | 38 | # softmax激活函数 39 | def softmax(x): 40 | x = np.array(x) 41 | assert len(x.shape) == 1 or len(x.shape) == 2 42 | if len(x.shape) == 1: 43 | x = x - x.max() 44 | x = np.exp(x) 45 | return x / x.sum() 46 | else: 47 | x = x - x.max(1, keepdims=True) 48 | x = np.exp(x) 49 | return x / x.sum(1, keepdims=True) 50 | 51 | 52 | # linear激活函数 53 | def linear(x): 54 | x = np.array(x) 55 | return x 56 | 57 | 58 | # 阈值激活函数 59 | def threshold(x, threshold=0): 60 | x = np.array(x) 61 | out = np.zeros_like(x, dtype=np.float) 62 | out[x >= threshold] = 1 63 | return out 64 | 65 | 66 | # arctan激活函数 67 | def arctan(x): 68 | x = np.array(x) 69 | return np.arctan(x) 70 | 71 | 72 | # leaky relu 73 | def leaky_relu(x, alpha=0.1): 74 | x = np.array(x, dtype=np.float) 75 | x[x < 0] = (x * alpha)[x < 0] 76 | return x 77 | 78 | 79 | # prelu激活函数 80 | def prelu(x, p): 81 | x = np.array(x, dtype=np.float) 82 | x[x < 0] = (x * p)[x < 0] 83 | return x 84 | 85 | 86 | # elu激活函数 87 | def elu(x, alpha=0.1): 88 | x = np.array(x, dtype=np.float) 89 | x[x < 0] = (alpha * (np.exp(x) - 1))[x < 0] 90 | return x 91 | 92 | 93 | # softplus激活函数 94 | def softplus(x): 95 | x = np.array(x) 96 | return np.log(1 + np.exp(x)) 97 | 98 | 99 | # bent identity 100 | def bent_identity(x): 101 | x = np.array(x) 102 | return (np.sqrt(np.square(x) + 1) - 1) * 0.5 + x 103 | 104 | 105 | # Soft Exponential 106 | def soft_exponential(x, p): 107 | x = np.array(x, dtype=np.float) 108 | x[p < 0] = (-np.log(np.maximum(1 - p[p < 0] * (x[p < 0] + p[p < 0]), 1e-7)) / p[p < 0]) 109 | x[p == 0] = 0 110 | x[p > 0] = ((np.exp(p * x) - 1) / p + p)[p > 0] 111 | return x 112 | 113 | 114 | # Sinusoid 115 | def sin(x): 116 | x = np.array(x) 117 | return np.sin(x) 118 | 119 | 120 | # Sinc 121 | def sinc(x): 122 | x = np.array(x, dtype=np.float) 123 | out = np.ones_like(x, dtype=np.float) 124 | out[x != 0] = np.sin(x[x != 0]) / x[x != 0] 125 | return out 126 | 127 | 128 | # Gaussian 129 | def guassian(x): 130 | x = np.array(x) 131 | return np.exp(-np.square(x)) 132 | -------------------------------------------------------------------------------- /ailearn/nn/losses.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS-IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # -*- coding: utf-8 -*- 17 | import numpy as np 18 | from ..utils import to_categorical 19 | from .activations import softmax, sigmoid 20 | 21 | 22 | # softmax交叉熵 23 | def softmax_cross_entropy(out, label): 24 | # out:神经元的输出值 25 | # label:实际类别或one-hot编码 26 | out, label = np.array(out), np.array(label) 27 | assert len(out.shape) == 2 # 输出形状错误 28 | assert len(label.shape) == 1 or len(label.shape) == 2 # 标签形状错误 29 | if len(label.shape) == 1: # 转化为one-hot编码 30 | y = to_categorical(label, num_classes=out.shape[1]) 31 | else: 32 | if label.shape[1] == 1: 33 | y = to_categorical(label.squeeze(), num_classes=out.shape[1]) 34 | else: 35 | assert label.max() == 1 and label.sum(1).mean() == 1 # 标签one-hot编码错误 36 | y = label 37 | yhat = softmax(out) 38 | return -np.mean(y * np.log(yhat)) 39 | 40 | 41 | # 交叉熵 42 | def cross_entropy(out, label): 43 | # out:神经元的输出值 44 | # label:实际类别或one-hot编码 45 | yhat, label = np.array(out), np.array(label) 46 | assert len(out.shape) == 2 # 输出形状错误 47 | assert len(label.shape) == 1 or len(label.shape) == 2 # 标签形状错误 48 | if len(label.shape) == 1: # 转化为one-hot编码 49 | y = to_categorical(label, num_classes=out.shape[1]) 50 | else: 51 | if label.shape[1] == 1: 52 | y = to_categorical(label.squeeze(), num_classes=out.shape[1]) 53 | else: 54 | assert label.max() == 1 and label.sum(1).mean() == 1 # 标签one-hot编码错误 55 | y = label 56 | return -np.mean(y * np.log(yhat)) 57 | 58 | 59 | # 二分类 60 | def sigmoid_binary_cross_entropy(out, label): 61 | # out:神经元的输出值 62 | # label:实际类别或one-hot编码 63 | out, y = np.array(out), np.array(label) 64 | assert len(out.shape) == 2 and out.shape[1] == 1 # 输出形状错误 65 | assert len(y.shape) == 1 # 标签形状错误 66 | yhat = sigmoid(out) 67 | return -np.mean(y * np.log(yhat) + (1 - y) * np.log(1 - yhat)) 68 | 69 | 70 | # 二分类 71 | def binary_cross_entropy(out, label): 72 | # out:神经元的输出值 73 | # label:实际类别或one-hot编码 74 | yhat, y = np.array(out), np.array(label) 75 | assert len(yhat.shape) == 2 and out.shape[1] == 1 # 输出形状错误 76 | assert len(y.shape) == 1 # 标签形状错误 77 | return -np.mean(y * np.log(yhat) + (1 - y) * np.log(1 - yhat)) 78 | 79 | 80 | # 最小二乘损失 81 | def square_loss(prediction, y): 82 | # prediction:预测值 83 | # y:实际值 84 | prediction, y = np.array(prediction), np.array(y) 85 | assert (len(prediction.shape) == 2 and prediction.shape[1] == 1) or len(prediction.shape) == 1 # 输出形状错误 86 | assert len(y.shape) == 1 or (len(y.shape) == 2 and y.shape[1] == 1) # 真实值形状错误 87 | return np.sum(np.sum(np.square(prediction.reshape(-1, 1) - y.reshape(-1, 1)), 1)) 88 | 89 | 90 | # 均方误差 91 | def mse(prediction, y): 92 | # prediction:预测值 93 | # y:实际值 94 | prediction, y = np.array(prediction), np.array(y) 95 | assert (len(prediction.shape) == 2 and prediction.shape[1] == 1) or len(prediction.shape) == 1 # 输出形状错误 96 | assert len(y.shape) == 1 or (len(y.shape) == 2 and y.shape[1] == 1) # 真实值形状错误 97 | return np.mean(np.sum(np.square(prediction.reshape(-1, 1) - y.reshape(-1, 1)), 1)) 98 | -------------------------------------------------------------------------------- /ailearn/text.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # -*- coding: utf-8 -*- 16 | from keras.preprocessing.text import Tokenizer 17 | from keras.preprocessing.sequence import pad_sequences 18 | 19 | 20 | # 本文列表转化成index列表 21 | def texts_to_sequences(texts, num_words=None, filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n', lower=True, split=' ', 22 | char_level=False, oov_token=None, document_count=0, is_padding=True, maxlen=None, dtype='int32', 23 | padding='pre', truncating='pre', value=0): 24 | ''' 25 | :param texts: 待转换的文本 26 | :param num_words: None或整数,处理的最大单词数量。若被设置为整数,则分词器将被限制为待处理数据集中最常见的num_words个单词 27 | :param filters: 需要滤除的字符的列表或连接形成的字符串 28 | :param lower: 布尔值,是否将序列设为小写形式 29 | :param split: 字符串,单词的分隔符,如空格 30 | :param char_level: 如果为 True, 每个字符将被视为一个标记 31 | :param oov_token: out-of-vocabulary,如果给定一个string作为这个oov token的话,就将这个string也加到word_index,也就是从word到index 的映射中,用来代替那些字典上没有的字。 32 | :param document_count: 整数。分词器被训练的文档(文本或者序列)数量。仅在调用fit_on_texts或fit_on_sequences之后设置。 33 | :param is_padding: 布尔值,是否对序列进行填充 34 | :param maxlen: 序列的最大长度 35 | :param dtype: numpy数组的数据类型 36 | :param padding: 'pre'或'post',在前面或后面填充 37 | :param truncating: 'pre'或'post',在起始或结尾截断 38 | :param value: 填充值 39 | :return: 转化后的index序列,index到word的字典,word到index的字典 40 | ''' 41 | tokenizer = Tokenizer(num_words=num_words, filters=filters, lower=lower, split=split, char_level=char_level, 42 | oov_token=oov_token, document_count=document_count) 43 | tokenizer.fit_on_texts(texts) 44 | sequences = tokenizer.texts_to_sequences(texts) 45 | index2word = tokenizer.index_word 46 | word2index = tokenizer.word_index 47 | if is_padding: 48 | sequences = pad_sequences(sequences, maxlen=maxlen, dtype=dtype, padding=padding, truncating=truncating, 49 | value=value) 50 | return sequences, index2word, word2index 51 | -------------------------------------------------------------------------------- /ailearn/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # -*- coding: utf-8 -*- 16 | 17 | from .distances import * 18 | from .metrics import * 19 | from .utilities import * -------------------------------------------------------------------------------- /ailearn/utils/distances.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # -*- coding: utf-8 -*- 16 | import numpy as np 17 | import warnings 18 | 19 | 20 | # 闵可夫斯基距离 21 | def minkowski_distance(a, b, p): 22 | ''' 23 | :param a: 向量1 24 | :param b: 向量2 25 | :param p: 参数(阶数) 26 | :return: 闵可夫斯基距离 27 | ''' 28 | a = np.array(a).squeeze() 29 | b = np.array(b).squeeze() 30 | if len(a.shape) != 1 or len(b.shape) != 1: 31 | warnings.warn('数据维度不为1,不执行操作!') 32 | return None 33 | return np.power(np.sum(np.power(np.abs(a - b), p)), 1 / p) 34 | 35 | 36 | # l1范数 37 | def l1_distance(a, b): 38 | ''' 39 | :param a: 向量1 40 | :param b: 向量2 41 | :return: l1范数 42 | ''' 43 | return minkowski_distance(a, b, 1) 44 | 45 | 46 | # 曼哈顿距离 47 | def manhattan_distance(a, b): 48 | ''' 49 | :param a: 向量1 50 | :param b: 向量2 51 | :return: 曼哈顿距离 52 | ''' 53 | return minkowski_distance(a, b, 1) 54 | 55 | 56 | # l2范数 57 | def l2_distance(a, b): 58 | ''' 59 | :param a: 向量1 60 | :param b: 向量2 61 | :return: l2范数 62 | ''' 63 | return minkowski_distance(a, b, 2) 64 | 65 | 66 | # 欧拉距离 67 | def euclidean_distance(a, b): 68 | ''' 69 | :param a: 向量1 70 | :param b: 向量2 71 | :return: 欧拉距离 72 | ''' 73 | return minkowski_distance(a, b, 2) 74 | 75 | 76 | # 切比雪夫距离 77 | def chebyshev_distance(a, b): 78 | ''' 79 | :param a: 向量1 80 | :param b: 向量2 81 | :return: 切比雪夫距离 82 | ''' 83 | a = np.array(a).squeeze() 84 | b = np.array(b).squeeze() 85 | if len(a.shape) != 1 or len(b.shape) != 1: 86 | warnings.warn('数据维度不为1,不执行操作!') 87 | return None 88 | return np.max(np.abs(a - b)) 89 | 90 | 91 | # 夹角余弦 92 | def cosine(a, b): 93 | ''' 94 | :param a: 向量1 95 | :param b: 向量2 96 | :return: 夹角余弦 97 | ''' 98 | a = np.array(a).squeeze() 99 | b = np.array(b).squeeze() 100 | if len(a.shape) != 1 or len(b.shape) != 1: 101 | warnings.warn('数据维度不为1,不执行操作!') 102 | return None 103 | return a.dot(b) / (np.linalg.norm(a, 2) * np.linalg.norm(b, 2)) 104 | 105 | 106 | # 汉明距离 107 | def hamming_distance(a, b): 108 | ''' 109 | :param a: 向量1 110 | :param b: 向量2 111 | :return: 汉明距离 112 | ''' 113 | a = np.array(a, np.str).squeeze() 114 | b = np.array(b, np.str).squeeze() 115 | if len(a.shape) != 1 or len(b.shape) != 1: 116 | warnings.warn('数据维度不为1,不执行操作!') 117 | return None 118 | return np.sum(a != b) 119 | 120 | 121 | # 杰拉德相似系数 122 | def jaccard_similarity_coefficient(a, b): 123 | ''' 124 | :param a: 向量1 125 | :param b: 向量2 126 | :return: 杰拉德相似系数 127 | ''' 128 | a = set(a) 129 | b = set(b) 130 | return len(a.intersection(b)) / len(a.union(b)) 131 | 132 | 133 | # 杰拉德距离 134 | def jaccard_distance(a, b): 135 | ''' 136 | :param a: 向量1 137 | :param b: 向量2 138 | :return: 杰拉德距离 139 | ''' 140 | return 1 - jaccard_similarity_coefficient(a, b) 141 | 142 | 143 | # 相关系数 144 | def correlation_coefficient(a, b): 145 | ''' 146 | :param a: 向量1 147 | :param b: 向量2 148 | :return: 相关系数 149 | ''' 150 | a = np.array(a).squeeze() 151 | b = np.array(b).squeeze() 152 | if len(a.shape) != 1 or len(b.shape) != 1: 153 | warnings.warn('数据维度不为1,不执行操作!') 154 | return None 155 | return ((a - a.mean()) * (b - b.mean())).mean() / (a.std() * b.std()) 156 | 157 | 158 | # 相关距离 159 | def correlation_distance(a, b): 160 | ''' 161 | :param a: 向量1 162 | :param b: 向量2 163 | :return: 相关距离 164 | ''' 165 | return 1 - correlation_coefficient(a, b) 166 | 167 | 168 | # 马氏距离 169 | def mahalanobis_distance(x): 170 | ''' 171 | :param x: 样本矩阵,形状:[样本个数,特征维数] 172 | :return: 不同样本间的马氏距离 173 | ''' 174 | x = np.array(x) 175 | if len(x.shape) != 2: 176 | warnings.warn('数据维度不为2,不执行操作!') 177 | return None 178 | m, n = x.shape 179 | S_inv = np.linalg.pinv(np.cov(x)) 180 | D = np.zeros([m, m]) 181 | for i in range(m): 182 | for j in range(i + 1, m): 183 | D[i, j] = np.sqrt((x[i] - x[j]).reshape(1, n).dot(S_inv).dot((x[i] - x[j]).reshape(n, 1))) 184 | D[j, i] = D[i, j] 185 | return D 186 | 187 | 188 | # 计算不同样本之间的距离,输入:[m,n],输出[m,m],其中dist[i,j]为x[i]到x[j]的距离 189 | def distance_matrix(x): 190 | ''' 191 | :param x: 样本矩阵,形状:[样本个数,特征维数] 192 | :return: 不同样本之间的距离 193 | ''' 194 | m = x.shape[0] 195 | distance = np.zeros([m, m]) 196 | for i in range(m): 197 | for j in range(m - 1, i, -1): 198 | distance[i][j] = np.linalg.norm(x[i] - x[j], 2) 199 | distance[j][i] = distance[i][j] 200 | return distance 201 | -------------------------------------------------------------------------------- /ailearn/utils/metrics.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # -*- coding: utf-8 -*- 16 | import numpy as np 17 | from sklearn.model_selection import StratifiedKFold, train_test_split 18 | import warnings 19 | from sklearn.metrics import precision_recall_fscore_support, confusion_matrix 20 | 21 | 22 | # 准确率 23 | def accuracy_score(prediction, label): 24 | ''' 25 | :param prediction: 预测类别或one-hot编码 26 | :param label: 实际类别或one-hot编码 27 | :return: 准确率 28 | ''' 29 | prediction, label = np.array(prediction), np.array(label) 30 | assert len(prediction.shape) == 1 or len(prediction.shape) == 2 # 输出值形状错误 31 | assert len(label.shape) == 1 or len(label.shape) == 2 # 真实值形状错误 32 | if len(prediction.shape) == 2: 33 | if prediction.shape[1] == 1: 34 | prediction = prediction.squeeze() 35 | else: 36 | prediction = np.argmax(prediction, 1) 37 | if len(label.shape) == 2: 38 | if label.shape[1] == 1: 39 | label = label.squeeze() 40 | else: 41 | label = np.argmax(label, 1) 42 | return np.mean(np.equal(prediction, label)) 43 | 44 | 45 | # 均方误差 46 | def MSE(prediction, y, sample_importance=None): 47 | ''' 48 | :param prediction: 预测值 49 | :param y: 真实值 50 | :param sample_importance: 样本重要性权重 51 | :return: 均方误差 52 | ''' 53 | prediction, y = np.array(prediction).squeeze(), np.array(y).squeeze() 54 | assert len(prediction.shape) == 1 and len(y.shape) == 1 # 预测值或真实值形状错误 55 | if sample_importance is None: 56 | return np.mean(np.square(prediction - y)) 57 | else: 58 | sample_importance = np.array(sample_importance) 59 | assert sample_importance.shape[0] == prediction.shape[0] # 重要性权值形状错误 60 | return np.mean(sample_importance * np.square(prediction - y)) 61 | 62 | 63 | # 均方根误差 64 | def RMSE(prediction, y, sample_importance=None): 65 | ''' 66 | :param prediction: 预测值 67 | :param y: 真实值 68 | :param sample_importance: 样本重要性权重 69 | :return: 均方根误差 70 | ''' 71 | prediction, y = np.array(prediction).squeeze(), np.array(y).squeeze() 72 | assert len(prediction.shape) == 1 and len(y.shape) == 1 # 预测值或真实值形状错误 73 | if sample_importance is None: 74 | return np.sqrt(np.mean(np.square(prediction - y))) 75 | else: 76 | sample_importance = np.array(sample_importance) 77 | assert sample_importance.shape[0] == prediction.shape[0] # 重要性权值形状错误 78 | return np.sqrt(np.mean(sample_importance * np.square(prediction - y))) 79 | 80 | 81 | # 平均绝对误差 82 | def MAE(prediction, y, sample_importance=None): 83 | ''' 84 | :param prediction: 预测值 85 | :param y: 真实值 86 | :param sample_importance: 样本重要性权重 87 | :return: 88 | ''' 89 | prediction, y = np.array(prediction).squeeze(), np.array(y).squeeze() 90 | assert len(prediction.shape) == 1 and len(y.shape) == 1 # 预测值或真实值形状错误 91 | if sample_importance is None: 92 | return np.mean(np.abs(prediction - y)) 93 | else: 94 | sample_importance = np.array(sample_importance) 95 | assert sample_importance.shape[0] == prediction.shape[0] # 重要性权值形状错误 96 | return np.mean(sample_importance * np.abs(prediction - y)) 97 | 98 | 99 | # 误差平方和 100 | def SSE(prediction, y, sample_importance=None): 101 | ''' 102 | :param prediction: 预测值 103 | :param y: 真实值 104 | :param sample_importance: 样本重要性权重 105 | :return: 106 | ''' 107 | prediction, y = np.array(prediction).squeeze(), np.array(y).squeeze() 108 | assert len(prediction.shape) == 1 and len(y.shape) == 1 # 预测值或真实值形状错误 109 | if sample_importance is None: 110 | return np.sum(np.square(prediction - y)) 111 | else: 112 | sample_importance = np.array(sample_importance) 113 | assert sample_importance.shape[0] == prediction.shape[0] # 重要性权值形状错误 114 | return np.sum(sample_importance * np.square(prediction - y)) 115 | 116 | 117 | # 总平方和 118 | def SST(y, sample_importance=None): 119 | ''' 120 | :param y: 真实值 121 | :param sample_importance: 样本重要性权重 122 | :return: 总平方和 123 | ''' 124 | y = np.array(y) 125 | assert len(y.shape) == 1 # 真实值形状错误 126 | if sample_importance is None: 127 | return np.sum(np.square(y - np.mean(y))) 128 | else: 129 | sample_importance = np.array(sample_importance) 130 | assert sample_importance.shape[0] == y.shape[0] # 重要性权值形状错误 131 | return np.sum(sample_importance * np.square(y - np.mean(y))) 132 | 133 | 134 | # 回归平方和 135 | def SSR(prediction, y, sample_importance=None): 136 | ''' 137 | :param prediction: 预测值 138 | :param y: 真实值 139 | :param sample_importance: 样本重要性权重 140 | :return: 回归平方和 141 | ''' 142 | prediction, y = np.array(prediction).squeeze(), np.array(y).squeeze() 143 | assert len(prediction.shape) == 1 and len(y.shape) == 1 # 预测值或真实值形状错误 144 | if sample_importance is None: 145 | return np.sum(np.square(prediction - np.mean(y))) # Total sum of squares 146 | else: 147 | sample_importance = np.array(sample_importance) 148 | assert sample_importance.shape[0] == prediction.shape[0] # 重要性权值形状错误 149 | return np.sum(sample_importance * np.square(prediction - np.mean(y))) 150 | 151 | 152 | # 确定系数 153 | def R_square(prediction, y, sample_importance=None): 154 | ''' 155 | :param prediction: 预测值 156 | :param y: 真实值 157 | :param sample_importance: 样本重要性权重 158 | :return: 确定系数 159 | ''' 160 | return 1 - SSE(prediction, y, sample_importance) / SST(y, sample_importance) 161 | 162 | 163 | # 皮尔森相关系数 164 | def PC(prediction, y): 165 | ''' 166 | :param prediction: 预测值 167 | :param y: 真实值 168 | :return: 169 | ''' 170 | prediction, y = np.array(prediction).squeeze(), np.array(y).squeeze() 171 | assert len(prediction.shape) == 1 and len(y.shape) == 1 # 预测值或真实值形状错误 172 | n = y.shape[0] 173 | return (n * np.sum(prediction * y) - np.sum(prediction) * np.sum(y)) / ( 174 | np.sqrt(n * np.sum(prediction ** 2) - np.sum(prediction) ** 2) * np.sqrt( 175 | n * np.sum(y ** 2) - np.sum(y) ** 2)) 176 | 177 | 178 | # K折交叉验证 179 | def cross_val_score(estimator, x, y, k=10, verbose=True, random_state=None, **kwargs): 180 | ''' 181 | :param estimator: 待评价的模型 182 | :param x: 样本数据 183 | :param y: 样本标签 184 | :param k: K折交叉验证中的K值 185 | :param verbose: 是否显示验证过程 186 | :param random_state: 数据集分割的随机数种子 187 | :param kwargs: estimator.fit()的参数 188 | :return: k次验证的准确率一维数组 189 | ''' 190 | x, y = np.array(x), np.array(y) 191 | if random_state is None: 192 | folder = StratifiedKFold(k, True) 193 | else: 194 | folder = StratifiedKFold(k, True, random_state) 195 | scores = [] 196 | for i, (train_index, test_index) in enumerate(folder.split(x, y)): 197 | estimator.fit(x[train_index], y[train_index], **kwargs) 198 | score = estimator.score(x[test_index], y[test_index]) 199 | scores.append(score) 200 | if verbose: 201 | print('第%d次交叉验证完成,得分为%.4f' % (i + 1, score)) 202 | scores = np.array(scores) 203 | return scores 204 | 205 | 206 | # 留p法交叉验证 207 | def leave_p_score(estimator, x, y, p=1, verbose=True, **kwargs): 208 | ''' 209 | :param estimator: 待评价的模型 210 | :param x: 样本数据 211 | :param y: 样本标签 212 | :param p: 留p法交叉验证中的p值 213 | :param verbose: 是否显示验证过程 214 | :param kwargs: estimator.fit()的参数 215 | :return: len(x)//p次验证的准确率一维数组 216 | ''' 217 | x, y = np.array(x), np.array(y) 218 | if x.shape[0] < p: 219 | warnings.warn('交叉验证参数错误,不执行操作!') 220 | return None 221 | epoch = x.shape[0] // p 222 | index = np.arange(x.shape[0]) 223 | np.random.shuffle(index) 224 | scores = [] 225 | for i in range(epoch): 226 | test_index = slice(i * p, (i + 1) * p) 227 | train_index = np.delete(index, test_index) 228 | estimator.fit(x[train_index], y[train_index], **kwargs) 229 | score = estimator.score(x[test_index], y[test_index]) 230 | scores.append(score) 231 | if verbose: 232 | print('第%d次交叉验证完成,得分为%.4f' % (i + 1, score)) 233 | scores = np.array(scores) 234 | return scores 235 | 236 | 237 | # Hold-Out检验 238 | def hold_out_score(estimator, x, y, test_size=0.25, shuffle=True, stratify=None, random_state=0, **kwargs): 239 | ''' 240 | :param estimator: 待评价的模型 241 | :param x: 样本数据 242 | :param y: 样本标签 243 | :param test_size: 测试数据比率 244 | :param shuffle: 是否随机化 245 | :param stratify: 是否分层采样(一般输入y) 246 | :param random_state: 数据集分割的随机数种子 247 | :param kwargs: estimator.fit()的参数 248 | :return: 准确率 249 | ''' 250 | x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=test_size, shuffle=shuffle, stratify=stratify, 251 | random_state=random_state) 252 | estimator.fit(x_train, y_train, **kwargs) 253 | return estimator.score(x_test, y_test) 254 | 255 | 256 | # 分类报告 257 | def class_report(y_true, y_pred, method='weighted'): 258 | ''' 259 | :param y_true: 真实标签 260 | :param y_pred: 预测标签 261 | :param method: 报告方式,包括'weighted'、'micro'、'macro' 262 | :return: 分类报告字典 263 | ''' 264 | y_true, y_pred = np.array(y_true), np.array(y_pred) 265 | accuracy = np.mean(y_true == y_pred) 266 | cm = confusion_matrix(y_true, y_pred) # 混淆矩阵 267 | _, _, _, num = precision_recall_fscore_support(y_true, y_pred) # num为每一类的样本数目列表 268 | n_classes = len(num) 269 | n_samples = len(y_true) 270 | TP, FN, FP, TN = np.zeros([n_classes], int), np.zeros([n_classes], int), np.zeros([n_classes], int), np.zeros( 271 | [n_classes], int) # 每一类的TP、FN、FP、TN 272 | precision, recall, recall_, F1_score, G_mean = np.zeros([n_classes]), np.zeros([n_classes]), np.zeros( 273 | [n_classes]), np.zeros([n_classes]), np.zeros([n_classes]) # 每一类的精确度、召回率、负类召回率、F1值、G-mean值 274 | # 微平均 275 | if method == 'micro': 276 | for i in range(n_classes): 277 | TP[i] = cm[i][i] 278 | FP[i] = np.sum(cm[:, i]) - TP[i] 279 | FN[i] = np.sum(cm[i]) - TP[i] 280 | TN[i] = n_samples - TP[i] - FP[i] - FN[i] 281 | TP, FP, FN, TN = np.mean(TP), np.mean(FP), np.mean(FN), np.mean(TN) 282 | if TP == 0 and FP == 0: 283 | precision = 0 284 | else: 285 | precision = TP / (TP + FP) 286 | if TP == 0 and FN == 0: 287 | recall = 0 288 | else: 289 | recall = TP / (TP + FN) 290 | if TN == 0 and FP == 0: 291 | recall_ = 0 292 | else: 293 | recall_ = TN / (TN + FP) 294 | if recall == 0 and precision == 0: 295 | F1_score = 0 296 | else: 297 | F1_score = 2 * precision * recall / (precision + recall) 298 | if recall == 0 and recall_ == 0: 299 | G_mean = 0 300 | else: 301 | G_mean = np.sqrt(recall * recall_) 302 | return {'accuracy': accuracy, 'precision': precision, 'recall': recall, 'F1_score': F1_score, 'G_mean': G_mean} 303 | 304 | # 宏平均或加权平均 305 | elif method == 'macro' or method == 'weighted': 306 | for i in range(n_classes): 307 | TP[i] = cm[i][i] 308 | FP[i] = np.sum(cm[:, i]) - TP[i] 309 | FN[i] = np.sum(cm[i]) - TP[i] 310 | TN[i] = n_samples - TP[i] - FP[i] - FN[i] 311 | if TP[i] == 0 and FP[i] == 0: 312 | precision[i] = 0 313 | else: 314 | precision[i] = TP[i] / (TP[i] + FP[i]) 315 | if TP[i] == 0 and FN[i] == 0: 316 | recall[i] = 0 317 | else: 318 | recall[i] = TP[i] / (TP[i] + FN[i]) 319 | if TN[i] == 0 and FP[i] == 0: 320 | recall_[i] = 0 321 | else: 322 | recall_[i] = TN[i] / (TN[i] + FP[i]) 323 | if recall[i] == 0 and precision[i] == 0: 324 | F1_score[i] = 0 325 | else: 326 | F1_score[i] = 2 * precision[i] * recall[i] / (precision[i] + recall[i]) 327 | if recall[i] == 0 and recall_[i] == 0: 328 | G_mean[i] = 0 329 | else: 330 | G_mean[i] = np.sqrt(recall[i] * recall_[i]) 331 | if method == 'macro': 332 | precision = np.mean(precision) 333 | recall = np.mean(recall) 334 | F1_score = np.mean(F1_score) 335 | G_mean = np.mean(G_mean) 336 | return {'accuracy': accuracy, 'precision': precision, 'recall': recall, 'F1_score': F1_score, 337 | 'G_mean': G_mean} 338 | else: 339 | precision = np.sum(precision * num) / n_samples 340 | recall = np.sum(recall * num) / n_samples 341 | F1_score = np.sum(F1_score * num) / n_samples 342 | G_mean = np.sum(G_mean * num) / n_samples 343 | return {'accuracy': accuracy, 'precision': precision, 'recall': recall, 'F1_score': F1_score, 344 | 'G_mean': G_mean} 345 | 346 | else: 347 | print('Classification report type error!') 348 | -------------------------------------------------------------------------------- /ailearn/utils/utilities.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # -*- coding: utf-8 -*- 16 | import numpy as np 17 | import warnings 18 | import random 19 | from sklearn.manifold import TSNE, MDS 20 | from sklearn.decomposition import PCA 21 | import matplotlib.pyplot as plt 22 | from scipy import stats 23 | import pandas as pd 24 | from sklearn.preprocessing import minmax_scale 25 | import keras 26 | 27 | 28 | # 生成样本 29 | def generate_sample(x, y, N_classes=None, sample_num=None, have_correct=True): 30 | ''' 31 | :param x: 特征 32 | :param y: 标签 33 | :param N_classes: 类别个数 34 | :param sample_num: 样本个数 35 | :param have_correct: 是否必包括正确标记 36 | :return: 生成的特征,生成的标签,是否为该类 37 | ''' 38 | x, y = np.array(x), np.array(y).astype(np.int32) 39 | if N_classes is None: 40 | N_classes = y.max() + 1 41 | if len(y.shape) == 1: 42 | y = keras.utils.to_categorical(y, N_classes) 43 | if sample_num is None: 44 | sample_num = N_classes 45 | assert sample_num > 0 # 生成的样本必须大于0个 46 | assert len(y.shape) == 2 # 转化后的标记的维度必须为2 47 | data, label, is_True = [], [], [] 48 | for i in range(x.shape[0]): 49 | samples = list(np.random.choice(N_classes, size=sample_num, replace=True)) 50 | if have_correct: 51 | samples.append(np.argmax(y[i])) 52 | samples = samples[1:] 53 | for j in range(sample_num): 54 | data.append(x[i]) 55 | label.append(samples[j]) 56 | if y[i, samples[j]] == 1: 57 | is_True.append(True) 58 | else: 59 | is_True.append(False) 60 | return np.array(data), np.array(label), np.array(is_True) 61 | 62 | 63 | # 生成迭代器 64 | def data_iter(data, label, batch_size, is_True=None): 65 | ''' 66 | :param data: 样本数据 67 | :param label: 样本标签 68 | :param batch_size: 批大小 69 | :param is_True: 标记是否为真 70 | :return: 迭代器 71 | ''' 72 | data, label = np.array(data), np.array(label) 73 | n_samples = data.shape[0] 74 | idx = list(range(n_samples)) 75 | random.shuffle(idx) 76 | if is_True is not None: 77 | is_True = np.array(is_True) 78 | for i in range(0, n_samples, batch_size): 79 | j = np.array(idx[i:min(i + batch_size, n_samples)]) 80 | yield np.take(data, j, 0), np.take(label, j, 0), np.take(is_True, j, 0) 81 | else: 82 | for i in range(0, n_samples, batch_size): 83 | j = np.array(idx[i:min(i + batch_size, n_samples)]) 84 | yield np.take(data, j, 0), np.take(label, j, 0) 85 | 86 | 87 | # label转one-hot 88 | def to_categorical(label, num_classes=None): 89 | ''' 90 | :param label: 样本标签 91 | :param num_classes: 总共的类别数 92 | :return: 标签的one-hot编码 93 | ''' 94 | label = np.array(label, dtype='int') 95 | if num_classes is not None: 96 | assert num_classes > label.max() # 类别数量错误 97 | else: 98 | num_classes = label.max() + 1 99 | if len(label.shape) == 1: 100 | y = np.eye(num_classes, dtype='int64')[label] 101 | return y 102 | elif len(label.shape) == 2 and label.shape[1] == 1: 103 | y = np.eye(num_classes, dtype='int64')[label.squeeze()] 104 | return y 105 | else: 106 | warnings.warn('Warning: one_hot_to_label do not work') 107 | return label 108 | 109 | 110 | # one-hot转label 111 | def one_hot_to_label(y): 112 | ''' 113 | :param y: one-hot编码 114 | :return: 标签 115 | ''' 116 | y = np.array(y) 117 | if len(y.shape) == 2 and y.max() == 1 and y.sum(1).mean() == 1: 118 | return y.argmax(1) 119 | else: 120 | warnings.warn('Warning: one_hot_to_label do not work') 121 | return y 122 | 123 | 124 | # 画出样本数据示意图 125 | def plot(x, y, method='t-SNE'): 126 | ''' 127 | :param x: 数据 128 | :param y: 标签 129 | :param method: 可视化方法,包括['t-SNE','PCA','MDS'] 130 | :return: None 131 | ''' 132 | x, y = check_data_target(x, y) 133 | if method == 't-SNE': 134 | x = TSNE(n_components=2).fit_transform(x) 135 | elif method == 'PCA' or method == 'pca': 136 | x = PCA(n_components=2).fit_transform(x) 137 | elif method == 'MDS' or method == 'mds': 138 | x = MDS(n_components=2).fit_transform(x) 139 | else: 140 | warnings.warn('Wrong method!') 141 | return 142 | for i in range(y.max() + 1): 143 | plt.scatter(x[y == i][:, 0], x[y == i][:, 1], label='class %d' % i) 144 | plt.legend() 145 | plt.show() 146 | 147 | 148 | # 画出分类边界图 149 | def plot_classifier(classifier, x, y): 150 | ''' 151 | :param classifier: 分类器 152 | :param x: 数据 153 | :param y: 标签 154 | :return: None 155 | ''' 156 | x, y = check_data_target(x, y) 157 | x1_min, x1_max = np.min(x[:, 0]) - 1, np.max(x[:, 0]) + 1 158 | x2_min, x2_max = np.min(x[:, 1]) - 1, np.max(x[:, 1]) + 1 159 | step_size = 0.01 160 | x1_values, x2_values = np.meshgrid(np.arange(x1_min, x1_max, step_size), np.arange(x2_min, x2_max, step_size)) 161 | mesh_output = classifier.predict(np.c_[x1_values.ravel(), x2_values.ravel()]) 162 | mesh_output = mesh_output.reshape(x1_values.shape) 163 | plt.figure() 164 | plt.pcolormesh(x1_values, x2_values, mesh_output, cmap=plt.cm.gray) 165 | plt.scatter(x[:, 0], x[:, 1], c=y, s=80, linewidths=1, cmap=plt.cm.Paired) 166 | plt.xlim(x1_values.min(), x1_values.max()) 167 | plt.ylim(x2_values.min(), x2_values.max()) 168 | plt.xticks((np.arange(int(np.min(x[:, 0]) - 1), int(np.max(x[:, 0]) + 1), 1))) 169 | plt.yticks((np.arange(int(np.min(x[:, 1]) - 1), int(np.max(x[:, 1]) + 1), 1))) 170 | plt.show() 171 | 172 | 173 | # 检查数据和标签的形状 174 | def check_data_target(x, y): 175 | ''' 176 | :param x: 数据 177 | :param y: 标签 178 | :return: 数据、标签 179 | ''' 180 | x, y = np.array(x), np.array(y) 181 | assert x.ndim == 2 # 数据形状错误 182 | assert y.ndim == 1 or (y.ndim == 2 and y.shape[1] == 1) # 标签形状错误 183 | if y.ndim == 2 and y.shape[1] == 1: 184 | y = y.squeeze() 185 | return x, y 186 | 187 | 188 | # Friedman检验 189 | def Friedman_test(x, alpha=0.05, ranked=False, use_f_distribution=False, verbose=False): 190 | ''' 191 | :param x: 各个算法在不同数据集上的得分或排序,形状为[数据集个数,算法个数] 192 | :param alpha: 显著性水平 193 | :param ranked: 输入的数据是否为排序 194 | :param use_f_distribution: 是否使用改进的Friedman检测 195 | :param verbose: 当输入数据为得分时,是否打印排序结果 196 | :return: 各算法的排序 197 | ''' 198 | x = np.array(x) + 0. 199 | n_datasets, n_algorithms = x.shape[0], x.shape[1] 200 | if not ranked: # 输入为得分 201 | for i in range(n_datasets): # 对于第i个数据集 202 | rank_list = np.zeros([n_algorithms]) # 不同算法的排名 203 | score = x[i].copy() 204 | chuli = 0 205 | while chuli != n_algorithms: 206 | M = np.max(score) 207 | score_equal = [] 208 | for j in range(n_algorithms): 209 | if score[j] == M: 210 | score_equal.append(j) 211 | rank_list[score_equal] = np.sum(np.arange(chuli + 1, chuli + 1 + len(score_equal))) / len(score_equal) 212 | score[score_equal] = -np.inf 213 | x[i] = rank_list.copy() 214 | chuli += len(score_equal) 215 | if verbose: 216 | print('输入得分排名为:') 217 | print(x) 218 | R = np.mean(x, axis=0) 219 | Tao = 12 * n_datasets / n_algorithms / (n_algorithms + 1) * np.sum(np.square(R - (n_algorithms + 1) / 2)) 220 | if use_f_distribution: # 使用改进的Friedman检测 221 | F = stats.f.isf(q=alpha, dfn=(n_algorithms - 1), dfd=int(n_algorithms - 1) * (n_datasets - 1)) 222 | Tao = (n_datasets - 1) * Tao / (n_datasets * (n_algorithms - 1) - Tao) 223 | if Tao > F: 224 | print('Tao值为%.4f,显著性水平为%.4f的F分布值为%.4f,有显著区别' % (Tao, alpha, F)) 225 | else: 226 | print('Tao值为%.4f,显著性水平为%.4f的F分布值为%.4f,无显著区别' % (Tao, alpha, F)) 227 | else: # 使用传统的Friedman检测 228 | Chi2 = stats.chi2.isf(q=alpha, df=n_algorithms - 1) 229 | if Tao > Chi2: 230 | print('Tao值为%.4f,显著性水平为%.4f的卡方分布值为%.4f,有显著区别' % (Tao, alpha, Chi2)) 231 | else: 232 | print('Tao值为%.4f,显著性水平为%.4f的卡方分布值为%.4f,无显著区别' % (Tao, alpha, Chi2)) 233 | return x 234 | 235 | 236 | # t检验 237 | def t_test(x1=None, x2=None, alpha=0.05, from_stats=False, mean1=None, std1=None, nobs1=None, mean2=None, std2=None, 238 | nobs2=None): 239 | ''' 240 | :param x1: 第一组准确率,list或numpy.array 241 | :param x2: 第二组准确率,list或numpy.array 242 | :param alpha:显著性水平 243 | :param from_stats: 输入的参数是否为统计学参数 244 | :param mean1: 第一组样本的均值 245 | :param std1: 第一组样本的方差 246 | :param nobs1: 第一组样本个数 247 | :param mean2: 第二组样本的均值 248 | :param std2: 第二组样本的方差 249 | :param nobs2: 第二组样本个数 250 | :return: 显著程度 251 | ''' 252 | if from_stats: 253 | std1, std2 = np.sqrt(nobs1 / (nobs1 - 1)) * std1, np.sqrt(nobs2 / (nobs2 - 1)) * std2 254 | statistic, pvalue = stats.ttest_ind_from_stats(mean1=mean1, std1=std1, nobs1=nobs1, mean2=mean2, std2=std2, 255 | nobs2=nobs2) 256 | else: 257 | x1, x2 = np.array(x1), np.array(x2) 258 | statistic, pvalue = stats.levene(x1, x2) 259 | print(pvalue) 260 | if pvalue > 0.05: 261 | equal_val = True 262 | else: 263 | equal_val = False 264 | statistic, pvalue = stats.ttest_ind(x1, x2, equal_var=equal_val) 265 | if pvalue > alpha: 266 | print('pvalue值为%.4f,显著性水平为%.4f,无显著区别' % (pvalue, alpha)) 267 | else: 268 | print('pvalue值为%.4f,显著性水平为%.4f,有显著区别' % (pvalue, alpha)) 269 | return pvalue 270 | 271 | 272 | # 计算Gram矩阵 273 | def Gram(x): 274 | ''' 275 | :param x: 输入向量 276 | :return: 输入向量对应的Gram矩阵 277 | ''' 278 | x = np.array(x).squeeze() 279 | assert len(x.shape) == 1 # 样本维度不符 280 | return x.reshape(-1, 1) * x 281 | 282 | 283 | # pandas DataFrame数据预处理 284 | def data_preprocessing(data): 285 | ''' 286 | :param data: Pandas DataFrame 287 | :return: 处理后的numpy二维数组 288 | ''' 289 | data = pd.get_dummies(data) 290 | data = data.fillna(data.mean()) 291 | return minmax_scale(np.array(data)) 292 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Zhao Xingyu & An Yuexuan. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # -*- coding: utf-8 -*- 16 | from setuptools import setup, find_packages 17 | 18 | with open('README.rst', 'r', encoding='utf8') as f: 19 | long_description = f.read() 20 | setup( 21 | name='ailearn', 22 | version='0.2.1.3', 23 | description='A lightweight package for artificial intelligence', 24 | long_description=long_description, 25 | author='ZHAO Xingyu; AN Yuexuan', 26 | author_email='757008724@qq.com', 27 | license='Apache License, Version 2.0', 28 | url='http://github.com/axi345/ailearn', 29 | packages=find_packages(), 30 | classifiers=[ 31 | 'Intended Audience :: Developers', 32 | 'Intended Audience :: Science/Research', 33 | 'Intended Audience :: Information Technology', 34 | 'Topic :: Scientific/Engineering', 35 | 'Topic :: Scientific/Engineering :: Artificial Intelligence', 36 | 'Topic :: Software Development :: Libraries', 37 | 'Programming Language :: Python :: 2', 38 | 'Programming Language :: Python :: 2.7', 39 | 'Programming Language :: Python :: 3', 40 | 'Programming Language :: Python :: 3.4', 41 | 'Programming Language :: Python :: 3.5', 42 | 'Programming Language :: Python :: 3.6', 43 | 'Operating System :: POSIX :: Linux', 44 | 'Operating System :: Microsoft :: Windows', 45 | 'Operating System :: MacOS :: MacOS X', 46 | 'Environment :: Console', 47 | ], 48 | zip_safe=False, 49 | install_requires=['numpy', 'pandas', 'sklearn', 'matplotlib', 'scipy', 'keras', 'bs4', 'requests'], 50 | ) 51 | --------------------------------------------------------------------------------