AutoDL论文解读(四):权值共享的搜索

自动化机器学习(AutoML)最近变得越来越火,是机器学习下个发展方向之一。其中的神经网络结构搜索(NAS)是其中重要的技术之一。人工设计网络需要丰富的经验和专业知识,神经网络有众多的超参数,导致其搜索空间巨大。NAS即是在此巨大的搜索空间里自动地找到最优的网络结构,实现深度学习的自动化。自2017年谷歌与MIT各自在ICLR上各自发表基于强化学习的NAS以来,已产出200多篇论文,仅2019年上半年就有100多篇论文。此系列文章将解读AutoDL领域的经典论文与方法,笔者也是刚接触这个领域,有理解错误的地方还请批评指正!
此系列的文章:

本次介绍的两篇论文是谷歌的《Efficient Neural Architecture Search via Parameter Sharing》和自动化所、图森的《You Only Search Once: Single Shot Neural Architecture Search via Direct Sparse Optimization》。一般自动生成的网络模型庞大而冗余,且搜索过程耗时耗力,这次的论文是用权值共享的办法来加速模型搜索。

一、Efficient Neural Architecture Search via Parameter Sharing

1、总览

这篇论文也是基于NAS的框架,对其进行了改进,称之为ENAS。NAS搜索到的图是一个更大的图的子图,也就是说NAS的搜索空间可以用一个有向无环图(DAG)来表示。下图给出了一个例子:
在这里插入图片描述
上面的图是整个搜索空间,红色的线连起来的子图是控制器发现的模型。ENAS的DAG是NAS搜索空间里所有可能子模型的叠加,图中的结点表示具体的计算操作,边表示信息流动。每个节点都有其自己的参数,这些参数在所有的子模型中都可以共享。其实ENAS要学习的就是节点之间的连线关系,通过不同的连线产生大量的模型。

2、生成RNN的cell

为了生成RNN的cell,作者使用一个有N个节点的DAG。ENAS的控制器是个RNN,它决定两个方面:(1)哪个边被激活;(2)DAG中的结点选择哪种操作。原NAS论文里的RNN生成,cell的结构被固定为二叉树结构,只决定树上每个节点的操作。而这里ENAS即设计节点操作也设计cell的结构。以下图为例说明控制器生成cell的过程:
在这里插入图片描述
假设有N=4个节点, x t x_{t} xt是cell的输入, h t − 1 h_{t-1} ht1是前一个时间步的输出。采样过程如下:

  • 在节点1:控制器采样一个激活函数。图中例子选择了tanh,那么节点1的计算为 h 1 = t a n h ( x t ⋅ W x + h t − 1 ⋅ W 1 h ) h_{1}={\rm tanh}(x_{t} \cdot W^{x}+h_{t-1} \cdot W_{1}^{h}) h1=tanh(xtWx+ht1W1h)
  • 在节点2:控制器采样之前节点的编号和激活函数。例子中选择了编号1和ReLU,则节点2的计算为 h 2 = R e L U ( h 1 ⋅ W 2 , 1 h ) h_{2}={\rm ReLU}(h_{1} \cdot W_{2,1}^{h}) h2=ReLU(h1W2,1h)
  • 在节点3:控制器再次采样之前节点的编号和激活函数。例子中选择了编号2和ReLU,则节点3的计算为: h 3 = R e L U ( h 2 ⋅ ( W 3 , 2 h ) ) h_{3}={\rm ReLU}(h_{2} \cdot (W_{3,2}^{h})) h3=ReLU(h2(W3,2h))
  • 在节点4:控制器再次采样之前节点的编号和激活函数。例子中选择了编号1和tanh,则节点3的计算为: h 4 = t a n h ( h 1 ⋅ ( W 4 , 2 h ) ) h_{4}={\rm tanh}(h_{1} \cdot (W_{4,2}^{h})) h4=tanh(h1(W4,2h))
  • 对于输出,将那么没有被选做其他任何节点输入的结点取平均。例子中,节点3和节点4没有被选为其他节点的输入,那么 h t = ( h 3 + h 4 ) / 2 h_{t}=(h_{3}+h_{4})/2 ht=(h3+h4)/2

在上面的例子中可以看出,对于每个节点对 j < l j<l j<l都有独立的参数矩阵 W l , j h W_{l,j}^{h} Wl,jh,通过选择前面节点的标号,控制器也选择了使用哪个参数矩阵,因此搜索空间里所有的cell都共享同样的参数集合。如果一个cell有N个节点,选择4种激活函数(tahn,ReLU,identity,sigmoid),那么搜索空间有 4 N × N ! 4^{N} \times N! 4N×N!种配置。

3、训练ENAS、导出网络结构

这里控制器LSTM有100个节点,通过softmax分类器进行采样。在ENAS中,有两类需要学习的参数:控制器LSTM的参数 θ \theta θ和子模型共享的参数 ω \omega ω。训练过程也包括两个阶段,第一个阶段用整个数据集训练 ω \omega ω,第二个阶段是训练 θ \theta θ,两个阶段轮流训练,具体细节如下:

  • 训练 ω \omega ω。这步中,控制器的策略 π ( m ; θ ) \pi(m;\theta) π(m;θ)被固定,用SGD去最小化交叉熵损失函数 E m   π [ L ( m i , ω ) ] {\Bbb E}_{m ~ \pi}[L(m_{i}, \omega)] Em π[L(mi,ω)] m m m是从 π ( m ; θ ) \pi (m; \theta) π(m;θ)中采样得到的一个模型。梯度使用蒙特卡洛估计:
    ∇ ω E m ∼ π ( m ; θ ) [ L ( m ; ω ) ] ≈ 1 M ∑ i = 1 M ∇ ω L ( m i , ω ) \nabla_{\omega} \mathbb{E}_{\mathbf{m} \sim \pi(\mathbf{m} ; \theta)}[\mathcal{L}(\mathbf{m} ; \omega)] \approx \frac{1}{M} \sum_{i=1}^{M} \nabla_{\omega} \mathcal{L}\left(\mathbf{m}_{i}, \omega\right) ωEmπ(m;θ)[L(m;ω)]M1i=1MωL(mi,ω)
    上式是对梯度的无偏估计,有较大的方差。但作者发现当 M = 1 M=1 M=1的时候效果就比较好,也就是说使用从 π ( m ; θ ) \pi (m; \theta) π(m;θ)采样到的任意一个模型 m m m上计算到的梯度就可以更新 ω \omega ω

  • 训练参数 θ \theta θ。在这步中固定 ω \omega ω来更新 θ \theta θ,以最大化期望奖励 E m   π ( m ; θ ) [ R ( m , ω ) ] {\Bbb E}_{m ~ \pi(m; \theta)}[\mathcal R(m, \omega)] Em π(m;θ)[R(m,ω)]。奖励 R ( m , ω ) \mathcal R(m,\omega) R(m,ω)是在验证集上计算得到的。

  • 导出网络结构。首先从训练的策略 π ( m , θ ) \pi (m, \theta) π(m,θ)采样几个模型,对于每个采样的模型,直接在验证集上计算奖励,然后选择奖励最高的模型从头训练。实际上这个过程会进行很多次,共享的权重也会在一段时间后更新。

4、设计卷积网络

在生成CNN时,控制器作出两个方面的决定:(1)连接前面的那个些节点;(2)使用哪种操作。这两个决策构造了一个卷积层。选择连接到前面的那些节点可以用来产生跨连接,在第 k k k层,前面最多有 k − 1 k-1 k1个可选的结点,这会有 2 k − 1 2^{k-1} 2k1种可能的决策。举例如下图:
在这里插入图片描述
上图中,在第 k = 4 k=4 k=4层,控制器选择了前面的第1、3个节点,所以第1、3层的输出在深度这个轴连接起来送到第4层。

对于可选的操作,一共有6种:3x3大小卷积、5x5大小卷积、3x3大小的可分离卷积、5x5大小可分离卷积、3x3的最大池化、3x3的平均池化。做这样的决策一共L次,可以得到一个L层的网络,在搜索空间里一共有 6 L × 2 L ( L − 1 ) / 2 6^{L} \times 2^{L(L-1)/2} 6L×2L(L1)/2种网络。

5、设计卷积cell

上述的搜索是从头搜索整个网络,也称作macro搜索。也可以只搜索卷积cell,然后将它们连接起来组成整个网络,这称为micro搜索。在每个cell里有B个节点,节点1和节点2作为cell的输入,是前两个cell的输出。对于剩下的B-2个节点,使用控制器取做两个决策:(1)选择两个前驱节点作为当前节点的输入;(2)选择两个操作作用在这两个前驱节点上。上述操作之后,将两个节点相加。可能的操作有5种:identity,3x3和5x5的可分离卷积,3x3的最大池化和平均池化。以B=4举例,如下图所示:
在这里插入图片描述

  • 对于节点1、2,其作为输入,控制器不对此作决策。用 h 1 h_{1} h1 h 2 h_{2} h2表示。
  • 在节点3,控制器选择两个前驱节点和两种操作。这里选择了节点2和节点2,选择的操作为5x5可分离卷积和identity,所以 h 3 = s e p _ c o n v _ 5 x 5 ( h 2 ) + i d ( h 2 ) h_{3}={\rm sep\_conv\_5x5}(h_{2})+{\rm id}(h_2) h3=sep_conv_5x5(h2)+id(h2)
  • 在节点4,控制器选择了节点3和节点1,选择的操作为3x3可分离卷积和3x3可分离卷积,所说 h 4 = s e p _ c o n v _ 3 x 3 ( h 3 ) + s e p _ c o n v _ 3 x 3 ( h 1 ) h_{4}={\rm sep\_conv\_3x3}(h_{3})+{\rm sep\_conv\_3x3}(h_{1}) h4=sep_conv_3x3(h3)+sep_conv_3x3(h1)
  • 最终除了 h 4 h_{4} h4其他节点都作为了其他节点的输入,所以 h 4 h_{4} h4作为cell的输出

从此搜索空间中还可以生成reduction cell,只需:(1)采样一个卷积cell;(2)应用步长为2的操作。卷积cell和reduction cell同时采样,则控制器RNN一共作 2 ( B − 2 ) 2(B-2) 2(B2)种决策。在节点 i ( 3 ≤ i ≤ B ) i(3 \leq i \leq B) i(3iB),控制器从前 i − 1 i-1 i1个节点任意选择2个节点,从5种操作中任意选择2个,所以总共有 ( 5 × ( B − 2 ) ) 2 (5 \times (B-2))^{2} (5×(B2))2种可能的cell,算上reduction cell,一共有 ( 5 × ( B − 2 ) ) 4 (5 \times (B-2))^{4} (5×(B2))4种可能的cell。

二、You Only Search Once: Single Shot Neural Architecture Search via Direct Sparse Optimization

1、总览

在这篇论文里,作者将NAS重新表述为从一个大型网络中剔除无用的连接,因此只需一个模型被训练。由于模型在训练阶段就直接被优化了,作者称之为直接稀疏优化NAS(Direct Sparse Optimization NAS, DSO-NAS)。作者进一步论证了这个稀疏正则化可以被改进的加速近端梯度(proximal gradient)方法优化,不需要任何的控制器、性能预测或者搜索空间松弛(搜索空间松弛是可微分方法的内容)。DSO-NAS也第一次论证了NAS可以直接在大型数据集上使用而没有块结构的共享。

2、Motivation

DSO-NAS通用是根据神经网络的结构空间可以用有向无环图DAG表示,在此空间的任何结构都是其子图。也就是说一个特定的结构可以从整个图中选择节点和边来得到。这里作者用完整的图来表示单个block的搜索空间,整个网络是由这些block用跨连接堆叠起来。

对于一个有T个节点的DAG,第 i i i个节点的输出 h ( i ) h^{(i)} h(i)可以通过变换所有前驱节点输出 h ( j ) , j < i h^{(j)},j<i h(j),j<i的和来得到:

h ( i ) = O ( i ) ( ∑ j = 1 i − 1 h ( j ) ) h^{(i)}={\mathcal O}^{(i)} \left(\sum_{j=1}^{i-1}h^{(j)} \right) h(i)=O(i)(j=1i1h(j))

下图展示了DAG里的一个特定的block,节点表示操作 O \mathcal O O,边表示信息流动。
在这里插入图片描述
上图中节点1和节点6是输入节点和输出节点,虚线表示没被连接起来的结点。那么节点5的输出是 h ( 5 ) = O ( 5 ) ( ∑ j = 4 4 h ( j ) ) h^{(5)}={\mathcal O} ^{(5)}\left(\sum_{j=4}^{4}h^{(j)} \right) h(5)=O(5)(j=44h(j)),也就是 h ( 5 ) = O ( 5 ) ( h ( 2 ) + h ( 4 ) ) h^{(5)}={\mathcal O} ^{(5)}(h^{(2)} + h^{(4)}) h(5)=O(5)(h(2)+h(4))

然后结构搜索问题可以看作边裁剪问题。在搜索过程中,我们移除没有用的边和节点,留下最重要的结构。这里作者对每个节点的输出都乘上一个乘子,上式变成:

h ( i ) = O ( i ) ( ∑ j = 1 i − 1 λ ( j ) ( i ) h ( j ) ) h^{(i)}={\mathcal O}^{(i)} \left(\sum_{j=1}^{i-1} \lambda_{(j)}^{(i)} h^{(j)} \right) h(i)=O(i)(j=1i1λ(j)(i)h(j))

λ ( j ) ( i ) \lambda_{(j)}^{(i)} λ(j)(i)是作用在从节点 j j j到节点 i i i的乘子,然后对这些乘子使用稀疏正则化,使其在搜索过程中有些为0。如果 λ ( j ) ( i ) \lambda_{(j)}^{(i)} λ(j)(i)为0,则相关的边和节点就被移除了。

3、搜索空间

DSO-NAS可以搜索block然后堆叠起来(micro-search),也可以搜索不带block的整个网络(macro-search)。

对于要搜索的block,设定里面有M个层级,每个层级包含N个不同的操作(也就是节点)。每个操作将其所有前层级的节点和block的输入连接起来,block的输出就是将block里的所有节点连接起来。对于每个连接,都乘上一个乘子 λ \lambda λ。对其使用稀疏正则化,优化之后移除 λ \lambda λ为0的连接和节点,得到最终的block结构。整个过程如下图所示:
在这里插入图片描述
图(a)是完整的DAG,图(b)是优化每个边的乘子 λ \lambda λ,图(c)是移除了没有用的边和节点后最终的block。

b b b个block里第 i i i层级的第 j j j个节点 h ( b , i , j ) h_{(b,i,j)} h(b,i,j)可以用如下公式表示:

h ( b , i , j ) = O ( b , i , j ) ( ∑ m = 1 i − 1 ∑ n = 1 N λ ( b , m , n ) ( i , j ) h ( b , m , n ) + λ ( b , 0 , 0 ) ( i , j ) O ( b − 1 ) ) h_{(b,i,j)}={\mathcal O}_{(b,i,j)} \left(\sum_{m=1}^{i-1} \sum_{n=1}^{N} \lambda_{(b,m,n)}^{(i,j)} h_{(b,m,n)} + \lambda_{(b,0,0)}^{(i,j)} O_{(b-1)} \right) h(b,i,j)=O(b,i,j)(m=1i1n=1Nλ(b,m,n)(i,j)h(b,m,n)+λ(b,0,0)(i,j)O(b1))

这里 h ( b , 0 , 0 ) = O ( b − 1 ) h_{(b,0,0)}=O_{(b-1)} h(b,0,0)=O(b1) h ( b , M + 1 , 0 ) = O ( b ) h_{(b, M+1,0)}=O_{(b)} h(b,M+1,0)=O(b)分别是第 b b b个block的输入节点和输出节点。第 m m m个层级有 ( m − 1 ) N + 1 (m-1)N+1 (m1)N+1个输入。第 b b b个block的输出 O ( b ) O_{(b)} O(b)是对所有连接到输出的结点应用reduction操作(1x1的卷积) R \mathcal R R得到的:

O ( b ) = R ( [ λ ( b , 1 , 1 ) ( M + 1 , 0 ) h ( b , 1 , 1 ) ] , λ ( b , 1 , 2 ) ( M + 1 , 0 ) h ( b , 1 , 2 ) , … , λ ( b , m , n ) ( M + 1 , 0 ) h ( b , m , n ) , … λ ( b , M , N ) ( M + 1 , 0 ) h ( b , M , N ) ) + O ( b − 1 ) , m ∈ [ 1 , M ] , n ∈ [ 1 , N ] \begin{aligned} O_{(b)}=\mathcal{R}\left(\left[\lambda_{(b, 1,1)}^{(M+1,0)} h_{(b, 1,1)}\right], \lambda_{(b, 1,2)}^{(M+1,0)} h_{(b, 1,2)}, \ldots, \lambda_{(b, m, n)}^{(M+1,0)} h_{(b, m, n)}, \ldots \lambda_{(b, M, N)}^{(M+1,0)} h_{(b, M, N)}\right) +O_{(b-1)}, m \in[1, M], n \in[1, N] \end{aligned} O(b)=R([λ(b,1,1)(M+1,0)h(b,1,1)],λ(b,1,2)(M+1,0)h(b,1,2),,λ(b,m,n)(M+1,0)h(b,m,n),λ(b,M,N)(M+1,0)h(b,M,N))+O(b1),m[1,M],n[1,N]

整个由block堆叠的完整网络如下图所示:
在这里插入图片描述
整个网络有 S S S个stage,每个stage有 B B B个卷积block。除了最后一个stage,每个stage的最后都有一个reduction block。这里作者尝试了两种搜索空间:(1) λ \lambda λ在所有block中都共享的搜索空间;(2)每个block中 λ \lambda λ都不同的搜索空间。

卷积操作遵循Conv-Bn-ReLU的顺序,使用下面几种操作:

  • 3x3的可分离卷积
  • 5x5的可分离卷积
  • 3x3的平均池化
  • 3x3的最大池化

对于reduction block,使用1x1和3x3大小的卷积核,步长为2。

搜索block的任务因此可以简化为学习每个边的 λ \lambda λ,可以用下式表示:

min ⁡ W , λ 1 K ∑ i = 1 K L ( y i , N e t ( x i , W , λ ) ) + δ ∥ W ∥ F 2 + γ ∥ λ ∥ 1 \min _{\mathbf{W}, \boldsymbol{\lambda}} \frac{1}{K} \sum_{i=1}^{K} \mathcal{L}\left(\mathbf{y}_{i}, N e t\left(\mathbf{x}_{i}, \mathbf{W}, \boldsymbol{\lambda}\right)\right)+\delta\|\mathbf{W}\|_{F}^{2}+\gamma\|\boldsymbol{\lambda}\|_{1} W,λminK1i=1KL(yi,Net(xi,W,λ))+δWF2+γλ1

x i \mathbf{x}_{i} xi y i \mathbf{y}_{i} yi是输入数据和标签, K K K表示训练样本的数目, W \mathbf{W} W表示网络的权重, δ \delta δ λ \lambda λ表示正则化权重。可以设 G ( λ ) = 1 K ∑ i = 1 K L ( y i , N e t ( x i , W , λ ) ) {\mathcal G}(\lambda)=\frac{1}{K} \sum_{i=1}^{K} \mathcal{L}\left(\mathbf{y}_{i},N e t\left(\mathbf{x}_{i}, \mathbf{W}, \boldsymbol{\lambda}\right)\right) G(λ)=K1i=1KL(yi,Net(xi,W,λ))

4、优化、训练

正则化参数 λ \lambda λ优化起来非常困难,尤其在有随机性的深度神经网络里,虽然启发式的阈值方法可行,但优化是不稳定的。不过使用稀疏结构选择(Sparse Structure Selection,SSS)可以解决这个问题,SSS通过修改理论上合理的优化方法加速近端梯度(Accelerated proximal Gradient,APG)方法,重写公式避免在计算梯度时重复地前向和反向计算:

z ( t ) = λ ( t − 1 ) − η ( t ) ∇ G ( λ ( t − 1 ) ) v ( t ) = S η ( t ) γ ( z ( t ) ) − λ ( t − 1 ) + μ ( t − 1 ) v ( t − 1 ) λ ( t ) = S η ( t ) γ ( z ( t ) ) + μ ( t ) v ( t ) \begin{array}{l}{\mathbf{z}_{(t)}=\boldsymbol{\lambda}_{(t-1)}-\eta_{(t)} \nabla \mathcal{G}\left(\boldsymbol{\lambda}_{(t-1)}\right)} \\ {\mathbf{v}_{(t)}=S_{\eta_{(t)} \gamma}\left(\mathbf{z}_{(t)}\right)-\boldsymbol{\lambda}_{(t-1)}+\mu_{(t-1)} \mathbf{v}_{(t-1)}} \\ {\boldsymbol{\lambda}_{(t)}=\mathcal{S}_{\eta_{(t)} \gamma}\left(\mathbf{z}_{(t)}\right)+\mu_{(t)} \mathbf{v}_{(t)}}\end{array} z(t)=λ(t1)η(t)G(λ(t1))v(t)=Sη(t)γ(z(t))λ(t1)+μ(t1)v(t1)λ(t)=Sη(t)γ(z(t))+μ(t)v(t)

t t t是迭代地轮数, S η ( t ) γ {\mathcal S}_{\eta_{(t)} \gamma} Sη(t)γ表示软阈值操作 S α ( z ) i = s i g n ( z i ) ( ∣ z i ∣ − α ) + {\mathcal S}_{\alpha}(\mathbf{z})_{i}={\rm sign}(z_{i})(|z_{i}|-\alpha)_{+} Sα(z)i=sign(zi)(ziα)+ η ( t ) {\eta}_{(t)} η(t)表示梯度步长, μ \mu μ是动量大小。这种方法称之为APG-NAG。 W \mathbf{W} W λ \mathbf{\lambda} λ使用NAG(Nesterov Accelerated Gradient )和APG-NAG同时被更新。然而APG-NAG不能直接用在这里,裁剪的搜索空间是有限的,会导致一定程度的过拟合。这里作者将训练数据分成两部分,一部分用来更新 W \mathbf{W} W,另一部分用来更新 λ \mathbf{\lambda} λ

整个方法包括三个阶段:

  • 训练DAG全部连接的网络,得到好的权重初始化
  • 从预训练的模型中搜索结构
  • 从头训练最终的网络

在前两个阶段,BN层的参数被固定为1,为了避免影响 λ \mathbf{\lambda} λ的学习。在第二步之后,作者通过一个全局宽度乘法器来调整每个操作中的滤波器数量,以满足计算预算。

参考文献

[1] Pham H, Guan M Y, Zoph B, et al. Efficient Neural Architecture Search via Parameter Sharing[J]. 2018.
[2] Zhang X , Huang Z , Wang N . You Only Search Once: Single Shot Neural Architecture Search via Direct Sparse Optimization[J]. 2018.
[3] 你想要的神经网络自动设计,谷歌大脑帮你实现了:用参数共享高效地搜索神经网络架构(ENAS)
[4] https://blog.csdn.net/weixin_34319817/article/details/89581856
[5] Huang Z , Wang N . Data-Driven Sparse Structure Selection for Deep Neural Networks[J]. 2017.
[6] 《深入理解AutoML和AutoDL》

在这里插入图片描述

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
您遇到的问题是autoDL在加载模型时出现了CUDA内存不足的错误。这种错误通常是由于GPU内存不足而导致的。根据引用内容,有几种可能的解决方法可以尝试。 首先,根据的引用,如果您怀疑是因为之前的进程没有完全结束导致的错误,您可以尝试使用指令来彻底终止进程。但是需要注意的是,这种操作可能会中断所有正在运行的GPU程序,因此需要谨慎使用。 另一种解决方法是根据的引用,将权重加载到CPU,并让模型在CPU上进行加载。这将避免与GPU相关的内存错误。使用这种方法,您可以尝试将模型的权重加载到CPU上,然后再进行加载操作。 您可以尝试使用以下代码来解决这个问题: ```python import torch # 将模型加载到CPU device = torch.device('cpu') model = YourModel().to(device) # 加载权重 weights = torch.load('your_weights.pth', map_location=device) model.load_state_dict(weights) # 进行模型加载操作 output = model(input) ``` 请将"YourModel"替换为您实际使用的模型类的名称,并将"your_weights.pth"替换为实际的权重文件路径。通过将模型加载到CPU上,并在加载操作时使用CPU设备,您可以避免与GPU内存相关的错误。 希望这些信息对您有所帮助!<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [完美解決pytorch载入预训练权重时出现的CUDA error: out of memory](https://blog.csdn.net/Davidietop/article/details/126803809)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值