超参数

超参数(Hyper-Parameter)是困扰神经网络训练的问题之一,因为这些参数不可通过常规方法学习获得。

神经网络经典五大超参数:

学习率(Leraning Rate)、权值初始化(Weight Initialization)、网络层数(Layers)

单层神经元数(Units)、正则惩罚项(Regularizer|Normalization)

这五大超参数使得神经网络更像是一门实践课,而不是理论课。

懂神经网络可能只要一小时,但是调神经网络可能要几天。

因此,后来Vapnik做SVM支持向量机的时候,通过巧妙的变换目标函数,避免传统神经网络的大部分超参数,

尤其是以自适应型的支持向量替代人工设置神经元,这使得SVM可以有效免于过拟合之灾。

传统对抗这些超参数的方法是经验规则(Rules of Thumb)。

这几年,随着深度学习的推进,全球神经网络研究者人数剧增,已经有大量研究组着手超参数优化问题:

★深度学习先锋的RBM就利用Pre-Traning自适应调出合适的权值初始化值。

★上个世纪末的LSTM长短期记忆网络,可视为“神经网络嵌套神经网络”,自适应动态优化层数。

★2010年Duchi et.al 则推出AdaGrad,自适应来调整学习率。

自适应调整学习率的方法,目前研究火热。一个经典之作,是 Matthew D. Zeiler 2012年在Google实习时,

提出的AdaDelta。

Matthew D. Zeiler亦是Hinton的亲传弟子之一,还是商业天才,大二时办了一个公司卖复习旧书。

Phd毕业之后,创办了Clarifai,估值五百万刀。参考[知乎专栏]

Clarifai的杰出成就是赢得了ImageNet 2013冠军,后来公布出CNN结构的时候,Caffe、Torch之类

的框架都仿真不出他在比赛时候跑的结果,应该是用了不少未公布的黑科技的。

再看他2012年提出的AdaDelta,肯定是用在的2013年的比赛当中,所以后来以普通方式才无法仿真的。

梯度更新

2.1 [一阶方法] 随机梯度

SGD(Stochastic Gradient Descent)是相对于BGD(Batch Gradient Descent)而生的。

BGD要求每次正反向传播,计算所有Examples的Error,这在大数据情况下是不现实的。

最初的使用的SGD,每次正反向传播,只计算一个Example,串行太明显,硬件利用率不高。

后续SGD衍生出Mini-Batch Gradient Descent,每次大概推进100个Example,介于BGD和SGD之间。

现在,SGD通常是指Mini-Batch方法,而不是早期单Example的方法。

一次梯度更新,可视为:

$x_{t+1}=x_{t}+\Delta x_{t} \quad where \quad  \Delta x_{t}=-\eta \cdot g_{t}$

$x$为参数,$t$为时序,$\Delta$为更新量,$\eta$为学习率,$g$为梯度

2.2 [二阶方法] 牛顿法

二阶牛顿法替换梯度更新量:

$\Delta x_{t}=H_{t}^{-1} \cdot g_{t}$

$H$为参数的二阶导矩阵,称为Hessian矩阵。

牛顿法,用Hessian矩阵替代人工设置的学习率,在梯度下降的时候,可以完美的找出下降方向,

不会陷入局部最小值当中,是理想的方法。

但是,求逆矩阵的时间复杂度近似$O(n^{3})$,计算代价太高,不适合大数据。

常规优化方法

3.1 启发式模拟退火

早期最常见的手段之一就是模拟退火。当然这和模拟退火算法没有半毛钱关系。

引入一个超参数(常数)的退火公式:

$\eta_{t}=\frac{\eta _{0}}{1+d\times t}$

$\eta _{0}$为初始学习率,$d$为衰减常数,通常为$10^{-3}$

模拟退火基于一个梯度法优化的事实:

在优化过程中,Weight逐渐变大,因而需要逐渐减小学习率,保证更新平稳。

3.2 动量法

中期以及现在最普及的就是引入动量因子:

$\Delta x_{t}=\rho \Delta x_{t-1}-\eta \cdot g_{t}$

$\rho$为动量因子,通常设为0.9

在更新中引入0.9这样的不平衡因子,使得:

★在下降初期,使用前一次的大比重下降方向,加速。

★在越过函数谷面时,异常的学习率,会使得两次更新方向基本相反,在原地”震荡“

此时,动量因子使得更新幅度减小,协助越过函数谷面。

★在下降中后期,函数面局部最小值所在的吸引盆数量较多,一旦陷进吸引盆当中,

$Gradient \rightarrow 0$,但是前后两次更新方向基本相同。

此时,动量因子使得更新幅度增大,协助跃出吸引盆。

3.3  AdaGrad

AdaGrad思路基本是借鉴L2 Regularizer,不过此时调节的不是$W$,而是$Gradient$:

$\Delta x_{t}=-\frac{\eta }{\sqrt{\sum_{\tau=1}^{t}(g_{\tau})^{2}}}\cdot g_{t}$

AdaGrad过程,是一个递推过程,每次从$\tau=1$,推到$\tau=t$,把沿路的$Gradient$的平方根,作为Regularizer。

分母作为Regularizer项的工作机制如下:

★训练前期,梯度较小,使得Regularizer项很大,放大梯度。[激励阶段]

★训练后期,梯度较大,使得Regularizer项很小,缩小梯度。[惩罚阶段]

另外,由于Regularizer是专门针对Gradient的,所以有利于解决Gradient Vanish/Expoloding问题。

所以在深度神经网络中使用会非常不错。

当然,AdaGrad本身有不少缺陷:

★初始化W影响初始化梯度,初始化W过大,会导致初始梯度被惩罚得很小。

此时可以人工加大$\eta$的值,但过大的$\eta$会使得Regularizer过于敏感,调节幅度很大。

★训练到中后期,递推路径上累加的梯度平方和越打越多,迅速使得$Gradinet$被惩罚逼近0,提前结束训练。

AdaDelta

AdaDelta基本思想是用一阶的方法,近似模拟二阶牛顿法。

4.1 矩阵对角线近似逆矩阵

1988年,[Becker&LeCun]提出一种用矩阵对角线元素来近似逆矩阵的方法:

$\Delta x_{t}=-\frac{1}{\left | diag(H_{t}) \right |+\mu }\cdot g_{t}$

$diag$指的是构造Hessian矩阵的对角矩阵,$\mu$是常数项,防止分母为0。

2012年,[Schaul&S. Zhang&LeCun]借鉴了AdaGrad的做法,提出了更精确的近似:

$\Delta x_{t}=-\frac{1}{\left | diag(H_{t}) \right |}\frac{E[g_{t}-w:t]^{2}}{E[g_{t}^{2}-w:t]}\cdot g_{t}$

$E[g_{t}-w:t]$指的是从当前t开始的前w个梯度状态的期望值。

$E[g_{t}^{2}-w:t]$指的是从当前t开始的前w个梯度状态的平方的期望值。

同样是基于Gradient的Regularizer,不过只取最近的w个状态,这样不会让梯度被惩罚至0。

4.2 窗口和近似概率期望

计算$E[g_{t}-w:t]$,需要存储前w个状态,比较麻烦。

AdaDelta使用了类似动量因子的平均方法:

$E[g^{2}]_{t}=\rho E[g^{2}]_{t-1}+(1-\rho )g_{t}^{2}$

当$\rho=0.5$时,这个式子就变成了求梯度平方和的平均数。

如果再求根的话,就变成了RMS(均方根):

$RMS[g]_{t}=\sqrt{E[g^{2}]_{t}+\epsilon }$

再把这个RMS作为Gradient的Regularizer:

$\Delta x_{t}=-\frac{\eta}{RMS[g]_{t}}\cdot g_{t}$

其中,$\epsilon$是防止分母爆0的常数。

这样,就有了一个改进版的AdaGrad。

该方法即Tieleman&Hinton的RMSProp,由于RMSProp和AdaDelta是同年出现的,

Matthew D. Zeiler并不知道这种改进的AdaGrad被祖师爷命名了。

RMSProp利用了二阶信息做了Gradient优化,在BatchNorm之后,对其需求不是很大。

但是没有根本实现自适应的学习率,依然需要线性搜索初始学习率,然后对其逐数量级下降。

另外,RMSProp的学习率数值与MomentumSGD差别甚大,需要重新线性搜索初始值。

注:$\epsilon$的建议取值为1,出处是Inception V3,不要参考V3的初始学习率。

4.3 Hessian方法与正确的更新单元

Zeiler用了两个反复近似的式子来说明,一阶方法到底在哪里输给了二阶方法。

首先,考虑SGD和动量法:

$\Delta x \propto g\propto \frac{\partial f}{\partial x} \propto \frac{1}{x}$

$\Delta x$可以正比到梯度$g$问题,再正比到一阶导数。而$log$一阶导又可正比于$\frac{1}{x}$。

再考虑二阶导Hessian矩阵法:

这里为了对比观察,使用了[Becker&LeCun 1988]的近似方法,让求逆矩阵近似于求对角阵的倒数:

$\Delta x \propto H^{-1}g\propto \frac{\frac{\partial f}{\partial x}}{\frac{\partial^{2}f}{\partial x^{2}}}\propto \frac{\frac{1}{x}}{\frac{1}{x}*\frac{1}{x}}\propto x$

$\Delta x$可以正比到Hessian逆矩阵$H^{-1}\cdot g$问题,再正比到二阶导数。而$log$二阶导又可正比于$x$。

可以看到,一阶方法最终正比于$\frac{1}{x}$,即与参数逆相关:参数逐渐变大的时候,梯度反而成倍缩小。

而二阶方法最终正比于$x$,即与参数正相关:参数逐渐变大的时候,梯度不受影响。

因此,Zeiler称Hessian方法得到了Correct Units(正确的更新单元)。

4.4 由Hessian方法推导出一阶近似Hessian方法

基于[Becker&LeCun 1988]的近似方法,有:

$\Delta x \approx  \frac{\frac{\partial f}{\partial x}}{\frac{\partial^{2}f}{\partial x^{2}}}$

进而又有:

$\frac{\frac{\partial f}{\partial x}}{\frac{\partial^{2}f}{\partial x^{2}}}=\frac{1}{\frac{\partial^{2}f}{\partial x^{2}}}\cdot \frac{\partial f}{\partial x}=\frac{1}{\frac{\partial^{2}f}{\partial x^{2}}}\cdot g_{t}$

简单收束变形一下, 然后用RMS来近似:

$\frac{1}{\frac{\partial^{2}f}{\partial x^{2}}}=\frac{\Delta x}{\frac{\partial f}{\partial x}}\approx -\frac{RMS[\Delta x]_{t-1}}{RMS[g]_{t}}$

最后,一阶完整近似式:

$\Delta x= -\frac{RMS[\Delta x]_{t-1}}{RMS[g]_{t}}\cdot g_t$

值得注意的是,使用了$RMS[\Delta x]_{t-1}$而不是$RMS[\Delta x]_{t}$,因为此时$\Delta x_{t}$还没算出来。

4.5 算法流程

$\quad\quad\quad\qquad\qquad\qquad ALGORITHM:ADADELTA\\\\\\\\Require:DecayRate \,\rho \, ,Constant \,\,\epsilon \\Require:InitialParam \,\,x_{1} \\1: \quad Initialize\,\,accumulation \,\,variables \,\,E[g^{2}]_{0}=E[\Delta x^{2}]_{0=0} \\2: \quad For \,\,t=1:T \,\, do \,\, Loop \,\, all \,\,updates \\3: \quad \quad Compute \,\,Gradients:g_{t} \\4: \quad \quad Accumulate \,\, Gradient:E[g^{2}]_{t}=\rho E[g^{2}]_{t-1}+(1-\rho )g_{t}^{2} \\5: \quad \quad Compute \,\,Update:\Delta x= -\frac{RMS[\Delta x]_{t-1}}{RMS[g]_{t}}\cdot g_t \\6: \quad \quad Accumulate \,\, Updates:E[\Delta x^{2}]_{t}=\rho E[\Delta x^{2}]_{t-1}+(1-\rho )\Delta x^{2} \\7: \quad \quad Apply \,\,Update:x_{t+1}=x_{t}+\Delta x_{t} \\8: \quad End \,\,For$

4.6 Theano实现

论文中,给出的两个超参数的合适实验值。

$\rho=0.95 \quad\quad \epsilon=1e-6$

Theano的实现在LSTM的教学部分,个人精简了一下:

defAdaDelta(tparams,grads):

p=0.95;e=1e-6

#init

delta_x2=[theano.shared(p.get_value() * floatX(0.)) for k, p intparams.iteritems()]

g2= [theano.shared(p.get_value() * floatX(0.)) for k, p intparams.iteritems()]#first to update g2

update_g2=[(g2, p * g2 + (1-p) * (g ** 2)) for g2, g inzip(g2, grads)]

fn_update_1=theano.function(inputs=[],updates=update_g2)#calc delta_x by RMS

delta_x=[-T.sqrt(delta_x2_last + e) / T.sqrt(g2_now + e) * g for g, delta_x2_last, g2_now inzip(grads,delta_x2,g2)]#then to update delta_x2 and param

update_delta_x2=[(delta_x2, p * delta_x2 + (1-p) * (delta_x ** 2)) for delta_x2, delta_x inzip(delta_x2, delta_x)]

update_param=[(param, param + delta) for param, delta inzip(tparams.values(), delta_x)]

fn_update_2=theano.function(inputs=[],updates=update_delta_x2+update_param)#return the update function of theano

return fn_update_1, fn_update_2

4.7 Dragon(Caffe)实现

默认代码以我的Dragon框架为准,对Caffe代码进行了重写。

//hpp文件

template

class AdaDeltaSolver :public SGDSolver < Dtype >{public:

AdaDeltaSolver(const SolverParameter& param) :SGDSolver(param) { }

AdaDeltaSolver(const string& param_file) :SGDSolver(param_file) { }protected:virtual void computeUpdateValue(intparam_id, Dtype rate);virtual voidapplyUpdate();

};//cpp文件

#include "gradient_solver.hpp"template

void AdaDeltaSolver::computeUpdateValue(intparam_id, Dtype rate){

Blob* net_param = net->getLearnableParams()[param_id];const Dtype lr_mult = net->getLrMults()[param_id];

Dtype eps=param.delta();

Dtype momntum=param.momentum();//adadelta will ignore base_lr

Dtype lr =lr_mult;const int count = net_param->count();switch(Dragon::get_mode()){caseDragon::CPU://history store for E[g^2]//update store for E[delta^2]//history=momentum*history + (1-momentum)*(diff^2)//1. compute diff^2 in temp

dragon_powx(count, net_param->cpu_diff(), Dtype(2), temp[param_id]->mutable_cpu_data());//2. compute history

dragon_cpu_axpby(count, Dtype(1) - momntum, temp[param_id]->cpu_data(),

momntum, history[param_id]->mutable_cpu_data());//3. compute RMS[history] as denominator in temp

dragon_set(count, eps, temp[param_id]->mutable_cpu_data());

dragon_axpy(count, Dtype(1), history[param_id]->cpu_data(),temp[param_id]->mutable_cpu_data());

dragon_powx(count, temp[param_id]->cpu_data(), Dtype(0.5), temp[param_id]->mutable_cpu_data());//4. compute diff/RMS[history] in diff

dragon_div(count, net_param->cpu_diff(), temp[param_id]->cpu_data(), net_param->mutable_cpu_diff());//5. compute RMS[update] as numerator in temp

dragon_set(count, eps, temp[param_id]->mutable_cpu_data());

dragon_axpy(count, Dtype(1), update[param_id]->cpu_data(), temp[param_id]->mutable_cpu_data());

dragon_powx(count, temp[param_id]->cpu_data(), Dtype(0.5), temp[param_id]->mutable_cpu_data());//6. compute diff*RMS[update] in diff

dragon_mul(count, net_param->cpu_diff(), temp[param_id]->cpu_data(), net_param->mutable_cpu_diff());//7. compute final diff^2 in temp

dragon_powx(count, net_param->cpu_diff(), Dtype(2), temp[param_id]->mutable_cpu_data());//8. compute update

dragon_cpu_axpby(count, (1 - momntum), temp[param_id]->cpu_data(),

momntum, update[param_id]->mutable_cpu_data());//9. apply learning rate

dragon_scal(count, lr, net_param->mutable_cpu_diff());break;caseDragon::GPU:

#ifndef CPU_ONLY

dragon_gpu_powx(count, net_param->gpu_diff(), Dtype(2), temp[param_id]->mutable_gpu_data());//2. compute history

dragon_gpu_axpby(count, Dtype(1) - momntum, temp[param_id]->gpu_data(),

momntum, history[param_id]->mutable_gpu_data());//3. compute RMS[history] as denominator in temp

dragon_gpu_set(count, eps, temp[param_id]->mutable_gpu_data());

dragon_gpu_axpy(count, Dtype(1), history[param_id]->gpu_data(), temp[param_id]->mutable_gpu_data());

dragon_gpu_powx(count, temp[param_id]->gpu_data(), Dtype(0.5), temp[param_id]->mutable_gpu_data());//4. compute diff/RMS[history] in diff

dragon_gpu_div(count, net_param->gpu_diff(), temp[param_id]->gpu_data(), net_param->mutable_gpu_diff());//5. compute RMS[update] as numerator in temp

dragon_gpu_set(count, eps, temp[param_id]->mutable_gpu_data());

dragon_gpu_axpy(count, Dtype(1), update[param_id]->gpu_data(), temp[param_id]->mutable_gpu_data());

dragon_gpu_powx(count, temp[param_id]->gpu_data(), Dtype(0.5), temp[param_id]->mutable_gpu_data());//6. compute diff*RMS[update] in diff

dragon_gpu_mul(count, net_param->gpu_diff(), temp[param_id]->gpu_data(), net_param->mutable_gpu_diff());//7. compute final diff^2 in temp

dragon_gpu_powx(count, net_param->gpu_diff(), Dtype(2), temp[param_id]->mutable_gpu_data());//8. compute update

dragon_gpu_axpby(count, Dtype(1) - momntum, temp[param_id]->gpu_data(),

momntum, update[param_id]->mutable_gpu_data());//9. apply learning rate

dragon_gpu_scal(count, lr, net_param->mutable_gpu_diff());#endif

break;default:LOG(FATAL) << "Unknown mode:" <<:get_mode>

}

}

template

void AdaDeltaSolver::applyUpdate(){

CHECK(Dragon::get_root_solver());

Dtype rate=getLearningRate();//AdaDelta do not need base lr

if (param.display() && iter%param.display() == 0)

LOG(INFO)<< "Iteration" << iter << ", lr = AdaDelta";

clipGradients();

vector*> net_params = net->getLearnableParams();for (int i = 0; i < net_params.size(); i++){

normalize(i);

regularize(i);

computeUpdateValue(i, rate);

net_params[i]->update();

}

}

INSTANTIATE_CLASS(AdaDeltaSolver);

View Code

AdaDelta的缺陷

局部最小值

从多个数据集情况来看,AdaDelta在训练初期和中期,具有非常不错的加速效果。

但是到训练后期,进入局部最小值雷区之后,AdaDelta就会反复在局部最小值附近抖动。

主要体现在验证集错误率上,脱离不了局部最小值吸引盆。

这时候,切换成动量SGD,如果把学习率降低一个量级,就会发现验证集正确率有2%~5%的提升,

这与常规使用动量SGD,是一样的。

之后再切换成AdaDelta,发现正确率又退回去了。

再切换成动量SGD,发现正确率又回来了。

---------------------------------------------------------------------

注:使用Batch Norm之后,这样从AdaDelta切到SGD会导致数值体系崩溃,原因未知。

---------------------------------------------------------------------

个人猜测,人工学习率的量级降低,给训练造成一个巨大的抖动,从一个局部最小值,

抖动到了另一个局部最小值,而AdaDelta的二阶近似计算,或者说所有二阶方法,

则不会产生这么大的抖动,所以很难从局部最小值中抖出来。

这给追求state of art的结果带来灾难,因为只要你一直用AdaDelta,肯定是与state of art无缘的。

基本上state of art的结果,最后都是SGD垂死挣扎抖出来的。

这也是SGD为什么至今在state of art的论文中没有废除的原因,人家丑,但是实在。

精度

eps的数值不是固定的。

1e-6在Caffe Cifar10上就显得过小了,1e-8比较适合。

这意味着不同数值比例体系,精度需要人工注意。

paper里高精度反而没低精度好,说明精度也有比较大抖动。

so,究竟什么样的精度是最好的呢?

————————————————————————————————————

2016.5.19 更新:

在FCNN-AlexNet里,1e-8在epoch1之后就会产生数值问题。

原因是sqrt(1e-8)*grad很大,这时候1e-10是比较好的。

另外,DensePrediction一定要做normalize,否则也有可能让AdaDelta的迭代步长计算出现数值问题。

该问题在FCNN-AlexNet进行到epoch5左右时候开始明显化。

caffe默认给的1e-10实际上要比paper里的1e-6要相对robust。

adadelta算法_自适应学习率调整:AdaDelta相关推荐

  1. 自适应学习率调整:AdaDelta

    自适应学习率调整:AdaDelta Reference:ADADELTA: An Adaptive Learning Rate Method 超参数 超参数(Hyper-Parameter)是困扰神经 ...

  2. adadelta算法_神经网络中常用的优化算法

    优化算法的目的:1. 跳出局部极值点或鞍点,寻找全局最小值:2.使训练过程更加稳定,更加容易收敛. 优化算法的改进无非两方面:1.方向--加动量,2.学习速率--加衰减 1.SGD 2.[Moment ...

  3. 从动力学角度看优化算法:自适应学习率算法

    作者丨苏剑林 单位丨广州火焰信息科技有限公司 研究方向丨NLP,神经网络 个人主页丨kexue.fm 在从动力学角度看优化算法SGD:一些小启示一文中,我们提出 SGD 优化算法跟常微分方程(ODE) ...

  4. adadelta算法_对C++用户比较友好的机器学习算法库

    由于疫情影响,这几天在家学习编程,整理了基于c++语言的机器学习算法库.目前大部分机器学习库都是面向pyhton语言的,尽管很python包的底层语言是c++,但c++用户使用起来很麻烦,这里整理了一 ...

  5. ffmpeg 缩放算法_图像尺寸调整算法介绍并手动实现近邻算法

    课程更新完毕,后面会根据同学的意见维护此课程 课程讲师:[夏曹俊] 课程基于opencv**版3.2讲解,全部使用**的c++接口,旧的接口会逐步被opencv抛弃,所以我们学习还是尽量学习新的接口, ...

  6. 不同算法的差异SGD/AdaGrad/AdaDelta/Adam/Nadam

    一.准备知识 指数加权平均 指数加权平均值又称指数加权移动平均值,局部平均值,移动平均值.加权平均这个概念都很熟悉,即根据各个元素所占权重计算平均值.指数加权平均中的指数表示各个元素所占权重呈指数分布 ...

  7. 深度学习梯度下降优化算法(AdaGrad、RMSProp、AdaDelta、Adam)(MXNet)

    在深度学习优化算法之动量法[公式推导](MXNet)中,动量法因为使用了指数加权移动平均,解决了自变量更新方向不一致的问题.动量法由于每个元素都使用了相同的学习率来做迭代,这也导致另外一个问题:如果x ...

  8. 《动手学深度学习》第三十三天---AdaGrad算法,RMSProp算法,AdaDelta算法,Adam算法

    回顾一下之前的优化算法: 在动量法中,我们用到指数加权移动平均来使得自变量的更新方向更加一致,从而降低发散的问题. (一)AdaGrad算法 AdaGrad算法会使用一个小批量随机梯度gt按元素平方的 ...

  9. 2学习率调整_学习率衰减

    之前我们的优化,主要是聚焦于对梯度下降运动方向的调整,而在参数迭代更新的过程中,除了梯度,还有一个重要的参数是学习率α,对于学习率的调整也是优化的一个重要方面. 01 - 学习率衰减 首先我们以一个例 ...

  10. 自适应学习率算法.基于阿米霍步长准则的线性回溯搜索算法

    解决在梯度下降等算法中,学习率步长的问题. 二分精确搜索法把梯度转化为和学习率的函数,问题转化为找学习率的问题, 然后寻找梯度最低的时候的学习率是多少,在此过程中用了二分法查找学习率.是一种精确搜索方 ...

最新文章

  1. ::selection 制作空心字
  2. lt;备份gt;10.9Sed and 计划任务
  3. jpa的批量修改_SpringDataJpa的批量 保存 修改 操作
  4. 爬虫笔记:pyquery详解
  5. 求三个数的最大最小值
  6. python的内存管理机制及调优手段_Python的内存管理机制及调优手段
  7. 东南亚再造天猫 Lazada品牌商城LazMall举办第二届品牌未来论坛
  8. 中的挂起是什么意思_仪表板亮奇怪指示灯,乌龟晒太阳是什么意思?老司机:不懂别上路...
  9. L3-003. 社交集群-PAT团体程序设计天梯赛GPLT(并查集)
  10. eclipse java开发实例_eclipse+webservice开发实例
  11. LInux 查看环境变量
  12. web开发性能优化---安全篇
  13. 【训练题55:尺取 + 高阶等差】Another String | HDU7015 | 杭电多校五 04题
  14. pdf转换器中文版下载
  15. 很傻很天真的问题: 什么是语法糖!
  16. Android检测网络状态监听网络变化
  17. XMind (2022)新版思维导图新增功能介绍
  18. Codeforces Round #700 (Div. 2) C. Searching Local Minimum(交互)
  19. Android签名打包报错:Conversion to Dalvik fromat failed with error 1
  20. 首次跌至发行价的Snap,要如何抵抗社交巨头Facebook的抄袭?

热门文章

  1. XueTr(PC Hunter) pro 注册分析
  2. gliffy confluen插件gliffy-confluence-plugin-5.1.ja破解
  3. 新版“北京通”今天上线 一个APP可办650件事儿
  4. mysql编写触发器语法_mysql触发器语法
  5. VMPlayer中Ubuntu 20.04鼠标在移动过程中消失的一种处理方法
  6. 多所985、211高校教授被通报!国自然发布处理决定!
  7. 【CS229 Deep Learning笔记】二. 介绍:分类问题
  8. 软件选择,iDreamPiano、freepiano、EveryonePiano
  9. Office 2010安装时缺少MSXML 6.10.1129.0
  10. 关于#include “stdafx.h“添加的错误问题