全部最小二乘BiSquare优化。

2023-07-17  本文已影响0人  寽虎非虫003

基本介绍

全部最小二乘解决形如如下的最小二乘问题
E_{TLS}=\sum_i (\boldsymbol{a}_i\boldsymbol{x})^2=||\mathbf{A}\boldsymbol{x}||^2
求解非零项转换为如下的特征值问题
\boldsymbol{x} = \text{arg} \min_\boldsymbol{x} \boldsymbol{x}^T(\mathbf{A}^T\mathbf{A}), \qquad \text{且} ||\boldsymbol{x}||^2=1.
可以最小化这个约数问题的\boldsymbol{x}就是\mathbf{A}^T\mathbf{A}最小的特征值对应的特征向量。它和\mathbf{A}的最末一个右特征向量是相同的。这是因为
\mathbf{A=U \Sigma V}^T \\ \mathbf{A}^T\mathbf{A=U \Sigma V}^T \\ \mathbf{A}^T\mathbf{A} \boldsymbol{v}_k = \sigma_k^2- \boldsymbol{v}_k
通过选择最小的\sigma_k值,我们就可以达到约束这个问题的目的。
求解直线拟合问题ax+by+c=0的时候,为了所有的输入变量具有相同的噪声模型,需要先对变量进行处理,减去各自均值
\hat{x}_i=x_i- \overline x\\ \hat{y}_i=y_i- \overline y\\
然后通过最小化
E_{LTS-2Dm} = \sum_i(a\hat{x}+b\hat{y})^2
来拟合2D直线方程a(x-\overline x)+b(y-\overline y)=0

最后在ax+by+c=0c = -a\overline{x} -b\overline{y}

BiSquare

给每个点施加一个权重,离群点的权重会变为零。
按matlab的文档中的写法
u_i = \frac{r_{i}}{Ks};\\ \displaystyle w_i = \begin{cases} (1-( u_i)^2)^2,\quad |u_i|<1\\ \qquad 0,\qquad \qquad |u_i|\geqslant 1\\ \end{cases}
其中 K=4.685 为调谐常数,s 为稳健标准偏差,由残差的中位绝对偏差 (MAD) 除以 0.6745 得出。
第一次迭代的时候所有的权重都默认为1.
则在任意一次迭代中,直线的加权的最小全部最小二乘的形式变为
E_{LTS-2Dm} = \sum_i w_i(a\hat{x}+b\hat{y})^2
解的过程变为了
\mathbf{WA=U \Sigma V}^T \\ \mathbf{(WA)}^T\mathbf{WA=U \Sigma V}^T \\ \mathbf{(WA)}^T\mathbf{WA} \boldsymbol{v}_k = \sigma_k^2- \boldsymbol{v}_k
其中W为对角矩阵,使得W_{ii}=w_i
结果同样是取最小的\sigma_k值对应的\boldsymbol{v}_k来达到约束这个问题的目的。

实现步骤

在实际操作过程中计算s的过程为
r^* = \frac{\sum w_i r_i}{\sum w_i}\\ s=\frac{ \text{median}\{|r_i-r^*| \}}{0.6745}

实现代码

int BiSquareLineFitting(
    const Pointf *vPts, const int n,
    double &xMean, double &yMean,
    float &a, float &b, float &c)
{
    double xsum{0.f}, ysum{0.f};

    // 初始化权重
    cv::Mat_<double> mWeight(n, 1);
    for (size_t i = 0; i < n; i++)
    {
        mWeight(i, 0) = 1.f;
    }

    cv::Mat_<double> mA(n, 2);

    // 迭代计算
    int maxIter = n * (n - 1) / 2;
    maxIter = min(maxIter, 50);

    cv::Mat_<double> mWA(n, 2);      ///< 实际计算时候,权重与A的乘积矩阵
    cv::Mat_<double> mR(n, 1);       ///< 偏差
    cv::Mat_<double> mRsort(n, 1);   ///< 偏差进行排序
    cv::Mat_<double> mMadsort(n, 1); ///< 偏差进行排序
    cv::Mat_<double> mWR(n, 1);      ///< 加权后的偏差
    cv::Mat w, u, vt;
    cv::Mat_<double> vk;
    double K = 4.685;
    double s{};
    double sumWR{}; // 加权后的偏差值
    for (size_t iter = 0; iter < maxIter; iter++)
    {
        // 这儿是不是可以根据权重先重新更新一下均值
        xsum = 0.0f;
        ysum = 0.0f;
        double w_iSum{0.f};
        for (size_t i = 0; i < n; i++)
        {
            xsum += vPts[i].x * mWeight(i, 0);
            ysum += vPts[i].y * mWeight(i, 0);
            w_iSum +=mWeight(i, 0);
        }
        // xMean = xsum / n;
        // yMean = ysum / n;

        xMean = xsum / w_iSum;
        yMean = ysum / w_iSum;

        for (size_t i = 0; i < n; i++)
        {
            mA(i, 0) = vPts[i].x - xMean;
            mA(i, 1) = vPts[i].y - yMean;
            // mA(i, 2) = 1.f;
        }

        // 更新计算权重后的矩阵
        for (size_t j = 0; j < n; j++)
        {
            mWA(j, 0) = mA(j, 0) * mWeight(j, 0);
            mWA(j, 1) = mA(j, 1) * mWeight(j, 0);
        }

        // 更新权重之后计算
        cv::SVD::compute(mWA, w, u, vt);
        vk = vt.t().col(1);
#ifdef _DEBUG
        std::cout << vk << std::endl;
#endif
        a = vk(0, 0);
        b = vk(1, 0);

        // 偏差矩阵
        for (size_t i = 0; i < n; i++)
        {
            // mR(i, 0) = mWeight(i, 0)*(a * mA(i, 0) + b * mA(i, 1));
            mR(i, 0) = (a * mA(i, 0) + b * mA(i, 1));
        }
        // 偏差中位数
        double median{};
        double wr_iSum{};
        w_iSum = 0.f;
        for (size_t i = 0; i < n; i++)
        {
            wr_iSum += mWeight(i, 0) * mR(i, 0);
            w_iSum += mWeight(i, 0);
        }
        median = wr_iSum / w_iSum;

        // mRsort = cv::abs(mRsort - median);
        mRsort = cv::abs(mR - median);
        cv::sort(mRsort, mMadsort, cv::SORT_EVERY_COLUMN + cv::SORT_ASCENDING);
        double mad = n % 2 == 0 ? (mMadsort(n / 2, 0) + mMadsort(n / 2 - 1, 0)) / 2 : mMadsort(n / 2, 0);
        s = mad / 0.6745;
        // 根据新计算出来的参数用bisquare更新权重
        for (size_t i = 0; i < n; i++)
        {
            double u_i = mR(i, 0) / (K * s);
            if (abs(u_i) < 1)
            {
                mWeight(i, 0) = pow(1 - pow(u_i, 2), 2);
            }
            else
            {
                mWeight(i, 0) = 0.f;
            }
            mWR(i, 0) = mWeight(i, 0) * pow(mR(i, 0), 2);
        }

        //
        sumWR = cv::sum(mWR)[0];
#ifdef _DEBUG
        // std::cout << "mWeight = " << mWeight << std::endl;
        std::cout << "sumWR = " << sumWR << std::endl;
#endif
    }

    a = vk(0, 0);
    b = vk(1, 0);
    c = -a * xMean - b * yMean;

    return 0;
}

参考

全部最小二乘
最小二乘法介绍(matlab文档翻译)

上一篇 下一篇

猜你喜欢

热点阅读