文章来源:Github
DeepMind 发布的 Sonnet,在 TensorFlow 上用于构建复杂神经网络的开源库。Sonnet 的开源意味着 DeepMind 构建的模型可以更轻松地与所有开发者共享。Sonnet将持续更新 Github 以使其符合 DeepMind 的内部版本。我们对目前 Sonnet 的功能还有很多想法,它们将陆续成为现实。
2016年底,DeepMind发布的论文《通过梯度下降学习通过梯度下降学习》中使用的一些代码就包括了 Sonnet 的初步版本。主要讲述将优化算法作为学习问题,让算法学会自动探索需要的结构。作者包括牛津大学教授、DeepMind 研究员 Nando de Feritas。
摘要
在机器学习里,从手工设计的特征到机器学习的特征这个过程已经非常成功了。但是,优化算法仍然是手工设计的。在本文中,我们展示了如何将优化算法的设计转化为学习问题,让算法学会自动探索需要的结构。最终学出来的算法(由 LSTM 实施),在训练过的任务上表现超越了通用的手工设计的算法,并且在具有相似结构的新的任务中表现也很好(泛化能力很高)。我们在许多任务测试了这种新的算法,包括简单的凸问题、训练神经网络和用神经艺术对图像增加风格(styling images with neural art)。
安装步骤
若想安装 Sonnet,你需要使用 bazel 依靠 TensorFlow 头文件对这个库进行编译。请按照指定步骤安装 TensorFlow:
https://www.tensorflow.org/install/
该库兼容 Linux/Mac OS X 和 Python 2.7。TensorFlow 的版本必须至少为 1.0.1。Sonnet 支持 TensorFlow 的 virtualenv 安装模式,以及 nativ pip 安装。
安装 BAZEL
请确保你拥有最新版本的 bazel(至少为 0.4.5 版),如果版本过旧,请遵循以下步骤:
https://bazel.build/versions/master/docs/install.html
Virtualenv TensorFlow 安装
如果你想使用 virtualenv,请在安装时急活你的 virtualenv,或跳过此步骤:
$ source $VIRTUALENV_PATH/bin/activate # bash, sh, ksh, or zsh
$ source $VIRTUALENV_PATH/bin/activate.csh # csh or tcsh
配置 TensorFlow 头文件
首先复制 Sonnet 和 TensorFlow 的源代码作为一个子模块:
$ git clone --recursive https://github.com/deepmind/sonnet
然后使用 configure:
$ cd sonnet/tensorflow
$ ./configure
$ cd ../
你可以在 TensorFlow 配置期间选择建议的默认值。注意:这不会修改你现有的 TensorFlow 安装。这一步是让 Sonnet 构建于 TensorFlow 头文件上的必要步骤。
构建和运行安装程序
运行安装脚本,在临时目录中创建一个 wheel file:
$ mkdir /tmp/sonnet
$ bazel build --config=opt :install
$ ./bazel-bin/install /tmp/sonnet
pip install 生成的 wheel file:
$ pip install /tmp/sonnet/*.whl
如果已安装 Sonnet,卸载 wheel file 上之前的 pip install :
$ pip uninstall sonnet
你可以通过尝试重采样(resampler op)这样的操作来验证 Sonnet 是否安装成功:
$ cd ~/
$ python>>> import sonnet as snt>>> import tensorflow as tf>>> snt.resampler(tf.constant([0.]), tf.constant([0.]))
预计的输出应该是:
当然,如果引入了 ImportError,C++组件未找到时,请确保你没有导入复制的源代码(即在复制的资源库外调用 Python),并在安装 wheel file 前卸载 Sonnet。
问:为什么又搞了个 TF 库出来?
答:现有的 TF 库对于 DeepMind 来说使用起来非常不灵活,因为我们有大量的使用案例是需要做权重共享的(weight sharing)。现在,所有的东西用 tf.make_template,从一开始就能支持权重共享,由此带来的好处显而易见是超出开发成本的。不仅如此,将配置与连接分离开来的 paradigm 也更方便模块组合。
问:在同一个build()的后续调用中可以访问不同的变量吗?
答:不行。tf.make_template 不允许这样做,它会把后续调用中访问不同的变量当成错误。
问:如果错误地把两个模块做了同样的命名会怎么样?
答:似乎以相同名称构造的模块将具有不同的名称和可变 scope。在内部,Sonnet 使用tf.make_template,它基本上是与一些 tf.VariableScope 一起包装了一个python函数,来确保对该函数的每次调用都发生在同一个 scope 内,并且第一个调用之后的所有调用都设置为重用变量。模板的一个特点是,如果已经在同一 scope 内输入了任何名称,它将使其唯一化。例如:
问:我必须给我的模块命名吗?
答:不。模块有默认名,应该是snake_case中的class name,如果必要的话,它也会被用作唯一化的名称(见上)。然而,我们推荐大家为模块起一个包含变量的名称,因为这一名称也会成为内部 scope 的名称,由此也定义了变量的名称。大多数模块的名称带有“w”(weights)和“b”(bias),来显示内部权重。大家都喜欢这么命名:
你选的名称会出现在 TensorBoard 中。
问:我怎么知道一个模块 declare 了什么变量?
答:你可以对一个模块用 get_variables() 的方法,来查询 scope 内的所有变量。注意如果模块没有连到 graph 上,就会显示错误,因为变量此时不存在,所以相关的 scope 是空的。
问:Sonnet中的所有内容都应该作为模块实现吗?
答:不,不创建tf.Variables并且不存储内部配置的计算可以在常规TF Op样式中实现,即接收输入张量,关键字参数和返回张量输出的python函数。
如果一个op要创建变量(即在内部的任何地方调用tf.get_variable,包括间接的)它必须被作为snt.AbstractModule的一个子类,这样才能正确地处理变量共享。
需要注意,如果一个计算没有创建任何变量,可能仍然需要使用一个Module而不是Op来实现它。
除了可变共享之外,在我们希望将配置参数附加到操作系统的情况下,使用Sonnet模块可以很方便。一个例子是[content addressing](可微分神经计算机中的模块),这些模块接收多个配置参数(内存中的每个单词的大小,读写头的数量),这些输入的一些功能定义了有效输入。我们在这个模块之前使用正确输出大小的snt.Linear来提供正确的维度。由于是一个模块,实现起来很容易——在构建时提供configuration,然后提供一个method.param_size(),它给出所需输入的维数。然后我们就能得到正确的输入张量大小并进行连接。
如果上面的操作是作为op cosine_weights(memory, cos_params, word_size, num_heads) 实现的,那么指示cos_params所需大小的逻辑必须存储在一个单独的函数中。将相关功能封装到一个模块中会让代码更清晰。
问:可以将其与其他高级TF API(如TF Slim)混合使用吗?
答:Sonnet模块一旦构建出来,就遵循Tensor-In-Tensor-Out的原理,因此可以与TF-Slim等的功能混合。不过需要注意的是,这可能会导致意外行为的发生——TF-Slim控制共享是通过传递明确的scope =和 reuse = kwargs到层函数中——如果在Sonnet模块的_build()方法中使用TF-Slim层,多次调用的话就不太可能正常工作。
问:使用Sonnet与其他库与原始TF的开销(overhead)是多少?
答:没有。只有在构造计算图时才会涉及Sonnet。一旦你到了使用Session.run()的阶段,只需执行Ops就行了,不用考虑将该图合在一起用了什么库。
GitHub 链接:https://github.com/deepmind/sonnet