<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      最小二乘問題詳解6:梯度下降法

      1. 引言

      在之前的兩篇文章《最小二乘問題詳解4:非線性最小二乘》《最小二乘問題詳解5:非線性最小二乘求解實例》中,筆者介紹了非線性最小二乘問題,并使用Gauss-Newton方法來進行求解。不過,求解非線性最小二乘問題還有另外一種方法——梯度下降法。

      2. 背景

      梯度下降法在人工智能的機器學習中使用的非常多,因為機器學習的訓練過程通常被形式化為經驗風險最小化問題(Empirical Risk Minimization, ERM):即在訓練數據上最小化損失函數。而最小二乘問題其實也是經驗風險最小化問題的一種,甚至機器學習的某些任務(比如回歸)本身就是最小二乘問題。經驗風險最小化問題是一種通用的函數擬合框架,不過損失函數有所不同,通常使用梯度下降法來進行求解。

      那么為什么機器學習中使用梯度下降法來求解,而計算機視覺(SLAM、SfM、相機標定、BA)中使用Gauss-Newton/Levenberg-Marquardt來進行求解呢?這是因為機器學習的問題可以只用關心局部的“好解”,而不用像計算機視覺問題那樣需要求解全局的“精確最小值”;另外,機器學習問題規模巨大、結構復雜,使用梯度下降法要簡單、健壯、高效的多。

      3. 求解

      接下來就來介紹一下使用梯度下降法求解非線性最小二乘問題。還是先看非線性最小二乘問題的定義:

      \[\min_{\theta} S(\theta) = \ \mathbf{r}(\theta)\ ^2 = \sum_{i=1}^m r_i(\theta)^2 \]

      其中:
      \(\theta \in \mathbb{R}^n\):待優化的參數向量(比如曲線的系數)
      \(\mathbf{r}(\theta) = \begin{bmatrix} r_1(\theta) \\ \vdots \\ r_m(\theta) \end{bmatrix}\):殘差向量,\(r_i(\theta) = y_i - f(x_i; \theta)\)
      \(S(\theta)\):目標函數(損失函數),是我們要最小化的殘差平方和

      梯度下降法的核心思想是:在當前點,沿著目標函數下降最快的方向走一步,然后重復。而這個“最快下降方向”就是負梯度方向\(-\nabla S(\theta)\)。因此問題的關鍵在于計算目標函數\(S(\theta) = \ \mathbf{r}(\theta)\ ^2\)的梯度。根據求導的鏈式法則:

      \[\nabla S(\theta) = \frac{\partial}{\partial \theta} \left( \mathbf{r}(\theta)^T \mathbf{r}(\theta) \right) = 2 \, J(\theta)^T \mathbf{r}(\theta) \]

      其中:
      \(J(\theta)\):雅可比矩陣(Jacobian),大小為 \(m \times n\)

      \[J(\theta) = \begin{bmatrix} \frac{\partial r_1}{\partial \theta_1} & \cdots & \frac{\partial r_1}{\partial \theta_n} \\ \vdots & \ddots & \vdots \\ \frac{\partial r_m}{\partial \theta_1} & \cdots & \frac{\partial r_m}{\partial \theta_n} \end{bmatrix} = \frac{\partial \mathbf{r}}{\partial \theta^T} \]

      即目標函數的梯度是:\(\nabla S(\theta) = 2 J(\theta)^T \mathbf{r}(\theta)\)

      另一方面,在每次梯度下降之后,需要更新參數向量:

      \[\boxed{ \theta_{k+1} = \theta_k - \alpha \cdot \nabla S(\theta_k) = \theta_k - 2\alpha \cdot J_k^T \mathbf{r}_k } \]

      其中:
      \(\theta_k\):第 \(k\) 次迭代的參數
      \(\alpha > 0\):學習率(step size),控制步長
      \(J_k = J(\theta_k)\)\(\mathbf{r}_k = \mathbf{r}(\theta_k)\)

      因此,將梯度下降方法完整的流程總結如下:

      1. 初始化:選一個初始猜測 θ?
      2. 設置學習率 α(例如 0.01)
      3. 對 k = 0, 1, 2, ... 直到收斂:
        a. 計算殘差:\(r_k = y - f(x; θ_k)\)
        b. 計算雅可比矩陣:\(J_k = J(θ_k)\)
        c. 計算梯度:\(g_k = 2 J_k^T r_k\)
        d. 更新參數:\(θ_{k+1} = θ_k - α g_k\)
        e. 檢查是否收斂:\(Δθ = θ_{k+1} - θ_k < ε\)\(g_k < ε\)\(S(θ)\)變化很小
      4. 輸出最終參數 θ

      4. 實例

      從上述求解過程可以看到,梯度下降法其實比之前文章中介紹的Gauss-Newton方法要簡單很多,那么這里還是給出一個只使用Eigen實現梯度下降法求解非線性最小二乘問題的例子。例子中模型函數為\(f(x; \boldsymbol{\theta}) = a e ^{bx}\)

      #include <Eigen/Dense>
      #include <cmath>
      #include <iostream>
      #include <random>
      #include <vector>
      
      using namespace std;
      using namespace Eigen;
      
      // 模型函數: y = a * exp(b * x)
      double model(double x, const Vector2d& theta) {
        double a = theta(0);
        double b = theta(1);
        return a * exp(b * x);
      }
      
      // 計算殘差: r_i = y_i - f(x_i; a, b)
      VectorXd computeResiduals(const vector<double>& x_data,
                                const vector<double>& y_data, const Vector2d& theta) {
        int N = x_data.size();
        VectorXd r(N);
        for (int i = 0; i < N; ++i) {
          r(i) = y_data[i] - model(x_data[i], theta);
        }
        return r;
      }
      
      // 計算 Jacobian 矩陣 (N x 2): ?r_i/?a, ?r_i/?b
      MatrixXd computeJacobian(const vector<double>& x_data, const Vector2d& theta) {
        int N = x_data.size();
        MatrixXd J(N, 2);
        double a = theta(0);
        double b = theta(1);
      
        for (int i = 0; i < N; ++i) {
          double x = x_data[i];
          double exp_bx = exp(b * x);  // exp(b*x)
      
          J(i, 0) = -exp_bx;          // ?r/?a = -exp(b*x)
          J(i, 1) = -a * exp_bx * x;  // ?r/?b = -a * exp(b*x) * x
        }
        return J;
      }
      
      int main() {
        // ========================
        // 1. 真實參數
        // ========================
        Vector2d true_params;
        true_params << 2.0, -0.3;  // a=2.0, b=-0.3 → y = 2 * exp(-0.3 * x)
        cout << "真實參數: a = " << true_params(0) << ", b = " << true_params(1)
             << endl;
      
        // ========================
        // 2. 生成帶噪聲的數據
        // ========================
        int N = 20;
        vector<double> x_data(N), y_data(N);
      
        random_device rd;
        mt19937 gen(rd());
        normal_distribution<double> noise(0.0, 0.05);  // 小噪聲
      
        for (int i = 0; i < N; ++i) {
          x_data[i] = -2.0 + i * 0.4;  // x 從 -2 到 6
          double y_true = model(x_data[i], true_params);
          y_data[i] = y_true + noise(gen);
        }
      
        // ========================
        // 3. 初始化參數
        // ========================
        Vector2d theta;
        theta << 1.0, 0.0;  // 初始猜測: a=1.0, b=0.0
        cout << "初始猜測: a = " << theta(0) << ", b = " << theta(1) << endl;
      
        // ========================
        // 4. 梯度下降法
        // ========================
        int max_iter = 500;
        double alpha = 5e-3;  // 學習率
        double tol = 1e-6;
      
        cout << "\n開始梯度下降...\n";
        cout << "迭代\t殘差平方和\t\t參數 a\t\t參數 b\n";
        cout << "----\t----------\t\t------\t\t------\n";
      
        for (int iter = 0; iter < max_iter; ++iter) {
          // 計算殘差
          VectorXd r = computeResiduals(x_data, y_data, theta);
          double cost = r.squaredNorm();
      
          // 計算梯度
          MatrixXd J = computeJacobian(x_data, theta);
          Vector2d gradient = 2.0 * J.transpose() * r;
      
          // 打印當前狀態(每10次)
          if (iter % 10 == 0) {
            cout << iter << "\t" << cost << "\t\t" << theta(0) << "\t\t" << theta(1)
                 << endl;
          }
      
          // 終止條件
          if (gradient.norm() < tol) {
            cout << "收斂!梯度范數: " << gradient.norm() << endl;
            break;
          }
      
          // 更新參數
          theta -= alpha * gradient;
        }
      
        // ========================
        // 5. 輸出結果
        // ========================
        cout << "\n--- 擬合完成 ---" << endl;
        cout << "估計參數: a = " << theta(0) << ", b = " << theta(1) << endl;
        cout << "真實參數: a = " << true_params(0) << ", b = " << true_params(1)
             << endl;
      
        return 0;
      }
      

      運行結果如下:

      真實參數: a = 2, b = -0.3
      初始猜測: a = 1, b = 0
      
      開始梯度下降...
      迭代    殘差平方和              參數 a          參數 b
      ----    ----------              ------          ------
      0       22.7591         1               0
      10      1.11435         1.72284         -0.345
      20      0.100641                1.93634         -0.301778
      30      0.0326195               1.99193         -0.294493
      40      0.0286004               2.00545         -0.292882
      50      0.0283681               2.0087          -0.292503
      60      0.0283548               2.00948         -0.292413
      70      0.028354                2.00967         -0.292391
      80      0.0283539               2.00971         -0.292386
      90      0.0283539               2.00972         -0.292385
      100     0.0283539               2.00972         -0.292384
      110     0.0283539               2.00973         -0.292384
      120     0.0283539               2.00973         -0.292384
      收斂!梯度范數: 9.36104e-07
      
      --- 擬合完成 ---
      估計參數: a = 2.00973, b = -0.292384
      真實參數: a = 2, b = -0.3
      

      求解的關鍵還是在于計算雅可比矩陣,對于問題模型函數\(f(x; \boldsymbol{\theta}) = a e ^{bx}\)來說,雅可比矩陣應該是:

      \[J(\theta) = \begin{bmatrix} \frac{\partial (y_1-ae^{bx_1})}{\partial a} & \frac{\partial (y_1-ae^{bx_1})}{\partial b} \\ \frac{\partial (y_2-ae^{bx_2})}{\partial a} & \frac{\partial (y_2-ae^{bx_2})}{\partial b} \\ \vdots & \vdots \\ \frac{\partial (y_m-ae^{bx_m})}{\partial a} & \frac{\partial (y_m-ae^{bx_m})}{\partial b} \\ \end{bmatrix}= -\begin{bmatrix} e^{bx_1} & ae^{bx_1}x_1 \\ e^{bx_2} & ae^{bx_2}x_2 \\ \vdots & \vdots \\ e^{bx_m} & ae^{bx_m}x_m \\ \end{bmatrix} \]

      對比代碼中的實現:

      // 計算 Jacobian 矩陣 (N x 2): ?r_i/?a, ?r_i/?b
      MatrixXd computeJacobian(const vector<double>& x_data, const Vector2d& theta) {
        int N = x_data.size();
        MatrixXd J(N, 2);
        double a = theta(0);
        double b = theta(1);
      
        for (int i = 0; i < N; ++i) {
          double x = x_data[i];
          double exp_bx = exp(b * x);  // exp(b*x)
      
          J(i, 0) = -exp_bx;          // ?r/?a = -exp(b*x)
          J(i, 1) = -a * exp_bx * x;  // ?r/?b = -a * exp(b*x) * x
        }
        return J;
      }
      

      另外,除了迭代過程中的初始條件和迭代停止條件,控制步長的學習率也需要注意。設置的學習率過小,迭代次數就會很長導致收斂很慢;而設置的學習率過大,就容易略過最優解導致結果不問題。因此,在實際的工程應用中,通常不會使用原始的梯度下降法,而是根據需求使用不同優化版本的梯度下降法。關于這一點,有機會的話會在后續的文章中進一步論述。

      posted @ 2025-10-28 21:25  charlee44  閱讀(121)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 激情五月开心婷婷深爱| 亚洲国产精品成人综合色| 亚洲精品二区在线播放| 柠檬福利第一导航在线| 亚洲精品乱码久久久久久按摩高清| 亚洲中文字幕日产无码成人片| 遂川县| 国产极品美女高潮无套| 亚洲伊人久久综合影院| 沈丘县| 这里只有精品在线播放| 国产精品国产三级国快看| 天天爽夜夜爱| 成在线人免费视频| 中文字幕午夜福利片午夜福利片97| 桃花岛亚洲成在人线AV| 日韩黄色av一区二区三区| 人妻系列无码专区免费| 中文字幕日韩精品东京热| 97av麻豆蜜桃一区二区| 久久香蕉国产线看观看怡红院妓院 | 制服jk白丝h无内视频网站| 亚洲精品国产一二三区| 亚洲av一本二本三本| 国产麻豆91网在线看| 久久精品国产最新地址| 亚洲人成网线在线播放VA| 美女内射毛片在线看3d| 国产一二三四区中| 国产乱人激情H在线观看| 亚洲AVAV天堂AV在线网阿V| 麻豆蜜桃伦理一区二区三区 | 亚洲AV无码破坏版在线观看| 粉嫩少妇内射浓精videos| 一本色道久久综合熟妇人妻| 久久这里只精品国产免费9| 国产精品一区 在线播放| 成人国产精品日本在线观看| 日韩欧美国产aⅴ另类| 成人午夜电影福利免费| 国产成人啪精品午夜网站|