随机抽样一致算法(Random sample consensus,RANSAC 简单版)PYTHON实现

17 篇文章 0 订阅
13 篇文章 0 订阅

一、RANSAC理论介绍

普通最小二乘是保守派:在现有数据下,如何实现最优。是从一个整体误差最小的角度去考虑,尽量谁也不得罪。

RANSAC是改革派:首先假设数据具有某种特性(目的),为了达到目的,适当割舍一些现有的数据。

给出最小二乘拟合(红线)、RANSAC(绿线)对于一阶直线、二阶曲线的拟合对比:

可以看到RANSAC可以很好的拟合。RANSAC可以理解为一种采样的方式,所以对于多项式拟合、混合高斯模型(GMM)等理论上都是适用的。

RANSAC简化版的思路就是:

第一步:假定模型(如直线方程),并随机抽取Nums个(以2个为例)样本点,对模型进行拟合:

第二步:由于不是严格线性,数据点都有一定波动,假设容差范围为:sigma,找出距离拟合曲线容差范围内的点,并统计点的个数:

第三步:重新随机选取Nums个点,重复第一步~第二步的操作,直到结束迭代:

第四步:每一次拟合后,容差范围内都有对应的数据点数,找出数据点个数最多的情况,就是最终的拟合结果

至此:完成了RANSAC的简化版求解。

这个RANSAC的简化版,只是给定迭代次数,迭代结束找出最优。如果样本个数非常多的情况下,难不成一直迭代下去?其实RANSAC忽略了几个问题:

  • 每一次随机样本数Nums的选取:如二次曲线最少需要3个点确定,一般来说,Nums少一些易得出较优结果;
  • 抽样迭代次数Iter的选取:即重复多少次抽取,就认为是符合要求从而停止运算?太多计算量大,太少性能可能不够理想;
  • 容差Sigma的选取:sigma取大取小,对最终结果影响较大;

 

 

代码:

# _*_ coding:utf-8 _*_

import numpy as np
import scipy as sp
import scipy.linalg as sl

# ransac_fit, ransac_data = ransac(all_data, model, 50, 1000, 7e3, 300, debug=debug, return_all=True)
def ransac(data, model, n, k, t, d, debug=False, return_all=False):
    '''
    参考:http://scipy.github.io/old-wiki/pages/Cookbook/RANSAC
    伪代码:http://en.wikipedia.org/w/index.php?title=RANSAC&oldid=116358182
    输入:
        data - 样本点
        model - 假设模型:事先自己确定
        n - 生成模型所需的最少样本点
        k - 最大迭代次数
        t - 阈值:作为判断点满足模型的条件
        d - 拟合较好时,需要的样本点最少的个数,当做阈值看待
    输出:
        bestfit - 最优拟合解(返回nil,如果未找到)

    iterations = 0
    bestfit = nil #后面更新
    besterr = something really large #后期更新besterr = thiserr
    while iterations < k
    {
        maybeinliers = 从样本中随机选取n个,不一定全是局内点,甚至全部为局外点
        maybemodel = n个maybeinliers 拟合出来的可能符合要求的模型
        alsoinliers = emptyset #满足误差要求的样本点,开始置空
        for (每一个不是maybeinliers的样本点)
        {
            if 满足maybemodel即error < t
                将点加入alsoinliers
        }
        if (alsoinliers样本点数目 > d)
        {
            %有了较好的模型,测试模型符合度
            bettermodel = 利用所有的maybeinliers 和 alsoinliers 重新生成更好的模型
            thiserr = 所有的maybeinliers 和 alsoinliers 样本点的误差度量
            if thiserr < besterr
            {
                bestfit = bettermodel
                besterr = thiserr
            }
        }
        iterations++
    }
    return bestfit
    '''
    iterations = 0
    bestfit = None
    besterr = np.inf  # 设置默认值
    best_inlier_idxs = None
    while iterations < k:
        maybe_idxs, test_idxs = random_partition(n, data.shape[0])
        maybe_inliers = data[maybe_idxs, :]  # 获取size(maybe_idxs)行数据(Xi,Yi)
        test_points = data[test_idxs]  # 若干行(Xi,Yi)数据点
        maybemodel = model.fit(maybe_inliers)  # 拟合模型
        test_err = model.get_error(test_points, maybemodel)  # 计算误差:平方和最小
        also_idxs = test_idxs[test_err < t]
        also_inliers = data[also_idxs, :]
        if debug:
            print ('test_err.min()', test_err.min())
            print ('test_err.max()', test_err.max())
            print ('numpy.mean(test_err)', numpy.mean(test_err))
            print ('iteration %d:len(alsoinliers) = %d' % (iterations, len(also_inliers)))
        if len(also_inliers > d):
            betterdata = np.concatenate((maybe_inliers, also_inliers))  # 样本连接
            bettermodel = model.fit(betterdata)
            better_errs = model.get_error(betterdata, bettermodel)
            thiserr = np.mean(better_errs)  # 平均误差作为新的误差
            if thiserr < besterr:
                bestfit = bettermodel
                besterr = thiserr
                best_inlier_idxs = np.concatenate((maybe_idxs, also_idxs))  # 更新局内点,将新点加入

        iterations += 1
    if bestfit is None:
        raise ValueError("did't meet fit acceptance criteria")
    if return_all:
        return bestfit, {'inliers': best_inlier_idxs}
    else:
        return bestfit


def random_partition(n, n_data):
    """return n random rows of data and the other len(data) - n rows"""
    all_idxs = np.arange(n_data)  # 获取n_data下标索引
    np.random.shuffle(all_idxs)  # 打乱下标索引
    idxs1 = all_idxs[:n]
    idxs2 = all_idxs[n:]
    return idxs1, idxs2


class LinearLeastSquareModel:
    # 最小二乘求线性解,用于RANSAC的输入模型
    def __init__(self, input_columns, output_columns, debug=False):
        self.input_columns = input_columns
        self.output_columns = output_columns
        self.debug = debug

    def fit(self, data):
        A = np.vstack([data[:, i] for i in self.input_columns]).T  # 第一列Xi-->行Xi
        B = np.vstack([data[:, i] for i in self.output_columns]).T  # 第二列Yi-->行Yi
        x, resids, rank, s = sl.lstsq(A, B)  # residues:残差和
        return x  # 返回最小平方和向量

    def get_error(self, data, model):
        A = np.vstack([data[:, i] for i in self.input_columns]).T  # 第一列Xi-->行Xi
        B = np.vstack([data[:, i] for i in self.output_columns]).T  # 第二列Yi-->行Yi
        B_fit = sp.dot(A, model)  # 计算的y值,B_fit = model.k*A + model.b
        err_per_point = np.sum((B - B_fit) ** 2, axis=1)  # sum squared error per row
        return err_per_point


def test():
    # 生成理想数据
    n_samples = 500  # 样本个数
    n_inputs = 1  # 输入变量个数
    n_outputs = 1  # 输出变量个数
    A_exact = 20 * np.random.random((n_samples, n_inputs))  # 随机生成0-20之间的500个数据:行向量
    perfect_fit = 60 * np.random.normal(size=(n_inputs, n_outputs))  # 随机线性度即随机生成一个斜率
    B_exact = sp.dot(A_exact, perfect_fit)  # y = x * k

    # 加入高斯噪声,最小二乘能很好的处理
    A_noisy = A_exact + np.random.normal(size=A_exact.shape)  # 500 * 1行向量,代表Xi
    B_noisy = B_exact + np.random.normal(size=B_exact.shape)  # 500 * 1行向量,代表Yi

    if 1:
        # 添加"局外点"
        n_outliers = 100
        all_idxs = np.arange(A_noisy.shape[0])  # 获取索引0-499
        np.random.shuffle(all_idxs)  # 将all_idxs打乱
        outlier_idxs = all_idxs[:n_outliers]  # 100个0-500的随机局外点
        A_noisy[outlier_idxs] = 20 * np.random.random((n_outliers, n_inputs))  # 加入噪声和局外点的Xi
        B_noisy[outlier_idxs] = 50 * np.random.normal(size=(n_outliers, n_outputs))  # 加入噪声和局外点的Yi
    # setup model
    all_data = np.hstack((A_noisy, B_noisy))  # 形式([Xi,Yi]....) shape:(500,2)500行2列
    input_columns = range(n_inputs)  # 数组的第一列x:0
    output_columns = [n_inputs + i for i in range(n_outputs)]  # 数组最后一列y:1
    debug = False
    model = LinearLeastSquareModel(input_columns, output_columns, debug=debug)  # 类的实例化:用最小二乘生成已知模型

    linear_fit, resids, rank, s = sp.linalg.lstsq(all_data[:, input_columns], all_data[:, output_columns])

    # run RANSAC 算法
    ransac_fit, ransac_data = ransac(all_data, model, 100, 1e4, 7, 30, debug=debug, return_all=True)
    # ransac_fit, ransac_data = ransac(all_data, model, 100, 1e4, 0.01, 30, debug=debug, return_all=True)
    '''
    输入:
    data - 样本点
    model - 假设模型:事先自己确定
    n - 生成模型所需的最少样本点
    k - 最大迭代次数
    t - 阈值:作为判断点满足模型的条件
    d - 拟合较好时, 需要的样本点最少的个数, 当做阈值看待

    输出:
    bestfit - 最优拟合解(返回nil, 如果未找到)
    '''

    if 1:
        import pylab

        sort_idxs = np.argsort(A_exact[:, 0])
        A_col0_sorted = A_exact[sort_idxs]  # 秩为2的数组

        if 1:
            pylab.plot(A_noisy[:, 0], B_noisy[:, 0], 'k.', label='data')  # 散点图
            pylab.plot(A_noisy[ransac_data['inliers'], 0], B_noisy[ransac_data['inliers'], 0], 'bx',
                       label="RANSAC data")
        else:
            pylab.plot(A_noisy[non_outlier_idxs, 0], B_noisy[non_outlier_idxs, 0], 'k.', label='noisy data')
            pylab.plot(A_noisy[outlier_idxs, 0], B_noisy[outlier_idxs, 0], 'r.', label='outlier data')

        pylab.plot(A_col0_sorted[:, 0],
                   np.dot(A_col0_sorted, ransac_fit)[:, 0],
                   label='RANSAC fit')
        pylab.plot(A_col0_sorted[:, 0],
                   np.dot(A_col0_sorted, perfect_fit)[:, 0],
                   # label='exact system')
                   label='perfect fit')
        pylab.plot(A_col0_sorted[:, 0],
                   np.dot(A_col0_sorted, linear_fit)[:, 0],
                   label='linear fit')
        pylab.legend()
        pylab.show()


if __name__ == "__main__":
    test()

 

运行结果

 

注:

  • RANSAC的作用有点类似:将数据一切两段,一部分是自己人,一部分是敌人,自己人留下商量事,敌人赶出去。
  • RANSAC开的是家庭会议,不像最小二乘总是开全体会议。
  • RANSAC在每一代的循环开始时,时随机选取的一些点,做最小二乘法来确定拟合曲线的,在多次循环后保留最佳的那一次随机生成的拟合曲线。

 

参考资料:

http://www.cnblogs.com/xingshansi/p/6763668.html

https://blog.csdn.net/vict_wang/article/details/81027730

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: RANSACRandom Sample Consensus)是一种基于随机采样的拟合算法,常用于拟合含有噪声的数据集。在拟合曲线时,RANSAC可以过滤掉不符合模型的噪声点,从而得到更准确的曲线拟合结果。 下面是用Python实现RANSAC拟合曲线的示例代码: ```python import numpy as np import matplotlib.pyplot as plt # 生成含噪声的数据集 x = np.linspace(-5, 5, 50) y = 2 * x + 3 + np.random.randn(50) * 3 # 定义RANSAC函数 def ransac(data, model, n, k, t, d, debug=False, return_all=False): """ 输入: data - 样本点 model - 假设模型:事先自己确定,比如这里的lineModel n - 生成模型所需的最少样本点 k - 最大迭代次数 t - 阈值:作为判断点满足模型的条件 d - 拟合度:样本点满足模型的最少数量 """ iterations = 0 bestfit = None besterr = np.inf best_inlier_idxs = None while iterations < k: # 从样本中随机选取n个点 maybe_idxs = np.random.randint(0, data.shape[0], n) maybe_inliers = data[maybe_idxs, :] # 除去随机选出的点,剩下的就是测试点 test_idxs = np.arange(data.shape[0]) test_idxs = np.delete(test_idxs, maybe_idxs) test_inliers = data[test_idxs, :] # 用假设模型拟合随机选取的点 maybemodel = model.fit(maybe_inliers) # 计算所有点到这条线的距离 test_err = model.get_error(test_inliers, maybemodel) # 找到满足条件的点 also_idxs = test_idxs[test_err < t] also_inliers = data[also_idxs, :] # 如果满足条件的点的数量大于d,说明找到了一个合理的模型 if len(also_inliers) > d: # 将随机选取的点和满足条件的点合并,重新拟合模型 betterdata = np.concatenate((maybe_inliers, also_inliers)) bettermodel = model.fit(betterdata) # 计算新模型下所有点到曲线的距离 better_errs = model.get_error(betterdata, bettermodel) # 计算误差 thiserr = np.mean(better_errs) # 如果误差小于之前的最小误差,就更新最优模型 if thiserr < besterr: bestfit = bettermodel besterr = thiserr best_inlier_idxs = np.concatenate((maybe_idxs, also_idxs)) iterations += 1 if debug: print('RANSAC Iteration %d: Inliers: %d' % (iterations, len(best_inlier_idxs))) if bestfit is None: raise ValueError("RANSAC: unable to find a valid consensus set") if return_all: return bestfit, {'inliers': best_inlier_idxs} else: return bestfit # 定义线性模型 class LineModel: def __init__(self): self.a = None self.b = None def fit(self, data): x = data[:, 0] y = data[:, 1] A = np.vstack([x, np.ones(len(x))]).T self.a, self.b = np.linalg.lstsq(A, y, rcond=None)[0] return self def get_error(self, data, model): x = data[:, 0] y = data[:, 1] A = np.vstack([x, np.ones(len(x))]).T return np.abs(np.dot(A, model) - y) # 使用RANSAC拟合直线 data = np.column_stack([x, y]) model = LineModel() ransac_fit = ransac(data, model, 2, 100, 10, 40, debug=True, return_all=True) # 绘制结果 inlier_idxs = ransac_fit[1]['inliers'] outlier_idxs = np.delete(np.arange(len(data)), inlier_idxs) plt.plot(data[inlier_idxs, 0], data[inlier_idxs, 1], '.g', label='Inliers') plt.plot(data[outlier_idxs, 0], data[outlier_idxs, 1], '.r', label='Outliers') line_x = np.array([-5, 5]) line_y = ransac_fit[0].a * line_x + ransac_fit[0].b plt.plot(line_x, line_y, '-b', label='RANSAC line') plt.legend(loc='best') plt.show() ``` 上述代码生成含噪声的数据集,然后定义了一个线性模型和RANSAC函数。最后使用RANSAC拟合直线并绘制结果。可以根据需要修改代码以拟合不同的曲线。 ### 回答2: RANSAC是一种基于迭代的参数估计方法,常用于拟合曲线。它主要通过随机选择样本中的一部分点来估计曲线的参数,并根据估计结果计算其他样本点与估计曲线之间的误差。下面我来介绍一下RANSAC拟合曲线的Python实现。 首先,我们需要导入一些必要的库: ```python import numpy as np from sklearn.linear_model import RANSACRegressor from sklearn.preprocessing import PolynomialFeatures import matplotlib.pyplot as plt ``` 接下来,我们准备一些样本数据: ```python x = np.linspace(-5, 5, 100) y = 2 * x**2 - 3 * x + 1 + np.random.randn(100) * 5 # 添加噪声 ``` 然后,我们使用RANSACRegressor进行拟合曲线的操作: ```python ransac = RANSACRegressor() poly = PolynomialFeatures(degree=2) # 设置多项式最高阶数为2 x_poly = poly.fit_transform(x.reshape(-1, 1)) ransac.fit(x_poly, y) inlier_mask = ransac.inlier_mask_ outlier_mask = np.logical_not(inlier_mask) ``` 这里我们使用了二次多项式进行拟合,因此设置`degree=2`。`inlier_mask`是用于识别符合模型的内点,`outlier_mask`则是用于识别不符合模型的外点。 最后,我们可以将拟合结果可视化: ```python plt.scatter(x[inlier_mask], y[inlier_mask], color='blue', label='Inliers') plt.scatter(x[outlier_mask], y[outlier_mask], color='red', label='Outliers') plt.plot(x, ransac.predict(poly.fit_transform(x.reshape(-1, 1))), color='black', label='RANSAC') plt.legend() plt.show() ``` 这里,我们使用蓝色的点表示符合模型的内点,红色的点表示不符合模型的外点,黑色的曲线表示拟合的曲线。 以上就是RANSAC拟合曲线的Python实现方法。通过以上步骤,我们可以很方便地使用RANSAC算法来拟合曲线并识别出符合模型的内点。 ### 回答3: RANSACRandom Sample Consensus)是一种鲁棒拟合模型的算法,可以用于拟合曲线。在Python中,可以使用scikit-learn库中的RANSACRegressor类来实现RANSAC算法。 首先,导入必要的库: ```python from sklearn.linear_model import RANSACRegressor import numpy as np import matplotlib.pyplot as plt ``` 然后,准备数据集,包括自变量和因变量: ```python x = np.array([1, 2, 3, 4, 5]) # 自变量 y = np.array([2, 3, 4, 5, 6]) # 因变量 ``` 接下来,使用RANSACRegressor类进行拟合: ```python model = RANSACRegressor() model.fit(x[:, np.newaxis], y) # 将自变量转换成列向量 ``` 拟合完成后,可以得到拟合的直线的斜率和截距: ```python slope = model.estimator_.coef_[0] intercept = model.estimator_.intercept_ ``` 最后,可以绘制原始数据和拟合的曲线: ```python plt.scatter(x, y, color='blue', label='Data') plt.plot(x, model.predict(x[:, np.newaxis]), color='red', label='RANSAC Fit') plt.legend() plt.show() ``` 以上就是用Python实现RANSAC拟合曲线的过程。通过这个方法可以对任意数据集进行曲线拟合,并得到拟合的直线模型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值