去进行咨询
1. 概要
Hyperbox现阶段全力支持:
脊髓构架搜寻(Neural Architecture Search, NAS):如前所述谷歌的NNI构架结构设计并做了改良,牵涉的核心理念组件主要包括hyperbox.mutables和hyperbox.mutator。除此之外hyperbox.networks再次同时实现了许多NAS学术论文中的搜寻内部空间。超参强化(Hyperparameters Optimization, HPO):如前所述hydra和optuna库同时实现手动统计数据进一步增强(Auto Data Augmentation, ADA):如前所述Kornia库同时实现完备的搜寻和教学方法如前所述Pytorch-lightningPCB同时实现,因此像多GPU,混和精确度排序等技术细节的小东西他们能不必害怕了。
2. 加装
加装形式有三种:
第二种是pip加装pip install hyperbox
第三种是源代码加装git clone https://github.com/marsggbo/hyperbox.git
cd hyperbox
python setup.py develop # 提议已开发人员商业模式加装
python install -r requirements.txt
3. 脊髓构架搜寻(NAS)
3.1 可搜寻组件 (hyperbox.mutables)
现阶段hyperbox全力支持同时实现上面三种搜寻组件,这已经能够覆盖现有的大多数数学模型结构了。hyperbox.networks中十几个常见的NAS搜寻内部空间。下面他们能看看图中三种不同搜寻内部空间的代码同时实现示例
图左代码同时实现示例:候选操作搜寻这个其实就是相当于在某一层中我们有若干个候选操作,他们想从中选择出最适合的一个,OperationSpace就是这个用途
import torch.nn as nn
from hyperbox . mutables . spaces import OperationSpace
op1 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, stride =1, padding=1)
op2 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=5, stride =1, padding=2)
op3 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=7, stride =1, padding=3)
ops = OperationSpace(candidates=[op1, op2, op3], key=’conv_op’, mask=[1, 0, 0])
能看到OperationSpace有三个重要的参数:
candidates:很显然就是一个list,每个元素是一个Pytorch的nn.Modulekey:能理解成是一个唯一识别码,如果两个可搜寻组件重名了,那么它们二者的搜寻结果会一样,这个需要特别注意mask: 默认为None,表示当前组件需要搜寻;否则,能传入list或者dict来之指明选择哪一个操作 one-hot格式的list,长度需要和candidates一样。上面mask=[1, 0, 0]表示选择第一个操作,即3×3卷积dict(字典),即{key: value},其中的key和value能有许多组,并且value也必须是list。不过其中必须包含一组{conv_op: [0, 1, 0]}。如无特殊说明,后面三种组件也遵循这种结构设计。
图中代码同时实现示例:输入节点搜寻除了从多个候选操作中选择一个,许多情况他们也想从数学模型前面几层的输出中选择一个或者若干作为当前层的输入,这个时候能用InputSpace
import torch
from hyperbox.mutables.spaces import InputSpace
in1 = torch.rand(2, 64)
in2 = torch.rand(2, 32)
in3 = torch.rand(2, 16)
inputs = [in1, in2, in3]
skipconnect = InputSpace(n_candidates=3, n_chosen=1, key=’sc’, mask=[0, 1, 0])
out = skipconnect([ in1 , in2 , in3 ])
assert out is in2
>>> True
图右代码同时实现示例:细腻度(Finegrained)操作搜寻以上两个组件都是参考的谷歌NNI构架同时实现的,不过他们仅全力支持搜寻完备的操作,许多时候他们也许像搜寻更加细腻度的操作,就像图右所示,他们想搜寻不同卷积核大小,不过它们就像人生无常,大肠包小肠一样地重叠在一起了,现有的构架大都不全力支持这个。Hyperbox同时实现了ValueSpace,然后override了pytorch的所有卷积操作(Conv1d/2d/3d)、线性层(Linear)和batchnorm。只要传入的参数是ValueSpace,hyperbox就能够对这个参数进行搜寻,他们看看下面的例子
from hyperbox.mutables.spaces import ValueSpace
from hyperbox.mutables.ops import Conv2d, Linear
# convolution
ks = ValueSpace([3, 5, 7], key=’kernelSize’, mask=[0, 1, 0])
cout = ValueSpace([16, 32, 64], key=’channelOut’, mask=[0, 1, 0])
conv = Conv2d(3 , cout , ks , stride =1, padding=2, bias=False)
print([x.shape for x in conv.parameters()])
>>> [torch.Size([32, 3, 5, 5])]
# linear
cout = ValueSpace([10, 100], key=’channelOut1’)
isBias = ValueSpace([0, 1], key=’bias’) # 0: False, 1: True
linear = Linear(10, cout , bias=isBias)
print([x.shape for x in linear.parameters()])
>>> [ torch.Size ([100 , 10]) , torch.Size ([100]) ]
3.2 搜寻算法 (hyperbox.mutator)
Random Search Algorithm RandomMutator 前面介绍hyperbox.mutables时,他们能看到都有显式地指明一个参数,即mask=…。但许多时候其实他们不知道数学模型应该长什么样,因此保持默认值mask=None即可。之后,能用到hyperbox.mutator来对结构设计的搜寻内部空间进行搜寻。能看看下面的代码示例:
from hyperbox.mutator import RandomMutator
from hyperbox.networks.ofa import OFAMobileNetV3
net = OFAMobileNetV3(mask=None)
rm = RandomMutator(net)
rm.reset() # search a subnet
arch: dict = rm._cache # arch of a subnet
print(arch)
subnet1 = OFAMobileNetV3(mask=arch) # initialize a subnet, which has smaller parameters than `net`
subnet2 = net.build_subnet(mask_arch)
OFAMobileNetV3是Once-for-all (OFA)学术论文中用到的搜寻内部空间,此时mask=None表示数学模型处于待搜寻状态第5行,数学模型net作为参数传给了RandomMutator,在RandomMutator初始化的时候它会对net中所有的组件遍历一遍,如果发现了hyperbox.mutables组件就会记录下。每次数学模型forward的时候,正常的nn.Module组件没有变化,而hyperbox.mutables的forward会受到Mutator的控制。比如一个OperationSpace的mask搜寻结构是[0,1,0],那么Mutator会手动激活第二个操作参与到的forward排序,除此之外两个不能参与运算。第6行:.reset()是调用搜寻算法,看看RandomMutator源代码应该就能知道是什么作用了第7行:每次调用reset()就会采样得到一个新的子网,该子网的结构用字典表示,存储在_cache这个属性中第10行和11行:这两行显示了三种生成子数学模型的方法,提议使用第三种,因为它能够手动继承Supernet的权重到子网的对应位置。第二种会随机初始化权重。class RandomMutator(Mutator):
def __init__(self, model, *args, **kwargs):
super().__init__(model)
def reset(self):
… # 一些预处理操作
self._cache = self.sample_search()
return self._cache
def sample_search(self):
result = dict()
for mutable in self.mutables:
if isinstance(mutable, OperationSpace):
gen_index = torch.randint(high=mutable.length, size=(1, ))
result[mutable.key] = F.one_hot(gen_index, num_classes=mutable.length).view(–1).bool()
mutable.mask = result[mutable.key].detach()
elif isinstance(mutable, InputSpace):
if mutable.n_chosen is None:
result[mutable.key] = torch.randint(high=2, size=(mutable.n_candidates,)).view(–1).bool()
else:
perm = torch.randperm(mutable.n_candidates)
mask = [i in perm[:mutable.n_chosen] for i in range(mutable.n_candidates)]
result[mutable.key] = torch.tensor(mask, dtype=torch.bool) # pylint: disable=not-callable
mutable.mask = result[mutable.key].detach()
elif isinstance(mutable, ValueSpace):
gen_index = torch.randint(high=mutable.length, size=(1, ))
result[mutable.key] = F.one_hot(gen_index, num_classes=mutable.length).view(–1).bool()
mutable.mask = F.one_hot(gen_index, num_classes=mutable.length).view(–1).bool()
return result
未完待续。。。
超参调优和手动统计数据进一步增强见下一篇文章介绍
Hyperbox框架还有许多能完善的地方,对构架开发感兴趣的小伙伴能扫码入群,有问题也能在群里讨论。
MARSGGBO♥原创
如有意合作或学术讨论欢迎私戳联系~
邮箱:[email protected]