PyTorch-Ignite PyTorch-Ignite

用PyTorch Ignite的代码生成器和Nebari连接数据科学工具

The Code-Generator and Nebari

致读者们,我是Aryan Gupta(@guptaaryan16),印度理工学院鲁尔基分校的电气工程大三学生。今年夏天,我有机会参与了PyTorch-Ignite的代码生成器项目,这是一个专门为机器学习研究者和爱好者设计的网页应用程序,同时也考虑到了日益增长的Kaggle社区。

转载自 https://labs.quansight.org/blog

项目本身

让我们来看看项目本身。 Code-Generator 是一个 Vue.js 应用程序,它简化了处理机器学习任务的过程。该应用程序为视觉分类、文本分类和机器学习竞赛中的其他常见主题生成预配置的代码模板。

目标不是成为PyTorch和PyTorch-Ignite之上的另一个抽象层。相反,Code-Generator是一个生成样板代码的网页应用程序,这意味着您仍然可以完全控制生成的代码。您可以从选择最小模板(如“文本分类”)开始,并根据需要打开或关闭选项,选择特定的日志记录器如Tensorboard或参数解析器如Python-Fire。这个应用程序在Kaggle的数据科学竞赛或撰写论文时可能很有帮助,特别是可以轻松集成日志记录器和检查点处理器。此外,这些模板在CI上经过了充分测试,希望能在您的工作流程中只需最少的更改即可使用。

这个应用程序是在PyTorch-Ignite社区成员的巨大努力下创建的,特别是@ ydcjeff @ trsvchn @ vfdev-5

The Code-Generator App

我处理的问题

我的任务主要是开发能够提高模板可重复性相关特性的功能。此外,我还需要为新的功能和库做出贡献,这些功能和库最适合实验的配置管理。我对项目的贡献主要有三个方面,具体如下。

1. 致力于增强模板和现有实验结果的可重复性范围

在这一部分,我致力于提高模板的可读性和API。这促使我进行了一些必要的API更改,并测试了用于编写数据科学和Kaggle竞赛研究文献的现有库。我们在PyYaml项目中发现的一个有趣的错误是

#~~ Saving config.yaml for reproducibility
#-------------------------------------
## Config file 1 (config1.yaml)
lr: 1e-10

## Config file 2 (config2.yaml)
lr: 1.0e-10
#-------------------------------------

with open("config1.yaml", "r") as f1:
	config1 = yaml.safe_load(f1) # loads lr -> string
>> config1 = { 'lr': '1e-10' }

with open("config2.yaml", "r") as f2:
  config2 = yaml.safe_load(f2) # loads lr -> python-floating point
>> config2 = { 'lr': 1e-10 } # (the expected behaviour)

还有其他问题,比如无法使用PyYAML API存储Python对象。这导致我们采用OmegaConf.DictConfig作为在模板中维护配置数据的API标准。这在测试中效果很好,并且由开发者很好地维护。此外,像配置字典解析这样的方法在这个OmegaConf项目中写得非常好。

此外,我们还增加了对 HydraPython-Fire 的支持,因为它们在配置管理方面表现出色,并且与 PyTorch-Ignite 配合得非常好。它们还允许使用以下 bash 命令覆盖现有配置。

[For Python Fire]
>> python main.py config.yaml lr=0.001

[For hydra]
>> python main.py lr=0.001 ++checkpoint_dir=./logs

请注意,您必须仅在config.yaml中未出现的覆盖项中使用++,以便Hydra CLI使用。更多信息,请查看 OmegaConfPython-FireHydra的文档。

一些相关的PR可以在这里找到: #292 #300 #302

2. 与Nebari及其他基础设施管理工具的集成

我参与了测试这个优秀的基础设施工具,Nebari,它用于管理数据科学家和其他专业人士的GPU集群和云基础设施。它提供了一个JupyterHub界面,这对于部署代码和运行工具非常有帮助。因此,为了解释我的项目与Nebari的集成,首先,我想讨论一下项目如何存储具有特定配置的模板。

The Nebari-server Option in Code-Generator App

为了快速解释,让我们看看当你点击Open in Nebari时应用程序是如何工作的。点击按钮后,我们使用一个netlify函数来提交一个zip文件和Jupyter笔记本,使用 github/octokitpytorch-ignite/nbs仓库。我们可以在下面看到一个提交的笔记本示例。为了更好地理解这一点,你可以阅读 pytorch-ignite/code-generator/functions/nebari.js中的代码。

GitHub Notebook Pushed By Code-Generator

现在,为了将其与Nebari服务器集成,我们使用了一个名为Jupyterlab-Github的扩展。我编写了一个netlify函数来创建一个可以使用此扩展的URL,在新标签页中打开生成的链接,拉取上述netlify函数提交的笔记本,并在服务器中打开此笔记本。这里可以看到一个示例。非常酷,对吧?

Template Opened in Nebari

为此,我非常感谢Jupyter社区的扩展生态系统。他们为各种功能提供了出色的扩展,也许在未来,我可能会工作或尝试添加更多扩展,以增加应用程序的功能。此外,我要感谢这里的Nebari-dev社区,他们在Nebari服务器上测试时提供了如此出色的支持。

一些相关的PR可以在这里找到: #265 #314

3. CI 和其他基于 Vuejs 的代码库部分的变化(主要基于 JS)

我不得不为应用程序贡献一些基于JavaScript的代码,但由于我之前没有在JS项目上工作过,我需要在为这些问题做出贡献之前学习基础知识,特别是 Vue.js :-( 。所以,我尝试通过阅读一些在线教程和文档来学习。我特别喜欢这个 教程系列在YouTube上。

为了进一步解释如何生成模板,假设我创建了一些文件,如main.pymodel.pyutils.py作为模板。现在我们使用ejs项目来添加和选择特定选项,并在浏览器中运行时渲染它们。为了说明这一点,让我们从模板中举一个例子。

#::: if ((it.argparser == 'fire')) { :::#
    fire.Fire(main)
#::: } else if ((it.argparser == 'hydra')){ :::#
    sys.argv.append("hydra.run.dir=.")
    sys.argv.append("hydra.output_subdir=null")
    sys.argv.append("hydra/job_logging=stdout")
    main()
#::: } else { :::#
    main()
#::: } :::#

现在,如你所见,有一些被注释的代码。这是JS的一部分,它帮助选择argparsers的不同配置。看起来很简单,对吧?

但管理起来可能具有挑战性,我们还需要确保满足CI的lint格式化要求(这在模板中可能相当棘手 :-( )。尽管如此,这些JS功能非常强大,我们正在尽可能改进这些功能。顺便说一下,如果你对it.argparser选择器的来源感到困惑,最好检查一下项目,但简短的答案是它来自一个metadata.json文件,该文件维护了应用中所有模板的选项。

一些相关的PR可以在这里找到: #283 #288

项目的前进方向

Code-Generator项目似乎正在以很快的速度前进,预计未来会有更多的添加。在开源中启动和维护一个新项目是具有挑战性的,并且可能具有非常高的风险/回报比。尽管如此,良好的社区会尽力制作和维护对最多人有益的项目,并减少新成员进入社区的摩擦。以下是我建议的一些问题,并可能在未来进行工作。

1. 为每个模板分离 metadata.json

我们正在考虑为每个模板提供单独的metadata.json文件或类似的东西。我已经为此向项目提出了一个问题这里,并将在未来尝试解决这个问题。

2. 贡献新模板的脚本

由于数据科学爱好者很难学习JS并轻松贡献模板,这似乎也是采用该应用程序的一个摩擦点,我们建议添加一个脚本,可以帮助根据问题中的建议对应用程序进行JS和CI相关的更改这里。这个问题可能需要一些艰苦的工作,但它可以成为应用程序的一个很好的补充。

3. 更多模板和功能选项

我们将在未来向项目中添加更多模板和功能选项。像目标检测、文本摘要和扩散模型这样的模板可以成为项目的优秀补充。

附注:如果您对这个项目感到兴奋,请随时提出更多更改建议并为项目做出贡献。

我在这一路上学到的一些经验

这段实习经历增强了我在维护开源代码库和了解如何测试函数和调试更改方面的信心。它还帮助我理解了现实生活中的设计决策和持续集成(CI)的价值,以及它们如何显著影响项目的成败。以下是我在开源项目中学到的一些最佳实践。

1. 尝试使代码更改更加以开发者为中心

当我们尝试编写新代码或更改现有文件时,我们常常试图以添加最多功能的方式进行,而常常忘记编写高质量的代码。在处理新更改时,尝试更好地组织代码,并将通用代码简化为单独的utils文件。这可以使代码库更好,更易于其他开发人员查看和理解。此外,留下有意义且简短的注释,因为它可以为下一个接手你项目的开发人员节省一些时间。

2. 尝试为大的更改创建单独的PR

虽然在项目中提交拉取请求(PR)看起来非常令人兴奋,但你应该知道在一个拉取请求中你想要完成什么以及完成多少。有时,我们可能会在一个拉取请求中做出太多的功能更改,这使得测试和修复所有错误变得困难。这个问题在我实习期间非常明显,因为我的一些PR格式非常糟糕,这导致我不得不完全重新基于它们,并向主项目提交4-5个更多的PR来修复这些更改中的许多错误。这对我来说和我的导师来说都是痛苦的,但我感谢他为我提供了很好的建议和审查。 此外,这次经历增加了我的学习,并帮助我提高了拉取请求的质量。所以不要害怕犯错;从中学习,并在这个令人难以置信的开源软件世界中找到自己的道路!

致谢

在本节中,我要感谢我的导师, @vfdev-5,以及PyTorch-Ignite社区的其他成员在我实习期间给予的帮助。同时,我也要感谢 Quansight-Labs 的人们为我提供了这样一个绝佳的机会,特别是 @rgommers@trallard@melissawm,以及其他人在我实习期间提供的宝贵指导和时间。

参考文献