deeplearning.ai 总结 - C++实现 Adma优化
flyfish
编译环境
VC++2017
理论摘自《深度学习》
Adam一种学习率自适应的优化算法“Adam”这个名字派生自短语”adaptive moments”。
早期算法背景下,它也许最好被看作结合RMSProp和具有一些重要区别的动量的变种。
首先,在Adam中,动量直接并入了梯度一阶矩(指数加权)的估计。 将动量加入RMSProp最直观的方法是将动量应用于缩放后的梯度。
结合缩放的动量使用没有明确的理论动机。 其次,Adam包括偏置修正,修正从原点初始化的一阶矩(动量项)和(非中心的)二阶矩的估计。
RMSProp也采用了(非中心的)二阶矩估计,然而缺失了修正因子。 因此,不像Adam,RMSProp二阶矩估计可能在训练初期有很高的偏置。
Adam通常被认为对超参数的选择相当鲁棒,尽管学习率有时需要从建议的默认修改。
//论文地址
//http://arxiv.org/abs/1412.6980
#include <vector>
#include <unordered_map>
template <typename T, typename Func>
inline void for_i(T size, Func f) {
for (size_t i = 0; i < size; ++i) {
f(i);
}
}
typedef std::vector<double> Tensor2D;
class adam
{
public:
adam()
: alpha(double(0.001)),
b1(double(0.9)),
b2(double(0.999)),
b1_t(double(0.9)),
b2_t(double(0.999)),
eps(double(1e-8)) {}
void update(const Tensor2D &dW, Tensor2D &W)
{
Tensor2D &mt = get<0>(W);
Tensor2D &vt = get<1>(W);
for (auto it = dW.begin(); it != dW.end(); it++)
std::cout << *it << "\t";
std::cout << "dW \n";
for (auto it = mt.begin(); it != mt.end(); it++)
std::cout << *it << "\t";
std::cout << "mt \n";
for (auto it = vt.begin(); it != vt.end(); it++)
std::cout << *it << "\t";
std::cout << "vt \n";
for (const auto& n : E_) //地址的hash作为key
{
for (auto it = n.begin(); it != n.end(); it++)
{
std::cout << (*it).first << ": ";// << (*it).second << std::endl;
for (auto s = (*it).second.begin(); s != (*it).second.end(); s++)
{
std::cout << (*s);
}
std::cout << "complete \n";
}
}
for_i(W.size(), [&](size_t i) {
mt[i] = b1 * mt[i] + (double(1.0) - b1) * dW[i];//Momentum
vt[i] = b2 * vt[i] + (double(1.0) - b2) * dW[i] * dW[i];//RMSprop
double mt_hat = mt[i] / (double(1) - b1_t);
double vt_hat = vt[i] / (double(1.0) - b2_t);
// L2 norm
W[i] -= alpha * (mt_hat) / (std::sqrt(vt_hat) + eps);
});
b1_t *= b1;
b2_t *= b2;
}
//学习率或步长因子,它控制了权重的更新比率(如 0.001)。
//较大的值(如 0.3)在学习率更新前会有更快的初始学习,
//而较小的值(如 1.0E-5)会令训练收敛到更好的性能。
double alpha; // learning rate
//一阶矩估计的指数衰减率(如 0.9)。
double b1; //
//二阶矩估计的指数衰减率(如 0.999)。
//该超参数在稀疏梯度(如在 NLP 或计算机视觉任务中)中应该设置为接近 1 的数
double b2; //
double b1_t; // b1的平方
double b2_t; // b2的平方
private:
//该参数是非常小的数,其为了防止在实现中除以零(如 10E-8)。
double eps;
private:
template <int Index>
Tensor2D &get(const Tensor2D &key)
{
if (E_[Index][&key].empty())
E_[Index][&key].resize(key.size(), double());
return E_[Index][&key];
}
std::unordered_map<const Tensor2D *, Tensor2D> E_[2];
};