Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Review-2018.05.13-杨磊 #138

Open
DIYer22 opened this issue May 13, 2018 · 1 comment
Open

Review-2018.05.13-杨磊 #138

DIYer22 opened this issue May 13, 2018 · 1 comment

Comments

@DIYer22
Copy link
Member

DIYer22 commented May 13, 2018

Review

一. 在 Furniture 数据集上实验一种共生模型

1. 实验简介

实验一种共生模型,大致思想如下:

共生模型分为宿主模型:host 寄生模型:parasitic, 而 parasitic 能补足 host 的不足之处。

大致结构如下图:
Venom net
每个节点代表一张 feature map,每层代表一个 Block

首先,选一个 base model 作为 host,host 先单独训练。

host 训练完成后,固定住 host 的参数,开始构建 parasitic,parasitic 的 block 数目和 host 相同

对于每一层 block,parasitic 的 Input 要 concat 上一层的 host 和 parasitic 的 Output .

因此,parasitic block 的 Input Channel 为 host 的 1.5 倍数 Output Channel 为 host 的 0.5 倍数,

以此,构建出寄生于 host 的 parasitic 主干网络

接下来移植 TreeSegNet 中的构建树状网络的方法,根据 host 在 validation set 上的性能,构建一颗树状网络:Tree net

将 Tree net, 接在 parasitic 主干网络后面, 使得整个 parasitic 能够学到 host 的不足之处

接下来训练 parasitic 网络,此时 host 的参数是固定的。为避免多余的计算,传给 parasitic 的 feature 要 detach

2. 实现过程 (踩坑记)

通过看 pytorch-summary 的源码,学会了通过 model.apply(regist_hook) 注册 hook 函数来探索网络内部结构,这个操作非常 Hack,但坑也特别多,下文会说。

使用 model.apply(regist_hook) 实现了自动检测 host 的网络结构及每个 block 的内部信息,并自动构建出对应的 parasitic 主干网络

接下来就是训练过程,一开始 我的方案是在 parasitic 内的 self.host=host 使 host 成为 parasitic 的子网络。

parasitic.foward(x) 内部, 先用 self.host.eval() 阻止计算 host 的 grad,再通过 self.host.apply(regist_hook) 注册提取 feature 的钩子函数, 在 self.host(x) 后, 便能通过钩子函数 提取出 host 的 feature。

这样下来发觉计算很慢,一个 epoch 比以前多花几倍的时间。经排查,在host.eval()模式下,host 网络参数的 grad 本应该为 None ,而运行后该参数的 grad 为 0矩阵。这说明 self.host.eval() 失效,整个 host 计算了 grad,哪怕 detach 了,也只是把 detach 部分的导数设置为 0,继续向后计算 grad。这个问题和之前提出来的 不在计算图中仍会计算 grad 的 issue 很相似

为避免 host.grad 的无效计算,不能把 host 作为 parasitic 的子网络。于是,我直接把 host 放到 parasitic 外部 作为全局变量,在 parasitic.foward(x) 内部再调用 host(x) 提取 feature。
这种方案在单卡上表现完美。但在多卡情况下,系统复杂性上升了一个数量集,各种错误。

(下都以双卡为例)
在多卡中,module 要被封装成为 torch.nn.DataParallel
通过查看文档和源码,DataParallel 的原理是:

  1. 把 module 复制两份 分别放在 0, 1 卡上即 module_0, module_1 (此时 整个Pyhton环境 有3个一样的 module,但是 id 不同)
  2. forward 时,把 input:x 在 batch_size 维度 分成两块 分别放到 0, 1 卡上即 x_0, x_1
  3. 多线程的形式 同时对每张卡上的数据和模型并行运行 y_i = module_i.forward(x_i) (多线程中,全局变量是可以共享的)
  4. 最后 将两张卡上 return 的结果 y_i 传输到 output_device (默认为 cuda(0)),合并成原始 batch_size 大小的 y , 再 return y

因此,parasitic.foward(x) 内部的 x 是被 DataParallel 分离出来的 半个 batch_size 的 x_i 了, 而且 x_i 已经是分配到了特定卡上的变量, 只有也在特定卡上的 module 才能处理

我还试过把 feature 打包为 list,作为 host 的输出,再传入 parasitic,但实验发现 DataParallel 只允许 return tensor,遂作罢。

经过不断的实验,最后多卡提取 feature 的方案是:在运行 parasitic.foward(x) 前,先运行 host(x) 并通过钩子函数将 host 的 feature 存储到全局变量 shareDic。钩子函数会根据 feature 所在的 device.id 把 feature 分开存储。在 parasitic.foward(x) 时,会根据 x 所在的 device.id 提取 shareDic 中对应 device 上的 feature ,然后前向计算 parasitic 的输出。

实验表面 上述方法可在 多卡中正常运行,不会计算 host 的 grad,单个 epoch 的时间也下降到了正常水平。

在上述实验中:

  • 自动构建 parasitic 主干网络花了一天半
  • 提 host feature 花了三天

接下来还要把 TreeSegNet 中的 tree net 接上, 再跑实验

二. 完善工具库 Box-X

  1. 重构了多进程和多线程的 map 接口 mapmpmapmt
  2. 写了 setup.py 打包到了PyPI, 可通过 pip install boxx 安装
  3. 完善了 linux/win/osx 的兼容性,以及 $DISPLAY 为空的情况
  4. 添加了 通过代码自动log的工具 叫 logc (log code)
>>> from boxx import logc
>>> a, b = 1, 2
>>> logc("c = a + b")
Code: c = a + b
└──—— 3 = 1 + 2

三. 看了两集台大李宏毅的 Machine Learning and having it deep and structured

Next

  1. Furniture 实验尽快收尾
  2. 复盘人脸生成有关的GAN,起草毕业论文
  3. Box-X 写个简明文档
@ilydouble
Copy link

需要约个事件当面讨论

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants