之前在https://blog.csdn.net/fengbingchun/article/details/75351323 介绍过梯度下降,常见的梯度下降有三种形式:BGD、SGD、MBGD,它们的不同之处在于我们使用多少数据来计算目标函数的梯度

大多数深度学习算法都涉及某种形式的优化。优化指的是改变x以最小化或最大化某个函数f(x)的任务。我们通常以最小化f(x)指代大多数最优化问题。我们把要最小化或最大化的函数称为目标函数(objective function)或准则(criterion)。当我们对其进行最小化时,我们也把它称为成本函数(cost function)、损失函数(loss function)或误差函数(error function)。

梯度下降是深度学习中一种常用的优化技术。梯度是函数的斜率。它衡量一个变量响应另一个变量的变化而变化的程度。在数学上,梯度下降是一个凸函数,其输出是输入的一组参数的偏导数。梯度越大,坡度越陡(the greater the gradient, the steeper the slope)。从初始值开始,迭代运行梯度下降以找到参数的最佳值,以找到给定成本函数的最小可能值。

梯度下降是一种优化算法,通常用于寻找深度学习算法中的权值及系数(weights or coefficients),如逻辑回归。它的工作原理是让模型对训练数据进行预测,并使用预测中的error来更新模型从而减少error(It works by having the model make predictions on training data and using the error on the predictions to update the model in such a way as to reduce the error)。

该算法的目标是找到使模型在训练数据集上的误差最小化的模型参数(e.g. coefficients or weights)。它通过对模型进行更改,使其沿着误差的梯度或斜率向下移动到最小误差值来实现这一点。这使该算法获得了"梯度下降"的名称。

梯度下降是深度学习中非常流行的优化算法。它的目标是搜索目标函数或成本函数(objective function or cost function)的全局最小值。这只有在目标函数是凸函数时才有可能,这间接意味着该函数将是碗形的。在非凸函数的情况下,梯度下降会找到最近的最小值,这个函数的最小值称为局部最小值。

梯度下降是一种一阶优化算法。这意味着在更新参数时它只考虑函数的一阶导数。我们的主要目标是在每次迭代中使梯度沿最陡斜率的方向行进,我们在与目标函数的梯度相反的方向上更新参数。

图解说明:假设只有weight没有bias。如果weight(w)的特定值的斜率>0,则表示我们在最优w*的右侧,在这种情况下,更新将是负数,并且w将开始接近最优w*。但是,如果weight(w)的特定值的斜率<0,则更新将为正值,并将当前值增加到w以收敛到w*的最佳值。以下截图来自于https://www.machinelearningman.com:重复该方法,直到成本函数收敛。

在https://blog.csdn.net/fengbingchun/article/details/79370310中有梯度下降应用于二分类的公式推导。

SGD(Stochastic Gradient Descent):随机梯度下降,通过每个样本迭代更新一次。有时提到SGD的时候,其实指的是MBGD

梯度下降是一种最小化目标函数的方法:θ为模型的参数,J(θ)为目标函数,以下截图来自https://arxiv.org/pdf/1609.04747.pdf

在https://blog.csdn.net/fengbingchun/article/details/79648664中也有对SGD的介绍。

在随机梯度下降中,随机选择几个样本而不是每次迭代整个数据集。在梯度下降中,有一个术语称为”batch”,它表示数据集中用于计算梯度每次迭代的样本数。在BGD中,batch被视为整个数据集。在SGD中,执行每次迭代计算梯度仅使用单个样本,即batch大小为1。被选择用于迭代的样本被随机打乱(randomly shuffled)。

SGD算法中的一个关键参数是学习率。在实践中,有必要随着时间的推移逐渐降低学习率。SGD中梯度估计引入的噪声源(m个训练样本的随机采样)并不会在极小点处消失。相比之下,当我们使用批量梯度下降到达极小点时,整个代价函数的真实梯度会变得很小,之后为0,因此批量梯度下降可以使用固定的学习率。

优点:

(1).频繁的更新可以立即让我们深入了解模型的性能和改进速度。

(2).这种形式的梯度下降可能是最容易理解和实现的,尤其对于初学者。

(3).增加的(increased)模型更新频率能够更快地学习某些问题。

(4).嘈杂的(noisy)的更新过程可以让模型避免局部最小值(如过早收敛)。

缺点:

(1).如此频繁地更新模型比其它形式的梯度下降耗费更高计算成本,在大型数据集上训练模型需要更长的时间。

(2).频繁的更新会导致一个嘈杂的(noisy)梯度信号,这可能会导致模型参数和模型误差跳来跳去(在训练时期具有更高的方差)。

(3).沿着误差梯度(error gradient)向下的嘈杂学习过程也可能使算法难以确定模型的误差最小值。

以上内容主要参考:

1. https://arxiv.org/pdf/1609.04747.pdf

2. https://machinelearningmastery.com/

3. https://www.machinelearningman.com

以下的测试代码以https://blog.csdn.net/fengbingchun/article/details/79346691 中逻辑回归实现的基础上进行调整:

logistic_regression2.hpp:

#ifndef FBC_SRC_NN_LOGISTIC_REGRESSION2_HPP_
#define FBC_SRC_NN_LOGISTIC_REGRESSION2_HPP_#include <cstdlib>
#include <ctime>
#include <vector>
#include <string>
#include <memory>namespace ANN {enum class ActivationFunction {Sigmoid // logistic sigmoid function
};enum class LossFunction {MSE // Mean Square Error
};enum class Optimization {BGD, // Batch Gradient DescentSGD, // Stochastic Gradient DescentMBGD // Mini-batch Gradient Descent
};struct Database {Database() = default;std::vector<std::vector<float>> samples; // training setstd::vector<int> labels; // ground truth labels
};class LogisticRegression2 { // two categories
public:LogisticRegression2(Optimization optim = Optimization::BGD, int batch_size = 1) : optim_(optim), batch_size_(batch_size) {}int init(std::unique_ptr<Database> data, int feature_length, float learning_rate = 0.00001, int epochs = 1000);int train(const std::string& model);int load_model(const std::string& model);float predict(const float* data, int feature_length) const; // y = 1/(1+exp(-(wx+b)))void set_error(float error) { error_ = error; }private:int store_model(const std::string& model) const;float calculate_z(const std::vector<float>& feature) const;  // z(i)=w^T*x(i)+bfloat calculate_cost_function() const;static int generate_random(int i) { return std::rand() % i; }float calculate_activation_function(float value) const;float calculate_loss_function() const;float calculate_loss_function_derivative() const;float calculate_loss_function_derivative(float predictive_value, float true_value) const;void calculate_gradient_descent(int start = 0, int end = 0);std::unique_ptr<Database> data_; // train data(images, labels)std::vector<int> random_shuffle_; // shuffle the training data at every epochstd::vector<float> o_; // predict valueint epochs_ = 100; // epochsint m_ = 0; // train samples numint feature_length_ = 0; // weights lengthfloat alpha_ = 0.00001; // learning ratestd::vector<float> w_; // weightsfloat b_ = 0.; // thresholdfloat error_ = 0.00001;int batch_size_ = 1;ActivationFunction activation_func_ = ActivationFunction::Sigmoid;LossFunction loss_func_ = LossFunction::MSE;Optimization optim_ = Optimization::BGD;
}; // class LogisticRegression2} // namespace ANN#endif // FBC_SRC_NN_LOGISTIC_REGRESSION2_HPP_

logistic_regression2.cpp:

#include "logistic_regression2.hpp"
#include <fstream>
#include <algorithm>
#include <random>
#include <cmath>
#include "common.hpp"namespace ANN {int LogisticRegression2::init(std::unique_ptr<Database> data, int feature_length, float learning_rate, int epochs)
{CHECK(data->samples.size() == data->labels.size());m_ = data->samples.size();if (m_ < 2) {fprintf(stderr, "logistic regression train samples num is too little: %d\n", m_);return -1;}if (learning_rate <= 0) {fprintf(stderr, "learning rate must be greater 0: %f\n", learning_rate);return -1;}if (epochs < 1) {fprintf(stderr, "number of epochs cannot be zero or a negative number: %d\n", epochs);return -1;}alpha_ = learning_rate;epochs_ = epochs;feature_length_ = feature_length;data_ = std::move(data);o_.resize(m_);return 0;
}int LogisticRegression2::train(const std::string& model)
{w_.resize(feature_length_, 0.);generator_real_random_number(w_.data(), feature_length_, -0.01f, 0.01f, true);generator_real_random_number(&b_, 1, -0.01f, 0.01f);if (optim_ == Optimization::BGD) {for (int iter = 0; iter < epochs_; ++iter) {calculate_gradient_descent();auto cost_value = calculate_cost_function();fprintf(stdout, "epochs: %d, cost function: %f\n", iter, cost_value);if (cost_value < error_) break;}} else {random_shuffle_.resize(data_->samples.size(), 0);for (int i = 0; i < data_->samples.size(); ++i)random_shuffle_[i] = i;float cost_value = 0.;for (int iter = 0; iter < epochs_; ++iter) {std::srand(unsigned(std::time(0)));std::random_shuffle(random_shuffle_.begin(), random_shuffle_.end(), generate_random);int loop = (m_ + batch_size_ - 1) / batch_size_;for (int i = 0; i < loop; ++i) {int start = i * batch_size_;int end = start + batch_size_ > m_ ? m_ : start + batch_size_;calculate_gradient_descent(start, end);for (int i = 0; i < m_; ++i)o_[i] = calculate_activation_function(calculate_z(data_->samples[i]));cost_value = calculate_cost_function();fprintf(stdout, "epochs: %d, loop: %d, cost function: %f\n", iter, i, cost_value);if (cost_value < error_) break;}if (cost_value < error_) break;}}CHECK(store_model(model) == 0);return 0;
}int LogisticRegression2::load_model(const std::string& model)
{std::ifstream file;file.open(model.c_str(), std::ios::binary);if (!file.is_open()) {fprintf(stderr, "open file fail: %s\n", model.c_str());return -1;}int length{ 0 };file.read((char*)&length, sizeof(length));w_.resize(length);feature_length_ = length;file.read((char*)w_.data(), sizeof(float)*w_.size());file.read((char*)&b_, sizeof(float));file.close();return 0;
}float LogisticRegression2::predict(const float* data, int feature_length) const
{CHECK(feature_length == feature_length_);float value{0.};for (int t = 0; t < feature_length_; ++t) {value += data[t] * w_[t];}value += b_;return (calculate_activation_function(value));
}int LogisticRegression2::store_model(const std::string& model) const
{std::ofstream file;file.open(model.c_str(), std::ios::binary);if (!file.is_open()) {fprintf(stderr, "open file fail: %s\n", model.c_str());return -1;}int length = w_.size();file.write((char*)&length, sizeof(length));file.write((char*)w_.data(), sizeof(float) * w_.size());file.write((char*)&b_, sizeof(float));file.close();return 0;
}float LogisticRegression2::calculate_z(const std::vector<float>& feature) const
{float z{0.};for (int i = 0; i < feature_length_; ++i) {z += w_[i] * feature[i];}z += b_;return z;
}float LogisticRegression2::calculate_cost_function() const
{/*// J+=-1/m([y(i)*loga(i)+(1-y(i))*log(1-a(i))])// Note: log0 is not definedfloat J{0.};for (int i = 0; i < m_; ++i)J += -(data_->labels[i] * std::log(o_[i]) + (1 - labels[i]) * std::log(1 - o_[i]) );return J/m_;*/float J{0.};for (int i = 0; i < m_; ++i)J += 1./2*std::pow(data_->labels[i] - o_[i], 2);return J/m_;
}float LogisticRegression2::calculate_activation_function(float value) const
{switch (activation_func_) {case ActivationFunction::Sigmoid:default: // Sigmoidreturn (1. / (1. + std::exp(-value))); // y = 1/(1+exp(-value))}
}float LogisticRegression2::calculate_loss_function() const
{switch (loss_func_) {case LossFunction::MSE:default: // MSEfloat value = 0.;for (int i = 0; i < m_; ++i) {value += 1/2.*std::pow(data_->labels[i] - o_[i], 2);}return value/m_;}
}float LogisticRegression2::calculate_loss_function_derivative() const
{switch (loss_func_) {case LossFunction::MSE:default: // MSEfloat value = 0.;for (int i = 0; i < m_; ++i) {value += o_[i] - data_->labels[i];}return value/m_;}
}float LogisticRegression2::calculate_loss_function_derivative(float predictive_value, float true_value) const
{switch (loss_func_) {case LossFunction::MSE:default: // MSEreturn (predictive_value - true_value);}
}void LogisticRegression2::calculate_gradient_descent(int start, int end)
{float db = 0.;std::vector<float> dw(feature_length_, 0.);switch (optim_) {case Optimization::SGD:case Optimization::MBGD: {int len = end - start;std::vector<float> z(len, 0), dz(len, 0);for (int i = start, x = 0; i < end; ++i, ++x) {z[x] = calculate_z(data_->samples[random_shuffle_[i]]);dz[x] = calculate_loss_function_derivative(calculate_activation_function(z[x]), data_->labels[random_shuffle_[i]]);for (int j = 0; j < feature_length_; ++j) {dw[j] += data_->samples[random_shuffle_[i]][j] * dz[x]; // dw(i)+=x(i)(j)*dz(i)}db += dz[x]; // db+=dz(i)}for (int j = 0; j < feature_length_; ++j) {dw[j] /= len;w_[j] -= alpha_ * dw[j];}b_ -= alpha_ * (db / len);}break;case Optimization::BGD:default: // BGDstd::vector<float> z(m_, 0), dz(m_, 0);for (int i = 0; i < m_; ++i) {z[i] = calculate_z(data_->samples[i]);o_[i] = calculate_activation_function(z[i]);dz[i] = calculate_loss_function_derivative(o_[i], data_->labels[i]);for (int j = 0; j < feature_length_; ++j) {dw[j] += data_->samples[i][j] * dz[i]; // dw(i)+=x(i)(j)*dz(i)}db += dz[i]; // db+=dz(i)}for (int j = 0; j < feature_length_; ++j) {dw[j] /= m_;w_[j] -= alpha_ * dw[j];}b_ -= alpha_ * (db / m_);}
}} // namespace ANN

test_logistic_regression2_gradient_descent:以MNIST为数据集,取0和1,在训练时取训练集各5000张,预测时取测试集各900张

int test_logistic_regression2_gradient_descent()
{fprintf(stdout,"Warning: first generate test images: execute demo/DatasetToImage/DatasetToImage: MNISTtoImage\n");fprintf(stdout, "load train images ...\n");
#ifdef _MSC_VERconst std::vector<std::string> image_path{ "E:/GitCode/NN_Test/data/tmp/MNIST/train_images/", "E:/GitCode/NN_Test/data/tmp/MNIST/test_images/"};const std::string model{ "E:/GitCode/NN_Test/data/logistic_regression2.model" };
#elseconst std::vector<std::string> image_path{ "data/tmp/MNIST/train_images/", "data/tmp/MNIST/test_images/"};const std::string model{ "data/logistic_regression2.model" };
#endifconst int image_size = 28*28;const int samples_single_class_num = 5000;auto data1 = std::make_unique<ANN::Database>();data1->samples.resize(samples_single_class_num*2);data1->labels.resize(samples_single_class_num*2);if (read_images(image_path[0], samples_single_class_num, image_size, data1) == -1) return -1;fprintf(stdout, "start train ...\n");auto start = std::chrono::steady_clock::now();//ANN::LogisticRegression2 lr(ANN::Optimization::BGD, samples_single_class_num * 2); // Batch Gradient Descent, epochs = 10000, correct rete: 0.997778ANN::LogisticRegression2 lr(ANN::Optimization::SGD, 1); // Stochastic Gradient Descent,  epochs = 5, correct rete: 0.998889lr.set_error(0.0002);int ret = lr.init(std::move(data1), image_size, 0.00001, 5);if (ret != 0) {fprintf(stderr, "logistic regression init fail: %d\n", ret);return -1;}ret = lr.train(model);if (ret != 0) {fprintf(stderr, "logistic regression train fail: %d\n", ret);return -1;}auto end = std::chrono::steady_clock::now();fprintf(stdout, "train elapsed time: %d seconds\n", std::chrono::duration_cast<std::chrono::seconds>(end - start).count());fprintf(stdout, "start predict ...\n");const int test_single_class_num = 900;const std::vector<std::string> prefix_name {"0_", "1_"};ANN::LogisticRegression2 lr2;lr2.load_model(model);int count = 0;for (int i = 1; i <= test_single_class_num; ++i) {for (const auto& prefix : prefix_name) {std::string name = std::to_string(i);if (i < 10) {name = "0000" + name;} else if (i < 100) {name = "000" + name;} else if (i < 1000) {name = "00" + name;}name = image_path[1] + prefix + name + ".jpg";cv::Mat mat = cv::imread(name, 0);if (mat.empty()) {fprintf(stderr, "read image fail: %s\n", name.c_str());return -1;}if (mat.cols * mat.rows != image_size || mat.channels() != 1) {fprintf(stderr, "image size fail: width: %d, height: %d, channels: %d\n", mat.cols, mat.rows, mat.channels());return -1;}mat.convertTo(mat, CV_32F);float probability = lr2.predict((float*)mat.data, image_size);int label = prefix == "0_" ? 0 : 1;if ((probability > 0.5 &&  label == 1) || (probability < 0.5 && label == 0)) ++count;}}float correct_rate = count / (test_single_class_num * 2.);fprintf(stdout, "correct rate: %f\n", correct_rate);return 0;
}

执行结果如下:训练时,SGD成本函数error值并不向BGD一样逐渐减少,偶尔会波动,但是总体上还是逐渐减少;预测准确率为99.94%

GitHub: https://github.com/fengbingchun/NN_Test

深度学习中的优化算法之SGD相关推荐

  1. 深度学习中的优化算法串讲

    Datawhale原创 作者:谢文睿,Datawhale成员 寄语:优化算法是一个超参数,一个优化算法不是适合所有损失函数的,没有哪个优化算法是绝对的好或绝对的坏,是要根据损失函数判断的 本文对深度学 ...

  2. 深度学习中的优化算法之Adam

    之前在https://blog.csdn.net/fengbingchun/article/details/124909910 介绍过深度学习中的优化算法Adadelta,这里介绍下深度学习的另一种优 ...

  3. 深度学习中的优化算法之RMSProp

    之前在https://blog.csdn.net/fengbingchun/article/details/124766283 中介绍过深度学习中的优化算法AdaGrad,这里介绍下深度学习的另一种优 ...

  4. 深度学习中的优化算法之MBGD

    之前在https://blog.csdn.net/fengbingchun/article/details/75351323 介绍过梯度下降,常见的梯度下降有三种形式:BGD.SGD.MBGD,它们的 ...

  5. 深度学习中的优化算法之BGD

    之前在https://blog.csdn.net/fengbingchun/article/details/75351323 介绍过梯度下降,常见的梯度下降有三种形式:BGD.SGD.MBGD,它们的 ...

  6. 深度学习中的优化算法与实现

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 GiantPandaCV导语:这篇文章的内容主要是参考 沐神的mxnet/gluon视频中,Aston ...

  7. 深度学习中的优化算法之带Momentum的SGD

    之前在https://blog.csdn.net/fengbingchun/article/details/123955067介绍过SGD(Mini-Batch Gradient Descent(MB ...

  8. 深度学习中的优化算法之NAG

    之前在https://blog.csdn.net/fengbingchun/article/details/124648766 介绍过Momentum SGD,这里介绍下深度学习的另一种优化算法NAG ...

  9. 深度学习中的优化算法之AdaGrad

          之前在https://blog.csdn.net/fengbingchun/article/details/123955067 介绍过SGD(Mini-Batch Gradient Des ...

  10. 2.2)深度学习笔记:优化算法

    目录 1)Mini-batch gradient descent(重点) 2)Understanding mini-batch gradient descent 3)Exponentially wei ...

最新文章

  1. Java数据结构2:堆排序思考
  2. python数组操作加法_Numpy数组索引和/或加法似乎是
  3. java.vm.info_深入理解java虚拟机
  4. linux 终端 画圆,16个圆桌面Linux用户必须要知道的Shell命令
  5. nslookup命令详解【转】
  6. python组合数据类型有哪些_Python学习之组合数据类型
  7. python如何导入turtle_centos7里python导入turtle的问题
  8. 一款非常好看的雷姆背景的时间单页(附雷姆图片)
  9. c语言编写贪吃蛇难点解析,刚学C语言,想写一个贪吃蛇的代码
  10. GO语言学习之路20
  11. 最新修复版efucms聚合小说漫画动漫听书分销系统源码(附安装教程)
  12. 云算子矩阵计算机,《CASIOfx-5800P矩阵编程计算器测量程序集锦梁宝禄.pdf》-支持高清全文免费浏览-max文档...
  13. 嵌入式-----产品手册----塔吊黑匣子电气安装培训
  14. 算法练习 - 五笔编码
  15. 麻将编程c语言,C语言麻将算法(胡牌算法)
  16. 服务器硬盘和普通硬盘驱动安装,固态硬盘与硬盘驱动器的区别及怎样选择
  17. 常用的机器学习算法(使用 Python 和 R 代码)
  18. [18调剂]华中师范大学伍伦贡联合研究院接收硕士研究生调剂的通知
  19. 简洁明了的刘海屏适配方案
  20. 开发一个简单网站--第三章:相知

热门文章

  1. 各种数值积分方法总结(从梯形积分、辛普森积分到龙贝格积分、自适应积分、高斯积分等)
  2. php 判断 小米 手机浏览器,网信办宣布整治手机浏览器 华为、QQ、小米等先查
  3. mysql的应用领域_面向应用领域的数据库新技术汇总(干货)
  4. 思维的基本过程与解决问题
  5. gradle-4.10.2-all等gradle下载
  6. 光环PMP 二模错题知识点
  7. 计算机中文无敌版,与电脑下象棋无敌版
  8. Python语言概述
  9. 【5G核心网】 3GPP TS 系列解读
  10. linux 编译安装libpng,交叉编译libpng以及zlib开源库