├── README.md ├── 如何成为顶级开源项目的贡献者(文档篇).md └── 如何成为顶级开源项目的贡献者(源码篇).md /README.md: -------------------------------------------------------------------------------- 1 | 2 | ![Logo](http://www.tisv.cn/img/logo.png) 3 | [![Build Status](http://www.tisv.cn/img/badge.svg)](http://www.tisv.cn/) 4 | 5 | --- 6 | 7 | 8 | 1,[如何成为顶级开源项目的贡献者(源码篇)](https://github.com/AITutorials/experience/blob/master/如何成为顶级开源项目的贡献者(源码篇).md) 9 | 10 | 2,[如何成为顶级开源项目的贡献者(文档篇)](https://github.com/AITutorials/experience/blob/master/如何成为顶级开源项目的贡献者(文档篇).md) 11 | 12 | 13 | --- 14 | 15 | ## 其他资源 16 | 17 | * [Datasets(集成世界范围内的免费数据集和数据站点)](https://github.com/AITutorials/datasets) 18 | 19 | 20 | --- 21 | 22 | ### 致谢 23 | 24 | 感谢以下机构或站点提供的人工智能工具和参考学习资料。所有人可以通过点击下方图片阅读免费电子书籍或访问这些机构的官方站点。 25 | 26 | 27 | | [![avatar](http://ai.tisv.cn/img/book11.png)](https://livebook.manning.com/book/deep-learning-with-python/) | [![avatar](https://user-images.githubusercontent.com/61530230/76381930-e7e25900-6391-11ea-861a-5ceebb96d4bd.png)](https://www.deeplearningbook.org/contents/TOC.html) | [![avatar](http://ai.tisv.cn/img/book13.png)](http://neuralnetworksanddeeplearning.com/)| 28 | | ---- | ---- | ---- | 29 | | [![avatar](http://ai.tisv.cn/img/t1.png)](https://tensorflow.google.cn/) | [![avatar](http://ai.tisv.cn/img/t2.png)](https://pytorch.org/) | [![avatar](http://ai.tisv.cn/img/t3.png)](https://keras.io/) | 30 | 31 | --- 32 | -------------------------------------------------------------------------------- /如何成为顶级开源项目的贡献者(文档篇).md: -------------------------------------------------------------------------------- 1 | 2 | ### 概述 3 | 4 | 文档的贡献与源码的贡献其实同样重要,而且据我所知,源码贡献者大都是从文档贡献开始的,因为你能看懂和改动源码的前提往往是对使用文档或API说明已经烂熟于心。那下面我们就来看看如何贡献文档! 5 | 6 | 7 | --- 8 | 9 | ### 文档的分类 10 | 11 | 在正式开始之前,先给大家明确以下我们所说的'文档'到底有哪些种类,从当前的顶级开源项目来看,可分为三种: 12 | 13 | * 官方中源码类/函数的API说明文档,即可能是文档本身的内容又可能是源码中的注释。 14 | * 官方的指南或者教程文档,大部分为md文件,目前也包括在colab上运行的ipynb文件。 15 | * 社区提供的翻译或其他贡献文档,这些文档大都是还原或补充官方文档,因此和上述两种文档形式类似。 16 | 17 | 18 | 下面我们就从这三点展开进行说明。 19 | 20 | --- 21 | 22 | ### 官方中源码类/函数的说明文档 23 | 24 | 发现官方API的代码注释出现问题,一般都是通过对该API的使用。因此我们一定是先掌握了这些API的查看方法,比如[Tensorflow r2.2 API文档地址](https://tensorflow.google.cn/api_docs/python/tf), 我们可以通过该地址查看所有当前版本的API说明。 25 | 26 | * 假如此时你发现了问题,那么你便可以通过View source on GitHub来到这个API文档的github页,放心,所有的顶级开源项目一定会设置这个链接,你不会找不到它们!所以此刻,你已经定位了该API源码以及对应文档说明的位置,你需要像修改源码一样在你的分支中修改他们([查看源码篇](https://github.com/AITutorials/experience/blob/master/%E5%A6%82%E4%BD%95%E6%88%90%E4%B8%BA%E9%A1%B6%E7%BA%A7%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E7%9A%84%E8%B4%A1%E7%8C%AE%E8%80%85%EF%BC%88%E6%BA%90%E7%A0%81%E7%AF%87%EF%BC%89.md)),然后提交PR。 27 | 28 | * 当然,如果你只是修改md文件,那么只需要在github中操作即可,用铅笔图标来打开文件编辑器进行文件编辑,然后提交新的拉取请求(pull request)即可。 29 | 30 | * 注意:修改文档必然也存在指定的规范,比如Tensorflow可以参考[TensorFlow 2.0 API文档建议](https://docs.google.com/document/d/1e20k9CuaZ_-hp25-sSd8E8qldxKPKQR-SkwojYr_r-U/preview)。 31 | 32 | 33 | 34 | --- 35 | 36 | ### 官方的指南或者教程文档 37 | 38 | 这是我们认为的最该是文档的文档,它们大部分由md写成,现在也有ipynb的形式。 39 | 40 | * 对于md文件出现问题,我们建议直接在github中进行修改,使用那个铅笔图标来打开文件编辑器进行文件编辑,然后创建一个新的PR提交即可。 41 | * 对于ipynb文件,可能要稍微麻烦一些,因为它不是直接打开可读的文件,你需要在colab上打开它(copy一个副本进行修改),然后编辑修改的地方后下载到本地,在你已经clone的项目中覆盖原有的ipynb,然后远程提交这个分支,没错,所有的过程还是和源码篇一样。[查看源码篇](https://github.com/AITutorials/experience/blob/master/%E5%A6%82%E4%BD%95%E6%88%90%E4%B8%BA%E9%A1%B6%E7%BA%A7%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E7%9A%84%E8%B4%A1%E7%8C%AE%E8%80%85%EF%BC%88%E6%BA%90%E7%A0%81%E7%AF%87%EF%BC%89.md) 42 | 43 | --- 44 | 45 | 46 | ### 社区提供的翻译或其他贡献文档 47 | 48 | 各大开源项目的社区组织形式和贡献方式都存在较大的差异,关于更多的信息,可以详细相对应的社区贡献文档。 49 | 50 | -------------------------------------------------------------------------------- /如何成为顶级开源项目的贡献者(源码篇).md: -------------------------------------------------------------------------------- 1 | ### 概述 2 | 3 | 4 | 对于程序员来讲,成为顶级开源项目的贡献者是一件有意义的事,当然,这也绝非易事。如果你正从事人工智能有关的工作,那么你一定了解诸如[Google Tensorflow](https://github.com/tensorflow),[Facebook Pytorch](https://github.com/pytorch)这样的开源项目。下面我们就说一说如何成为这些顶级的开源项目的Contributor。 5 | 6 | 7 | 8 | --- 9 | 10 | 11 | ### 准备 12 | 13 | 14 | 1,首先你必须成为github的使用者,并已经熟悉了github上托管代码的基本逻辑。 15 | 16 | 2,对于顶级的开源项目,一般需要你去签署一份Contributor License Agreement(简称CLA),例如Tensorflow项目,个人签署[TF individual CLA](https://code.google.com/legal/individual-cla-v1.0.html),公司签署[TF corporate CLA](https://code.google.com/legal/corporate-cla-v1.0.html),Pytorch中的部分项目则需要签署[Facebook CLA](https://code.facebook.com/cla/),这样你的代码才允许被接收。 17 | 18 | 3,让你编写的代码风格更规范,一般的开源项目都要求为[Google Python Style](https://github.com/google/styleguide/blob/gh-pages/pyguide.md),即使是Pytorch都是遵循该规范,更不要说Google自家的Tensorflow了。 19 | 20 | 4,你贡献的代码往往由类或者函数构成(文档贡献除外),因此你需要单元测试程序,它和代码注释一样,是代码共享过程中必不可少的一部分。没有它,即使你的代码正确无误也不会被merge,最终还是会被要求提供单元测试脚本。 21 | 22 | 23 | 5,很多开源项目要求你的每个py脚本都要以许可证书开头,比如Tensorflow,这是它的python许可证书示例: [Python license example](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/ops/nn.py#L1),当然,这很简单。 24 | 25 | ```python 26 | # Copyright 2015 The TensorFlow Authors. All Rights Reserved. 27 | # 28 | # Licensed under the Apache License, Version 2.0 (the "License"); 29 | # you may not use this file except in compliance with the License. 30 | # You may obtain a copy of the License at 31 | # 32 | # http://www.apache.org/licenses/LICENSE-2.0 33 | # 34 | # Unless required by applicable law or agreed to in writing, software 35 | # distributed under the License is distributed on an "AS IS" BASIS, 36 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 37 | # See the License for the specific language governing permissions and 38 | # limitations under the License. 39 | # ============================================================================= 40 | ``` 41 | 42 | 43 | ### 工具 44 | 45 | 接下来我们将介绍相关工具的使用,它能够有效的帮助我们来完成贡献前的准备工作,比如:代码规范和单元测试等。 46 | 47 | 48 | #### 代码规范工具 49 | 50 | 51 | * 为了满足代码满足Google Style的要求,我们首先需要一个代码规范检测工具,这里我们使用官方推荐的[pylint](https://pylint.org/)。 52 | 53 | > * 安装: 54 | 55 | ```shell 56 | pip install pylint 57 | ``` 58 | 59 | --- 60 | 61 | > * 使用: 62 | 63 | 64 | ```shell 65 | # 使用pylint检测脚本代码,默认将按照PEP8标准 66 | # 这里我们需要指定配置文件,即按照Google Style标准 67 | # myfile.py代表你写好的python脚本文件 68 | pylint --rcfile=pylintrc myfile.py 69 | ``` 70 | 71 | --- 72 | 73 | > * pylintrc内容请参照: [pylintrc](https://github.com/tensorflow/tensorflow/edit/master/tensorflow/tools/ci_build/pylintrc) 74 | 75 | 76 | --- 77 | 78 | 79 | * 又因为我们初始写的代码往往随意性过强,可能直接用pylint需要修改的地方太多,可能对你幼小的心灵造成重创,因此,这里也带来很多开源项目推荐的另外一款工具:[black](https://github.com/psf/black),它能够直接帮你修改代码中出现的基本问题(仍然存在很多问题无法被判定,需要使用pylint检测)。 80 | 81 | > * 安装: 82 | 83 | ```shell 84 | pip install black 85 | ``` 86 | 87 | --- 88 | 89 | 90 | > * 使用: 91 | 92 | ```shell 93 | # 这里的-l代表代码的每行最大长度 94 | # 默认是88,但是Google Style要求为80 95 | # 因此这里指定为80 96 | black myfile.py -l 80 97 | ``` 98 | 99 | 100 | --- 101 | 102 | > * 代码样式示例: 103 | 104 | ```python 105 | def my_op(tensor_in, other_tensor_in, my_param, other_param=0.5, 106 | output_collections=(), name=None): 107 | """My operation that adds two tensors with given coefficients. 108 | 109 | Args: 110 | tensor_in: `Tensor`, input tensor. 111 | other_tensor_in: `Tensor`, same shape as `tensor_in`, other input tensor. 112 | my_param: `float`, coefficient for `tensor_in`. 113 | other_param: `float`, coefficient for `other_tensor_in`. 114 | output_collections: `tuple` of `string`s, name of the collection to 115 | collect result of this op. 116 | name: `string`, name of the operation. 117 | 118 | Returns: 119 | `Tensor` of same shape as `tensor_in`, sum of input values with coefficients. 120 | 121 | Example: 122 | >>> my_op([1., 2.], [3., 4.], my_param=0.5, other_param=0.6, 123 | output_collections=['MY_OPS'], name='add_t1t2') 124 | [2.3, 3.4] 125 | """ 126 | with tf.name_scope(name or "my_op"): 127 | tensor_in = tf.convert_to_tensor(tensor_in) 128 | other_tensor_in = tf.convert_to_tensor(other_tensor_in) 129 | result = my_param * tensor_in + other_param * other_tensor_in 130 | tf.add_to_collection(output_collections, result) 131 | return result 132 | ``` 133 | 134 | ```python 135 | output = my_op(t1, t2, my_param=0.5, other_param=0.6, 136 | output_collections=['MY_OPS'], name='add_t1t2') 137 | ``` 138 | 139 | 140 | --- 141 | 142 | 143 | #### 单元测试工具 144 | 145 | 146 | * 单元测试对于团队开发十分重要,是检验代码质量的重要依据,因此你的每一份完整的代码都要配备单元测试脚本。这里我们使用python主流的单元测试工具:[unittest](https://docs.python.org/3/library/unittest.html#module-unittest)。 147 | 148 | 149 | > * 安装: 150 | 151 | ```shell 152 | pip install unittest 153 | ``` 154 | 155 | --- 156 | 157 | 158 | > * 使用: 这里只去演示核心的使用方法,更具体的内容请参照[unittest文档](https://docs.python.org/3/library/unittest.html#module-unittest) 159 | 160 | 161 | ```python 162 | # 导入unittest工具包 163 | import unittest 164 | 165 | # 我们首先要建立一个测试类,它将包含你所有需要进行测试的函数 166 | # 这个类不使用__init__(self),但可以使用setUp(self)来定义公有部分 167 | # 它需要继承unittest.TestCase, 类名往往也建议以Test开头 168 | class TestStringMethods(unittest.TestCase): 169 | # 类的里面依次是你需要进行测试的函数 170 | # 这些函数建议以test_开头 171 | # 这些函数一般情况不设置参数,而是直接在函数中具体化需要的参数 172 | # 当然你也可以设置原始的参数,然后在外部具体化参数并调用该函数 173 | # 在测试函数中必须存在assert...来断定测试结果 174 | # 常用的assert...包括: assertEqual, assertTrue, assertFalse, 175 | # assertRaises, assertIn, assertNotIn, assertIs, assertIsNot... 176 | def test_upper(self,): 177 | # 使用assertEqual判断两个字符串是否相等 178 | self.assertEqual( 179 | "foo".upper(), "FOO", 180 | ) 181 | 182 | def test_isupper(self,): 183 | # 使用assertTrue/False断定条件为真/假 184 | self.assertTrue("FOO".isupper()) 185 | self.assertFalse("Foo".isupper()) 186 | 187 | def test_split(self,): 188 | # 设定任意输入 189 | s = "hello world" 190 | # 使用assertIn断定列表包含关系 191 | self.assertIn( 192 | s.split(), [["hello", "world"]], 193 | ) 194 | # 注意:这里with self.assertRaises来断定异常 195 | with self.assertRaises(TypeError): 196 | s.split("asd") 197 | 198 | 199 | # 这里是主函数,如果使用python运行该脚本测试,则必须存在 200 | # 如果使用pytest(后面会介绍),则可以省略 201 | if __name__ == "__main__": 202 | # 使用unittest.main运行所有继承unittest.TestCase的类 203 | unittest.main() 204 | 205 | ``` 206 | 207 | --- 208 | 209 | 210 | > * 装饰器的使用: unittest最常使用方法之一就是类/函数的装饰器。 211 | 212 | 213 | 214 | ```python 215 | # 对于一些特殊需要强制跳过的测试的类/函数使用下方装饰器,但你必须说明原因 216 | # @unittest.skip("长得太帅,不需要测试,给我跳过!") 217 | 218 | # 如果条件为真,则该测试被强制跳过。比如:检测GPU是否可用 219 | # @unittest.skipIf(TEST_CUDA, "CUDA available") 220 | 221 | # 除非条件为真,否则该测试被强制跳过。比如: 检测某些依赖包是否安装 222 | # @unittest.skipUnless(has_unittest, "unittest dependencies are not installed") 223 | 224 | # 函数异常测试的表达方式,函数出现异常则测试通过,比之前说的内部异常粒度更大 225 | # @unittest.expectedFailure 226 | 227 | import torch 228 | try: 229 | import unittest 230 | except ImportError: 231 | has_unittest = False 232 | else: 233 | has_unittest = True 234 | 235 | if torch.cuda.is_available(): 236 | TEST_CUDA = True 237 | else: 238 | TEST_CUDA = False 239 | 240 | # 条件为真,不跳过 241 | @unittest.skipUnless(has_unittest, "unittest dependencies are not installed") 242 | # 条件为真,跳过;条件为假,不跳过 243 | @unittest.skipIf(TEST_CUDA, "CUDA available") 244 | class TestStringMethods(unittest.TestCase): 245 | def test_upper(self,): 246 | self.assertEqual( 247 | "foo".upper(), "FOO", 248 | ) 249 | @unittest.skip("长得太帅,不需要测试,给我跳过!") 250 | def test_isupper(self,): 251 | self.assertTrue("FOO".isupper()) 252 | self.assertFalse("Foo".isupper()) 253 | 254 | @unittest.expectedFailure 255 | def test_split(self,): 256 | s = "hello world" 257 | self.assertIn( 258 | s.split(), [["hello", "world"]], 259 | ) 260 | # 这里预计抛出异常,但实际没有异常,本质上这也算一种异常 261 | # 可以使用@unittest.expectedFailure 262 | with self.assertRaises(TypeError): 263 | s.split("ZMZ") 264 | 265 | 266 | if __name__ == "__main__": 267 | unittest.main() 268 | 269 | ``` 270 | 271 | --- 272 | 273 | 274 | > * 运行你的测试脚本: 275 | 276 | ```shell 277 | # 建议使用pytest执行测试脚本,你的python中往往自带这个工具包 278 | # 这时你不必写下主函数,并且他的输出形式更美观 279 | pytest test_myfile.py 280 | ``` 281 | 282 | --- 283 | 284 | > * 输出效果: 285 | 286 | ```text 287 | ======================== test session starts ========================= 288 | platform linux -- Python 3.7.3, pytest-5.0.1, py-1.8.0, pluggy-0.12.0 289 | rootdir: /root 290 | plugins: remotedata-0.3.1, celery-4.3.0, doctestplus-0.3.0, arraydiff-0.3, openfiles-0.3.2 291 | collected 3 items 292 | 293 | test_myfile.py sx. [100%] 294 | 295 | =========== 1 passed, 1 skipped, 1 xfailed in 0.34 seconds =========== 296 | ``` 297 | 298 | --- 299 | 300 | * 真实单元测试脚本请参考[Pytorch Tests](https://github.com/pytorch/pytorch/tree/master/test)和[Tensorflow Tests](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/python/util)。 301 | 302 | 303 | 304 | 305 | 306 | --- 307 | 308 | 309 | ### 过程 310 | 311 | 在准备成为贡献者之前,要确保你已经能够熟练使用该项目。进而明确你要贡献源码的类型,是Fix Bug还是Implement New Feature(实现新特性)。当然,对一个新手贡献者来讲,Fix Bug是你的不二选择。除非你已经通过自己的实践,明确了要做贡献的具体内容,否则,建议你需要遵循以下步骤: 312 | 313 | * 第一步: 314 | 315 | > * 从开源项目的Github Issues中寻找open的问题,这里是[Tensorflow Issues](https://github.com/tensorflow/tensorflow/issues), [Pytorch Issues](https://github.com/pytorch/pytorch/issues),仔细阅读大家提出的问题,这将帮你在寻找问题上节约大量时间,同时你可以在讨论区看到有关技术的讨论或已经提交的PR,进一步明确自己是否应该参与该问题的解决。(有很多开源项目的issue会带有"contributions welcome"的标签,可以优先看一看。) 316 | 317 | 318 | * 第二步: 319 | 320 | > * 当你明确了自己要解决的问题,在正式写代码之前,你需要fork这个开源项目到你自己的Github仓库,然后再将该仓库clone到自己指定的服务器上,这样最后你才可以提交PR。 321 | 322 | ```shell 323 | # 例如: 324 | git clone https://github.com/AITutorials/tensorflow.git 325 | ``` 326 | 327 | 328 | > * 到这里你可以通过git remote -v发现我们只与自己远程仓库进行了连接(origin/master)。 329 | 330 | 331 | > * 此时我们还需要与开源项目的远程仓库建立连接(upstream/master) 332 | 333 | ```shell 334 | # 以tensorflow为例建立连接 335 | git remote add upstream https://github.com/tensorflow/tensorflow.git 336 | 337 | # 查看到upstream 338 | git remote -v 339 | ``` 340 | 341 | --- 342 | 343 | > * 然后你就需要建立一个自己的分支,当然,你可以先查看一下远程的分支情况 344 | 345 | ```shell 346 | # 查看远程分支 347 | git branch -a 348 | 349 | # 创建自己的远程分支cnsync 350 | git checkout -b cnsync 351 | ``` 352 | 353 | --- 354 | 355 | 356 | * 第三步: 357 | 358 | > * 通过第二步你已经拿到了项目的源码并创建了自己分支,这时就要开始你的表演,coding + review,你之前准备的[代码规范工具](http://52.83.69.131:8888/1/#_4)和[单元测试工具](http://52.83.69.131:8888/1/#_5)将派上用场。 359 | 360 | 361 | --- 362 | 363 | * 第四步: 364 | 365 | > * 提交代码你的代码并在github中创建一个PR。 366 | 367 | ```shell 368 | # 把内容添加到暂存区 369 | git add . 370 | 371 | # 提交更改的内容 372 | git commit -m "添加你的改变说明" 373 | 374 | # push到自己的远程仓库 375 | git push origin cnsync 376 | ``` 377 | 378 | > * 注意:这里虽然你只push到了自己的远程仓库,但其实你的远程仓库和源项目的仓库是连接的。也就是说,此时你可以通过操作自己的远程仓库决定是否将创建一个源项目的PR(这些过程可以在你刚刚fork的项目页面中实现,包括填写PR的title和comment,有时你也需要在title中添加一个标记,如[Draft]/[WIP]/[RFR]等等)。 379 | 380 | 381 | --- 382 | 383 | 384 | * 第五步: 385 | 386 | > * 耐心的等待,如果你是PR是一个Ready For Review的状态,它将很快进入自动化测试的流程以及评委会的介入,不久后你将收到一些反馈,你的代码方案可能被采纳,可能需要更多的修改或测试。 387 | 388 | 389 | 390 | --- 391 | 392 | 393 | ### Pytorch-NLP贡献经历 394 | 395 | --- 396 | 397 | ### 结语 398 | 399 | 最终,经过不断地磨练,你将成为一名顶级开源项目的贡献者,所以,加油吧少年! 400 | 401 | 402 | 403 | 404 | --------------------------------------------------------------------------------