ENAS:首个权值共享的神经网络搜索方法,千倍加速 ICML 2018

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ENAS:首个权值共享的神经网络搜索方法,千倍加速 ICML 2018相关的知识,希望对你有一定的参考价值。

参考技术A

论文: Efficient Neural Architecture Search via Parameter Sharing

  神经网络结构搜索(NAS)目前在图像分类的模型结构设计上有很大的成果,但十分耗时,主要花在搜索到的网络(child model)的训练。论文的主要工作是提出 Efficient Neural Architecture Search (ENAS),强制所有的child model进行权重共享,避免从零开始训练,从而达到提高效率的目的。虽然不同的模型使用不同的权重,但从迁移学习和多任务学习的研究结果来看,将当前任务的模型A学习到的参数应用于别的任务的模型B是可行的。从实验看来,不仅共享参数是可行的,而且能带来很强的表现,实验仅用单张1080Ti,相对与NAS有1000x倍加速

  NAS的搜索结果可以看作是大图中的子图,可以用单向无环图(DAG)来表示搜索空间,每个搜索的结构可以认为是图2的DAG一个子网。ENAS定义的DAG为所有子网的叠加,其中每个节点的每种计算类型都有自己的参数,当特定的计算方法激活时,参数才使用。因此,ENAS的设计允许子网进行参数共享,下面会介绍具体细节

  为了设计循环单元(recurrent cell),采用 节点的DAG,节点代表计算类型,边代表信息流向,ENAS的controller也是RNN,主要定义:1) 激活的边 2) 每个节点的计算类型。在NAS(Zoph 2017),循环单元的搜索空间在预先定义结构的拓扑结构(二叉树)上,仅学习每个节点的计算类型,而NAS则同时学习拓扑结构和计算类型,更灵活

  为了创建循环单元,the controller RNN首先采样 个block的结果,取 , 为当前单元输入信息(例如word embedding), 为前一个time step的隐藏层输出,具体步骤如下:

  注意到每对节点( )都有独立的参数 ,根据选择的索引决定使用哪个参数,因此,ENAS的所有循环单元能同一个共享参数集合。论文的搜索空间包含指数数量的配置,假设有N个节点和4种激活函数,则共有 种配置

  ENAS的controller为100个隐藏单元的LSTM,通过softmax分类器以自回归(autoregressive fashion)的方式进行选择的决定,上一个step的输出作为下一个step的输入embedding,controller的第一个step则接受空embedding输入。学习的参数主要有controller LSTM的参数 和子网的共享权重 ,ENAS的训练分两个交叉的阶段,第一阶段在完整的训练集上进行共享权重 学习,第二阶段训练controller LSTM的参数

  固定controller的策略 ,然后进行 进行随机梯度下降(SGD)来最小化交叉熵损失函数的期望 , 为模型 在mini-batch上的交叉熵损失,模型 从 采样而来

  梯度的计算如公式1, 上从 采样来的,集合所有模型的梯度进行更新。公式1是梯度的无偏估计,但有一个很高的方差(跟NAS一样,采样的模型性能差异),而论文发现,当 时,训练的效果还行

  固定 然后更新策略参数 ,目标是最大化期望奖励 ,使用Adam优化器,梯度计算使用Williams的REINFORCE方法,加上指数滑动平均来降低方差, 的计算在独立的验证集上进行,整体基本跟Zoph的NAS一样

  训练好的ENAS进行新模型构造,首先从训练的策略 采样几个新的结构,对于每个采样的模型,计算其在验证集的minibatch上的准确率,取准确率最高的模型进行从零开始的重新训练,可以对所有采样的网络进行从零训练,但是论文的方法准确率差不多,经济效益更大

  对于创建卷积网络,the controller每个decision block进行两个决定,这些决定构成卷积网络的一层:

  做 次选择产生 层的网络,共 种网络,在实验中,L取12

  NASNet提出设计小的模块,然后堆叠成完整的网络,主要设计convolutional cell和reduction cell

  使用ENAS生成convolutional cell,构建B节点的DAG来代表单元内的计算,其中node 1和node 2代表单元输入,为完整网络中前两个单元的输出,剩余的 个节点,预测两个选择:1) 选择两个之前的节点作为当前节点输入 2) 选择用于两个输入的计算类型,共5种算子:identity, separable convolution with kernel size 3 × 3 and 5 × 5, and average pooling and max pooling with kernel size 3×3,然后将算子结果相加。对于 ,搜索过程如下:

  对于reduction cell,可以同样地使用上面的搜索空间生成: 1) 如图5采样一个计算图 2) 将所有计算的stride改为2。这样reduction cell就能将输入缩小为1/2,controller共预测 blocks
  最后计算下搜索空间的复杂度,对于node i ,troller选择前 个节点中的两个,然后选择五种算子的两种,共 种坑的单元。因为两种单元是独立的,所以搜索空间的大小最终为 ,对于 ,大约 种网络

  节点的计算做了一点修改,增加highway connections,例如 修改为 ,其中 , 为elementwise乘法。搜索到的结果如图6所示,有意思的是:1) 激活方法全部为tanh或ReLU 2) 结构可能为局部最优,随机替换节点的激活函数都会造成大幅的性能下降 3) 搜索的输出是6个node的平均,与mixture of contexts(MoC)类似

  单1080Ti训练了10小时,Penn Treebank上的结果如表1所示,PPL越低则性能越好,可以看到ENAS不准复杂度低,参数量也很少

  表2的第一块为最好的分类网络DenseNet的结构,第二块为ENAS设计整个卷积网络的结果(感觉这里不应有micro search space),第三块为设计单元的结果

  全网络搜索的最优结构如图7所示,达到4.23%错误率,比NAS的效果要好,大概单卡搜索7小时,相对NAS有50000x倍加速

  单元搜索的结构如图8所示,单卡搜索11.5小时, ,错误率为3.54%,加上CutOut增强后比NASNet要好。论文发现ENAS搜索的结构都是局部最优的,修改都会带来性能的降低,而ENAS不采样多个网络进行训练,这个给NAS带来很大性能的提升

  NAS是自动设计网络结构的重要方法,但需要耗费巨大的资源,导致不能广泛地应用,而论文提出的 Efficient Neural Architecture Search (ENAS),在搜索时对子网的参数进行共享,相对于NAS有超过1000x倍加速,单卡搜索不到半天,而且性能并没有降低,十分值得参考



2816. [ZJOI2012]网络LCT

Description

  有一个无向图G,每个点有个权值,每条边有一个颜色。这个无向图满足以下两个条件:

  1. 对于任意节点连出去的边中,相同颜色的边不超过两条。

  2. 图中不存在同色的环,同色的环指相同颜色的边构成的环。

   在这个图上,你要支持以下三种操作:

  1. 修改一个节点的权值。

  2. 修改一条边的颜色。

  3. 查询由颜色c的边构成的图中,所有可能在节点u到节点v之间的简单路径上的节点的权值的最大值。

Input

   输入文件network.in的第一行包含四个正整数N, M, C, K,其中N为节点个数,M为边数,C为边的颜色数,K为操作数。

   接下来N行,每行一个正整数vi,为节点i的权值。

   之后M行,每行三个正整数u, v, w,为一条连接节点u和节点v的边,颜色为w。满足1 ≤ u, v ≤ N,0 ≤ w < C,保证u ≠ v,且任意两个节点之间最多存在一条边(无论颜色)。

   最后K行,每行表示一个操作。每行的第一个整数k表示操作类型。

  1. k = 0为修改节点权值操作,之后两个正整数x和y,表示将节点x的权值vx修改为y。

  2. k = 1为修改边的颜色操作,之后三个正整数u, v和w,表示将连接节点u和节点v的边的颜色修改为颜色w。满足0 ≤ w < C。

  3. k = 2为查询操作,之后三个正整数c, u和v,表示查询所有可能在节点u到节点v之间的由颜色c构成的简单路径上的节点的权值的最大值。如果不存在u和v之间不存在由颜色c构成的路径,那么输出“-1”。

Output

   输出文件network.out包含若干行,每行输出一个对应的信息。

  1. 对于修改节点权值操作,不需要输出信息。

  2. 对于修改边的颜色操作,按以下几类输出:

    a) 若不存在连接节点u和节点v的边,输出“No such edge.”。

    b) 若修改后不满足条件1,不修改边的颜色,并输出“Error 1.”。

    c) 若修改后不满足条件2,不修改边的颜色,并输出“Error 2.”。

    d) 其他情况,成功修改边的颜色,并输出“Success.”。

    输出满足条件的第一条信息即可,即若同时满足b和c,则只需要输出“Error 1.”。

  1. 对于查询操作,直接输出一个整数。

Sample Input

4 5 2 7
1
2
3
4
1 2 0
1 3 1
2 3 0
2 4 1
3 4 0
2 0 1 4
1 1 2 1
1 4 3 1
2 0 1 4
1 2 3 1
0 2 5
2 1 1 4

Sample Output

4
Success.
Error 2.
-1
Error 1.
5
 
一个非常裸的lct……
按颜色维护多颗lct即可
让题意坑了一次……如果改颜色的边之前就是当前颜色的话就Success
在此感谢夫哥向我伸出的援手
 
  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdlib>
  4 #include<cstdio>
  5 #include<cctype>
  6 #include<algorithm>
  7 #define N (100000+100)
  8 using namespace std;
  9 struct node
 10 {
 11     int x,y,c;
 12 } E[N];
 13 int n,m,c,k,p=10000,x,y,z,opt,val;
 14 int Father[N],Son[N][2],Val[N],Max[N],Rev[N],Ind[N];
 15 bool cmp(node a,node b){return a.x<b.x || (a.x==b.x && a.y<b.y);}
 16 
 17 inline int read()
 18 {
 19     int X=0,w=0; char ch=0;
 20     while(!isdigit(ch)) {w|=ch==-;ch=getchar();}
 21     while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
 22     return w?-X:X;
 23 }
 24 
 25 int Get (int x){return Son[Father[x]][1]==x;}
 26 void Update(int x){Max[x]=max(Val[x],max(Max[Son[x][0]],Max[Son[x][1]]));}
 27 int Is_root(int x){return Son[Father[x]][0]!=x && Son[Father[x]][1]!=x;}
 28 
 29 void Rotate(int x)
 30 {
 31     int wh=Get(x);
 32     int fa=Father[x],fafa=Father[fa];
 33     if (!Is_root(fa)) Son[fafa][Son[fafa][1]==fa]=x;
 34     Father[fa]=x; Son[fa][wh]=Son[x][wh^1];
 35     if (Son[fa][wh]) Father[Son[fa][wh]]=fa;
 36     Father[x]=fafa;    Son[x][wh^1]=fa;
 37     Update(fa);    Update(x);
 38 }
 39 
 40 void Pushdown(int x)
 41 {
 42     if (Rev[x] && x)
 43     {
 44         if (Son[x][0]) Rev[Son[x][0]]^=1;
 45         if (Son[x][1]) Rev[Son[x][1]]^=1;
 46         swap(Son[x][0],Son[x][1]);
 47         Rev[x]=0;
 48     }
 49 }
 50 
 51 void Push(int x){if (!Is_root(x)) Push(Father[x]); Pushdown(x);}
 52 void Splay(int x)
 53 {
 54     Push(x);
 55     for (int fa; !Is_root(x); Rotate(x))
 56         if (!Is_root(fa=Father[x]))
 57             Rotate(Get(fa)==Get(x)?fa:x);
 58 }
 59 
 60 void Access(int x){for (int y=0; x; y=x,x=Father[x]) Splay(x),Son[x][1]=y,Update(x);}
 61 void Make_root(int x){Access(x); Splay(x); Rev[x]^=1;}
 62 int  Find_root(int x){Access(x); Splay(x); while (Son[x][0]) x=Son[x][0]; return x;}
 63 void Link(int x,int y){Make_root(x); Father[x]=y;}
 64 void Cut(int x,int y){Make_root(x); Access(y); Splay(y); Son[y][0]=Father[x]=0;}
 65 int  Query(int x,int y){Make_root(x); Access(y); Splay(y); return Max[y];}
 66 
 67 int getid(int x,int y)
 68 {
 69     int l=1,r=m;
 70     while (l<=r)
 71     {
 72         int mid=(l+r)/2;
 73         if (E[mid].x==x && E[mid].y==y) return mid;
 74         if (E[mid].x<x || (E[mid].x==x && E[mid].y<y))
 75             l=mid+1;
 76         else
 77             r=mid-1;
 78     }
 79     return 0;
 80 }
 81 
 82 int main()
 83 {
 84     n=read(); m=read(); c=read(); k=read();
 85     for (int i=1; i<=n; ++i)
 86     {
 87         x=read();
 88         for (int j=0; j<c; ++j)
 89             Val[i+j*p]=x;
 90     }
 91     for (int i=1; i<=m; ++i)
 92     {
 93         x=read(); y=read(); z=read();
 94         if (x>y) swap(x,y);
 95         E[i].x=x, E[i].y=y, E[i].c=z;
 96         Link(x+z*p,y+z*p);
 97         Ind[x+z*p]++;
 98         Ind[y+z*p]++;
 99     }
100     sort(E+1,E+m+1,cmp);
101     for (int i=1; i<=k; ++i)
102     {
103         opt=read();
104         switch (opt)
105         {
106             case 0:
107             {
108                 x=read(); val=read();
109                 for (int i=0; i<c; ++i)
110                 {
111                     Splay(x+i*p);
112                     Val[x+i*p]=val;
113                     Update(x+i*p);
114                 }
115                 break;
116             }
117             case 1:
118             {
119                 x=read(); y=read(); val=read();
120                 if (x>y) swap(x,y);
121                 int id=getid(x,y);
122                 if (id && E[id].c==val)
123                 {
124                     printf("Success.\n");
125                     break;
126                 }
127                 if (id==0)
128                 {
129                     printf("No such edge.\n");
130                     break;
131                 }
132                 if (Ind[x+val*p]>=2 || Ind[y+val*p]>=2)
133                 {
134                     printf("Error 1.\n");
135                     break;
136                 }
137                 if (Find_root(x+val*p)==Find_root(y+val*p))
138                 {
139                     printf("Error 2.\n");
140                     break;
141                 }
142                 Cut(x+E[id].c*p,y+E[id].c*p);
143                 Ind[x+E[id].c*p]--;
144                 Ind[y+E[id].c*p]--;
145                 Link(x+val*p,y+val*p);
146                 Ind[x+val*p]++;
147                 Ind[y+val*p]++;
148                 E[id].c=val;
149                 printf("Success.\n");
150                 break;
151             }
152             case 2:
153             {
154                 val=read(); x=read(); y=read();
155                 if (Find_root(x+val*p)!=Find_root(y+val*p))
156                 {
157                     printf("-1\n");
158                     break;
159                 }
160                 printf("%d\n",Query(x+val*p,y+val*p));
161                 break;
162             }
163         }
164     }
165 }

以上是关于ENAS:首个权值共享的神经网络搜索方法,千倍加速 ICML 2018的主要内容,如果未能解决你的问题,请参考以下文章

神经网络搜索Efficient Neural Architecture Search

AT2294 Eternal Average

神经网络AI加速器技术

中国已准备好启动首个央行数字货币,人民币国际化,指日可待!

CNN卷积神经网络 的学习记录一

霍夫曼树