Channel Pruning for Accelerating Very Deep Neural Networks

###########################################

补充阅读:LASSO回归,最小二乘法

###########################################

本文提出了一种新的通道剪枝方法来加速超深卷积神经网络,给出了一个训练好的CNN模型,通过基于LASSO回归的通道选择和最小二乘重建,提出了一种迭代两步算法来有效地剪枝每一层。进一步将该算法推广到多层多分支的情况。该方法减少了累积误差,增强了与各种体系结构的兼容性。我们修剪后的VGG-16速度提高了5倍,误差仅增加了0.3%,达到了最新的效果。更重要的是,我们的方法能够加速ResNet、exception等现代网络,在2倍加速下的精度损失分别只有1.4%、1.0%,具有重要意义。代码已公开1。 https://github.com/yihui-he/channel-pruning

1. Introduction

最近的CNN加速工作分为三类:优化实现(如FFT[48])、量化(如BinaryNet[8])和将CNN转换为紧凑CNN的结构化简化[22]。这项工作的重点是最后一项。

结构简化主要包括:张量因子分解[22]、稀疏连接[17]和通道剪枝[49]。张量因子分解将卷积层分解为几个有效层(图1(c))。然而,特征图宽度(通道数)不能减小,这使得很难分解现代网络所青睐的1x 1卷积层(例如GoogleNet[46]、ResNet[18]、Xception[7])。这种方法也会带来额外的计算开销。稀疏连接使神经元或通道之间的连接失效(图1(b))。

图1。加速CNNs的结构化简化方法:(a)具有3个conv层的网络。(b) 稀疏连接停用通道之间的某些连接。(c) 张量因式分解将卷积层分解成若干部分。(d) 通道修剪减少了每一层的通道数(本文的重点)。

稀疏卷积层虽然能达到较高的理论加速比,但其形状不规则,不利于实现。相反,如图1(d)所示,通道修剪直接减小特征映射的宽度,从而将网络缩小为较薄的网络。它在CPU和GPU上都是有效的,因为不需要特殊的实现。

修剪通道很简单,但很有挑战性,因为删除一个层中的通道可能会极大地改变下一层的输入。近年来,基于训练的通道剪枝工作[149]主要集中在训练过程中对权值施加稀疏约束,以自适应地确定超参数。然而,training from scratch是非常昂贵的,在ImageNet上对非常深的cnn的结果很少有报道。推理时间尝试[31,3]集中于分析个体体重的重要性。报告的加速比非常有限。

本文提出了一种利用通道间冗余度进行信道剪枝的新的推理时间方法。受feature
maps重构张量分解改进的启发[53],我们没有分析滤波器权重[22,31],而是充分利用了冗余的特征图,具体来说,给定一个训练过的CNN模型,通过最小化输出特征图上的重构误差来修剪每一层,如图2所示。

图2。加速卷积层的通道修剪。我们的目标是减少特征图B的通道数,同时最小化特征图C的重建误差。我们的优化算法(3.1节)在虚线框内执行,不涉及非线性。此图说明了为特征映射B修剪两个通道的情况。因此可以移除滤波器W的相应通道。此外,即使我们的算法没有直接优化,也可以去掉前一层中相应的滤波器。c, n:是特征图B和C的通道数,kh×kwk_h \times k_wkh​×kw​:卷积核大小

我们通过两个交替的步骤来解决这个最小化问题:通道选择和特征图重建。一步,我们找出最有代表性的通道,并根据LASSO regression对冗余通道进行剪枝。在另一步中,我们用线性最小二乘法重建剩余通道的输出。我们也可以采取两个步骤。此外,我们逐层逼近网络,并考虑累积误差。我们还讨论了剪枝多分支网络的方法(例如ResNet[18],Xception[7])。

对于VGG-16,我们实现了4倍的加速,前5位误差仅增加1.0%。结合张量分解,我们得到了5倍的加速度,但误差仅增加了0.3%,优于以前的状态,进一步将ResNet-50和exception-50速度提高了2倍,精度损失分别为1.4%和1.0%。

2. Related Work

在加速cnn方面已经做了大量的工作。其中许多可分为三类:优化实现[4]、量化[41]和结构化简化[22]。

基于优化实现的方法[36,48,27,4]使用特殊的卷积算法(如FFT[48])加速卷积。量化[8,41]降低了浮点计算复杂度。

稀疏连接消除了神经元之间的连接[17,33,29,15,14]。[52]基于权重大小修剪连接。[16] 可以将完全连接的层加速到50倍。但是,实际上,实际的加速可能与实现非常相关。

张量因子分解[22,28,13,24]将权重分解成若干个片段。[51,10,12]使用截断的SVD加速完全连接的层。[53]在feature map redundancy的驱动下,将图层分解为3×3和1×1组合。

通道修剪删除feature maps的冗余通道。有几种training-based的方法。[1,49,54]调整网络以提高准确性。对于LeNet[30]和AlexNet[26]的前几个conv层,Channel-wise SSL[49]达到高压缩比。[54]对于完全连接的层可以很好地工作。然而,基于训练的方法成本更高,而且对于大型数据集上的非常深的网络的有效性很少被利用。

如前几篇文章[2,40]所述,推理时间通道修剪是一项挑战。一些工作[45,35,19]侧重于模型大小压缩,主要操作完全连接的层。Data-free方法[31,3]的加速比结果(例如,5倍)尚未报告,需要长时间的再训练程序。[3] 通过100多个随机试验选择信道,但是在一个深度网络上评估每个试验需要很长时间,这使得在非常深的模型和大型数据集上工作是不可行的。[31]有时甚至比我们观察到的天真的解决方案更糟糕。

3. Approach

在这一节中,我们首先提出了一种针对单层的通道修剪算法,然后将这种方法推广到多层或整个模型。此外,我们还讨论了我们的方法在多分支网络中的变体。

3.1. Formulation

图2示出了我们针对单个卷积层的通道修剪算法。我们的目标是减少特征图B的通道数,同时保持特征图C的输出。一旦通道被剪除,我们就可以移除以这些通道为输入的滤波器的相应通道。此外,还可以移除产生这些通道的卷积核。很明显,通道修剪涉及到两个关键点。首先是通道选择,因为我们需要选择合适的通道组合来维护尽可能多的信息。二是重建。我们需要使用选定的通道重建以下特征映射。

基于此,我们提出了一个迭代的两步算法。一步,我们的目标是选择最具代表性的通道。由于穷举搜索是不可行的,即使是对微小的网络,我们提出了一个LASSO regression的方法,找出代表性的通道和剪除多余的。在另一步中,我们用线性最小二乘法重建剩余通道的输出。我们也可以采取两个步骤。

假设B层到C层的卷积核参数为W,其尺寸为n×c×kh×kwn \times c \times k_h \times k_wn×c×kh​×kw​,其中n为输出层C层特征图的通道数,c为输入层B层特征图的通道数,kh×kwk_h\times k_wkh​×kw​为卷积核尺寸,假设输入特征图为X,输出特征图为Y,设batch_size为N,则X尺寸为N×Hin×Win×cN\times Hin\times Win\times cN×Hin×Win×c,Y尺寸为N×Hout×Wout×nN\times Hout\times Wout \times nN×Hout×Wout×n。我们将通道数从c,裁剪到c’<c,在最小化重构误差的同时,我们将问题表述为:

首先β的0范数要小于等于c‘,也就是说β向量中非零值的数量要小于c’。

∥⋅∥F\Vert \cdot\Vert_F∥⋅∥F​ 是Frobenius范数,XiX_iXi​ 是从input volumes X的第i个通道中分割出的 N×khkwN\times k_hk_wN×kh​kw​ 矩阵,i = 1,…,c。WiW_iWi​ 是从W的第i个通道切出的n×khkwn\times k_hk_wn×kh​kw​ 滤波器权值 。β\betaβ 是长度为c的系数向量用于通道选择,βi\beta_iβi​第i通道的标量掩码,(即是否放弃整个频道)。注意,如果βi\beta_iβi​=0,XiX_iXi​将不再有用,这可以从feature map中安全地删除。WiW_iWi​也可以被移除。c′c'c′是是保留通道的数量,它是手动设置的,因为它可以从所需的加速比计算出来。对于整个模型的加速(即4.1.2节),给定整体的加速,我们首先为每一层分配加速比,然后计算每个c′c'c′。

Optimization

求解Eqn. 1中的这个l0l_0l0​最小化问题是np -困难的。因此,我们对l0l_0l0​正则化松弛到l1l_1l1​正则化。

λ\lambdaλ为惩罚系数。通过增大λ\lambdaλ,将有更多的零项在 β\betaβ 中,并且可以获得更高的加速比。另外 ∀i∥Wi∥F=1\forall_i\Vert W_i\Vert_F=1∀i​∥Wi​∥F​=1 是用来约束W的唯一解。现在我们用两种方法来解决这个问题。首先对于通道选择,固定W,解β\betaβ,第二,对于重构误差,固定β\betaβ,解W。

(i) The subproblem of .

在这种情况下,W是固定的。我们解决通道选择问题通过解 β\betaβ 。LASSO回归[47,5]可以解决这一问题,在模型选择中得到了广泛的应用。


其中Zi=XiWiTZ_i=X_iW_i^TZi​=Xi​WiT​,大小为N×nN\times nN×n,如果βi=0\beta_i=0βi​=0我们将忽略第i个通道。

(ii) The subproblem of W.

在这种情况下, β\betaβ 是固定的。我们利用所选通道来最小化重建误差,我们可以通过最小二乘法找到最佳解:

其中,X′=[β1X1β2X2...βiXi...βcXc]X'=[\beta_1X_1\ \beta_2X_2\ ...\ \beta_iX_i\ ...\ \ \beta_cX_c]X′=[β1​X1​ β2​X2​ ... βi​Xi​ ...  βc​Xc​] (大小为N×ckhkwN\times ck_hk_wN×ckh​kw​)。W′W'W′是reshaped的WWW,大小为n×ckhkwn\times ck_hk_wn×ckh​kw​,W′=[W1W2...Wi...Wc]W'=[W_1\ W_2\ ...\ W_i\ ...\ W_c]W′=[W1​ W2​ ... Wi​ ... Wc​]。得到结果W′W'W′后,它被重新塑形为WWW。然后我们分配βi←βi∥Wi∥F\beta_i\leftarrow\beta_i\Vert W_i\Vert_Fβi​←βi​∥Wi​∥F​,Wi←Wi/∥Wi∥FW_i\leftarrow W_i/\Vert W_i\Vert_FWi​←Wi​/∥Wi​∥F​。在 ∀i∥Wi∥F=1\forall_i\Vert W_i\Vert_F=1∀i​∥Wi​∥F​=1 条件下。

我们交替优化(i)和(ii)。在开始时,W是从训练的模型初始化的,λ=0\lambda=0λ=0,意味着没有惩罚,∥β∥0=c\Vert\beta\Vert_0=c∥β∥0​=c。我们慢慢增加λ\lambdaλ。没变化一次λ\lambdaλ,我们重复这两个步骤直到∥β∥0\Vert\beta\Vert_0∥β∥0​稳定。在∥β∥0≤c′\Vert\beta\Vert_0\le c'∥β∥0​≤c′满足后,我们获得最后的W from {βiWi}\{\beta_iW_i\}{βi​Wi​}。在实践中,我们发现这两个步骤的迭代非常耗时。所以我们应用(i)多次,直到满足∥β∥0≤c′\Vert\beta\Vert_0\le c'∥β∥0​≤c′,然后应用(ii)一次,以获得最终结果。从我们的观察来看,这个结果与两步迭代的结果相当,因此,在接下来的实验中,我们采用这个方法来提高效率。

讨论:最近的一些作品[49,1,17](hough training based)也引入l1范数或LASSO。然而,我们必须强调,我们使用不同的公式。他们中的许多人将稀疏正则化引入训练损失,而不是显式求解LASSO。其他的工作[1]解决LASSO,而在优化过程中不考feature maps或数据。由于这些差异,我们的方法可以在推理时应用。

3.2. Whole Model Pruning

受[53]启发,我们按顺序逐层应用我们的方法。对于每一层,我们从当前的输入特征映射中获取输入量,并从未修剪模型的输出feature map中获取输出量。这可以形式化为:

不同于Eqn。1,Y替换为Y′Y'Y′,Y′Y'Y′来自原始模型的feature map。因此,在连续修剪过程中,可以考虑累积误差。

3.3. Pruning MultiBranch Networks

上面讨论的整个模型剪枝对于LeNet[30]、AlexNet[26]和VGG网[44]这样的单分支网络来说已经足够了。然而,对于像GoogLeNet[46]和ResNet[18]这样的多分支网络来说,这是不够的。我们主要关注剪枝广泛使用的residual structure(例如ResNet[18],e Xception[7])。给定图3(左)中所示的residual block,输入分叉为shortcut分支和residual分支。

图3。残差块的多分支增强图解。左:原始残余块。右图:经过增强的剪枝残差图块,cxc_xcx​表示特征图宽度。采样第一卷积层的输入通道,以减小输入特征图的大宽度。对于最后一层,我们尝试直接近似Y1+Y2,而不是近似Y2。

在residual分支上,有几个卷积层(例如,3个卷积层,其空间大小为1x1,3x3;1x1,图3,左)。除第一层和最后一层之外的其他层可以像前面描述的那样修剪。对于第一层,挑战在于大的feature map映射宽度(对于ResNet,是其输出的4倍)不能很容易地修剪,因为它与shortcut共享。对于最后一层,由于shortcut方式上没有参数,因此很难恢复shortcut方式累积的错误。为了应对这些挑战,我们提出了以下几种方法变体。

Last layer of residual branch:

如图3所示,residual block的输出层由两个输入组成:来自shortcut和residual branch的特征映射Y1和Y2,我们的目标是恢复该块的Y1+Y2。这里,Y1,Y2是修剪之前的原始feature maps。Y2可近似为Eqn. 1.。但是,shortcut分支没有参数,因此无法直接恢复Y1。为了弥补这个错误,最后一层的优化目标从Y2改为Y1−Y1′+Y2Y_1-Y'_1+Y_2Y1​−Y1′​+Y2​,这并没有改变我们的优化。这里,Y1′Y'_1Y1′​是前几层剪除后的当前feature map。修剪时,应相应地从这两个枝条上取样。

First layer of residual branch:

如图3(左)所示,由于residual block的feature maps也与shortcut分支共享,因此不能对其进行修剪。在这种情况下,我们可以在第一次卷积之前进行feature maps采样以节省计算量,我们仍然使用我们的算法Eqn. 1。不同的是,我们在共享feature maps上对所选择的通道进行采样,以构造用于稍后卷积层的新输入,如图3(右)所示。此操作的计算成本可以忽略。更重要的是,在引入feature maps采样后,卷积仍然是规则的。

对于残差分支上的第一次卷积,另一种选择是按滤波器进行剪枝。由于无参数shortcut分支的输入通道不能被剪除,**我们将我们的Eqn. 1独立地应用于每个过滤器(每个过滤器选择其自己的代表性输入通道)。在单层加速下,滤波剪枝比原剪枝更精确。**从我们的实验来看,2xresnet-50(应用于每个剩余分支的第一层)在没有微调的情况下提高了0.5%的top-5精度,但是经过微调后,没有明显的改善,并且输出“不规则”卷积层,需要特殊的库实现支持。我们在下面的实验中不采用它。

4. Experiment

我们对流行的VGG网[44]、ResNet[18]、ImageNet上的exception[7]、CIFAR10[25]和PASCAL VOC 2007[11]进行了评估。

对于批量规范化[21],我们首先将其合并为卷积权重,这不会影响网络的输出。因此每个卷积层后面跟着ReLU[37]。我们使用Caffe[23]进行深度网络评估,使用scikit learn[39]进行求解器实现。对于通道剪枝,我们发现它提取5000个图像,and 10 samples per image就足够了,这也很高效的(VGG-162只需几分钟)。在ImageNet上,我们用单视图(single view)评估前5位的准确性。调整图像的大小,使较短的一侧为256。

The testing is on center crop of 224x224 pixels。通过微调,我们可以获得更好的性能。我们使用的批处理大小为128,学习速率为1e−51e^{-5}1e−5。我们对修剪过的模型进行了10个周期的微调(少于1/10的重复训练)。微调用的数据增强是random crop of 224 x 224 and mirror.

4.1. Experiments with VGG16

VGG-16[44]是一个16层的单分支卷积神经网络,有13个卷积层。它在识别、检测和分割等领域有着广泛的应用,VGG-16的Single view前5位精度为89.9%。

4.1.1 Single Layer Pruning

在这一小节中,我们使用我们在3.1节的算法中评估单层加速性能。为了更好的理解,我们将我们的算法与两种朴素的通道选择策略进行了比较。first k选择第first k通道。max response基于具有高绝对权重和的相应滤波器来选择通道[31]。为了公平的比较,我们得到了每个feature map所选择的索引,然后进行重建(perform reconstruction)(Sec. 3.1 (ii))。我们希望这能证明通道选择的重要性。性能是通过在没有微调的情况下修剪某一层后误差的增加来测量的,如图4所示。

**如预期的,误差随着加速比的增加而增加。在不同的卷积层中,在不同的加速比下,我们的方法始终优于其他方法。**出乎意料的是,有时最大响应(max response)比first k更差。我们认为,最大响应(max response)忽略了不同滤波器之间的相关性。绝对权重较大的滤波器可能具有很强的相关性。因此,基于滤波器权重的选择就没有意义了。特征地图上的相关性是值得利用的。结果表明,信道选择对重建误差有很大影响。因此,对信道进行剪枝是非常重要的。

还要注意的是,从浅层到深层通道修剪逐渐变得困难。这表明较浅的层具有更多的冗余,这与[53]一致。在整个模型加速过程中,我们可以更积极地修剪较浅的层。

4.1.2 Whole Model Pruning

如表1所示,显示了2x、4x、5x下的整个模型加速度结果。我们采用了Sec3.2中提出的整体模型剪枝。在上述单层实验的指导下,我们对较浅的层进行更具攻击性的修剪。浅层(conv1_x到conv3_x)和深层(conv4_x)的剩余通道比为1:1.5。conv5_x没有被删减,因为它们总共只贡献9%的计算量,并且没有冗余。

经过微调,我们可以达到2倍的速度,而不会失去准确性。在4倍加速下,我们只遭受1.0%的下跌。与单层分析一致,我们的方法优于先前的通道修剪方法(Li等人。[31])大幅度地。

这是因为我们充分利用了feature map中的通道冗余。与张量因子分解算法相比,本文的方法优于Jaderberg等人的方法。[22],没有微调。尽管比Asym更糟糕[53],但我们的组合模型通过Asym要优于它.3D(表2)。这可能表明,通道修剪比张量因子分解更具挑战性,因为在一个层中删除通道可能会显著改变下一层的输入。然而,信道修剪保持了原有的模型架构,不引入额外的层,并且GPU上的绝对加速比要高得多(表3)。

由于我们的方法利用了一个新的基数,我们进一步将我们的信道修剪与空间因子分解[22]和信道因子分解[53]结合起来。如表2所示,我们的3个基数加速(空间、信道因子分解和信道修剪,由3C表示)优于先前的技术状态。不对称。3D[53](空间和通道分解),将卷积层分解为三部分:1×3;3×1;1×1。

我们将空间因子分解、信道因子分解和信道剪枝按顺序逐层结合起来,对20个epoch的加速模型进行了微调,因为它们比原始模型深3倍。经过微调,我们的4x型号没有退化。显然,不同加速技术的结合比任何单一的技术都好。这表示模型在每个基数中都是冗余的。

4.1.3 Comparisons of Absolute Performance

我们进一步评估了加速度在GPU上的绝对性能。表3中的结果是在Caffe[23]、CUDA8[38]和cuDNN5[6]下获得的,在GPU(GeForce GTX TITAN X)上有一小批32个。结果平均从50次运行。张量分解方法将权值分解成过多的块,增加了系统开销,不能获得太大的绝对速度。虽然我们的方法也遇到了性能下降的问题,但它在GPU上的推广效果要好于其他方法。我们对张量因子分解的结果与以前的研究不同[53,22],可能是因为当前的库和硬件更喜欢单个大卷积而不是几个小卷积。

4.1.4 Comparisons with Training from Scratch

尽管从零开始训练一个紧凑的模型是很费时的(通常是120个epoches),但它值得将我们的方法与从零开始的对应方法进行比较。公平地说,我们评估了从头开始的对等网络和具有相同计算复杂度和相同架构的正常设置网络。

如表4所示,我们观察到白手起家的同行很难达到竞争性的精度。我们的模型优于白手起家的模型。我们的方法成功地挑选出信息渠道并构建高度紧凑的模型。我们可以得出这样一个结论:同样的模型很难从头得到。这与建筑设计研究[20,1]一致,即如果在较浅的层中有更多的通道,模型可以更容易地训练。然而,渠道修剪有利于浅层。

对于从头开始(统一格式),每层中的滤波器减少一半(例如,将conv1从64减少到32),我们可以观察到相同复杂度的正常设置网络也不能达到相同的精度。这巩固了我们的观点,即在训练时,网络中存在大量冗余。然而,冗余可以在推理时选择退出。这可能是推理时间加速方法优于基于训练的方法的一个优点。

1]一致,即如果在较浅的层中有更多的通道,模型可以更容易地训练。然而,渠道修剪有利于浅层。

对于从头开始(统一格式),每层中的滤波器减少一半(例如,将conv1从64减少到32),我们可以观察到相同复杂度的正常设置网络也不能达到相同的精度。这巩固了我们的观点,即在训练时,网络中存在大量冗余。然而,冗余可以在推理时选择退出。这可能是推理时间加速方法优于基于训练的方法的一个优点。

请注意,从头开始的模型和统一的模型之间有0.6%的差距,这表明模型开发还有空间。采用我们的方法比从头开始训练一个模型要快得多,即使对于更瘦的模型也是如此。进一步的研究可以缓解我们进行薄模型探索的方法。

Channel Pruning for Accelerating Very Deep Neural Networks相关推荐

  1. 【模型压缩】Channel Pruning for Accelerating Very Deep Neural Networks算法笔记

    转:https://blog.csdn.net/u014380165/article/details/79811779 论文:Channel Pruning for Accelerating Very ...

  2. 《Channel Pruning for Accelerating Very Deep Neural Networks》论文笔记

    1. 概述 这篇文章提出了一种基于LASSO回归的通道选择和最小二乘重构的迭代两步算法,有效地对每一层进行修剪.并进一步将其推广到多层和多分枝的场景下.论文中的方法能够减少累积误差并且提升对于不同结构 ...

  3. 深度学习(六十九)darknet 实现实验 Compressing Deep Neural Networks with Pruning, Trained Quantization and Huffma

    本文主要实验文献文献<Deep Compression: Compressing Deep Neural Networks with Pruning, Trained Quantization ...

  4. 剪枝综述论文阅读:Methods for Pruning Deep Neural Networks

    文章目录 一.概述 1.分类 2.评估 二.Magnitude based pruning 1.权重剪枝 2.彩票定理 3.特征图和过滤器剪枝 (1)基于通道方差的剪枝 Inbound pruning ...

  5. DEEP COMPRESSION: COMPRESSING DEEP NEURAL NETWORKS WITH PRUNING, TRAINED QUANTIZATION AND HUFFMAN

    深入理解DEEP COMPRESSION: COMPRESSING DEEP NEURAL NETWORKS WITH PRUNING, TRAINED QUANTIZATION AND HUFFMA ...

  6. deep compression:compressing deep neural networks with pruning,trained quantization and huffman codi

    deep compression:compressing deep neural networks with pruning,trained quantization and huffman codi ...

  7. 论文解读《Structured Pruning for Deep Neural Networks with Adaptive Pruning Rate Derivation Based on Con》

    论文:Structured Pruning for Deep Neural Networks with Adaptive Pruning Rate Derivation Based on Connec ...

  8. 3.Deep Neural Networks for YouTube Recommendations论文精细解读

    一.总述 今天分享的是Deep Neural Networks for Y ouTube Recommendations这篇论文的一些核心,这篇论文被称为推荐系统工程实践领域的一篇神文,每一个细节都值 ...

  9. 【翻译】Aggregated Residual Transformations for Deep Neural Networks

    Aggregated Residual Transformations for Deep Neural Networks 作者:Saining Xie1 Ross Girshick2 Piotr Do ...

最新文章

  1. 814. Binary Tree Pruning
  2. nginx内存池大小快内存_使用直接内存时可以更快
  3. 了解 SharePoint 2010 开发中的关键点
  4. Flask安装首页显示
  5. python3.6字典有序_为什么Python3.6字典变得有序了?
  6. Futter基础第15篇: 实现类似闲鱼App底部导航凸起按钮
  7. html5 怎么入门,初学HTML5 从入门到精通你需要懂得这些
  8. 使用ffmpeg推流拉流
  9. Ansible的安装和全面介绍
  10. AI时代,陪孩子玩什么游戏?| 前Google资深工程师实战心法
  11. 生活游记——泰国自由行
  12. nvidia-smi 查看显卡型号
  13. android的usb热插拔,Android M能让外部存储变成内部存储 支持U盘热插拔
  14. 一文读懂babel的使用
  15. python PIL库中的getpixel函数
  16. JavaScript HTML DOM EventListener
  17. 生日蛋糕-python实现
  18. 174-C语言入门(进制,补码,类型)
  19. Hadoop分布式集群安装
  20. SpringMVC与SiteMesh

热门文章

  1. 解决Ubuntu下中文输入法顿号打不出来的情况
  2. 中国眼科光学仪器行业产销需求与投资预测分析报告2022年
  3. Dlink路由器模拟器
  4. 关于FCN 论文中的 Shift-and-stitch 的详尽解释
  5. 神龙服务器和功夫熊猫什么关系?
  6. java 中单例_java 中单例实例
  7. @所有人:我写这份寄语是希望您们在IPFS的事业里面有所成就!
  8. 多设备智能语音交互—小爱协同唤醒
  9. html5隐藏%3ccenter%3e,HTML 5 代码
  10. html怪异盒模型,CSS-标准盒模型怪异盒模型