简体   繁体   English

将python scipy.optimize.leastsq转换为Java org.apache.commons.math3.fitting.leastsquares

[英]Python scipy.optimize.leastsq to Java org.apache.commons.math3.fitting.leastsquares

I try to mimic this algorithm , developed in Python, that calculates geolocation based on seen Wifi stations positions, itself based on this idea . 我尝试模仿这种用Python开发的算法 ,该算法根据看到的Wifi电台位置来计算地理位置,而该位置本身就是基于这种想法

This algorithm uses at first a Numpy function in order to calculate a basic weighted average of observed latitudes and longitudes. 该算法首先使用Numpy函数,以便计算观察到的纬度和经度的基本加权平均值。 To minimize the impact of possible Wifi positions errors, it's also use the “ scipy.optimize.leastsq ” method in order to calculate in an statistical way and if possible, a more precise position. 为了最大程度地减少可能出现的Wifi位置误差的影响,它还使用了“ scipy.optimize.leastsq ”方法,以便以统计方式以及更准确的方式计算位置。

I want to implement the same behavior on the Java Android platform. 我想在Java Android平台上实现相同的行为。

For all other calculations I successfully rely on org.apache.commons.math3. 对于所有其他计算,我成功地依赖org.apache.commons.math3。 So for the least-squares problem I logically try to rely on https://commons.apache.org/proper/commons-math/userguide/leastsquares.html . 因此,对于最小二乘问题,我在逻辑上尝试依靠https://commons.apache.org/proper/commons-math/userguide/leastsquares.html

My problem, if I well understood, is that Scipy manage for me the complexity of Jacobian function definition and my poor mathematics skills doesn't allow me to define correctly the model of a LeastSquaresProblem. 如果我很好理解的话,我的问题是Scipy为我管理了Jacobian函数定义的复杂性,而我较差的数学技能使我无法正确定义LeastSquaresProblem的模型。 I tried some experimentations based on this example , that seems closed to what I need, but the results aren't good as I don't know how to deal with the "jacobian" parts. 我根据此示例尝试了一些实验,这些实验似乎接近我的需要,但结果不佳,因为我不知道如何处理“ jacobian”部分。

As someone do for this post, could someone do the same thing for me and try to explain it in a simple way? 正如有人为这篇文章所做的那样,有人可以为我做同样的事情并尝试以简单的方式进行解释吗?

More details on how Python part is working : 有关Python部分工作方式的更多详细信息:

The “scipy.optimize.leastsq” statement used is: 使用的“ scipy.optimize.leastsq”语句为:

(lat, lon), cov_x, info, mesg, ier = 
scipy.optimize.leastsq(func, initial, args=data, full_output=True)

Where data are: latitude/longitude/age in milliseconds/signal strength, for example: data = numpy.array([(43.48932915, 1.66561772, 1000, -20), (43.48849093, 1.6648176, 2000, -10), (43.48818612, 1.66615113, 3000, -50)]) 其中数据为:纬度/经度/以毫秒为单位的年龄/信号强度,例如: data = numpy.array([(43.48932915, 1.66561772, 1000, -20), (43.48849093, 1.6648176, 2000, -10), (43.48818612, 1.66615113, 3000, -50)])

Initial is calculated weighted average latitude/longitude, in this example: initial = 43.48864654, 1.66550075 初始值是计算得出的加权平均纬度/经度,在此示例中: initial = 43.48864654, 1.66550075

Function is 功能是

  def func(initial, data):
        return numpy.array([
            geographic distance((float(point[latitude]), float(point[longitude])), (initial[latitude], initial[longitude])).meters * min(math.sqrt(2000.0 / float(point[age])), 1.0) / math.pow(float(point[signal strength]), 2)

The result is: 43.4885401095, 1.6648660983 结果是: 43.4885401095, 1.6648660983

My experiments in Java, I've replaced data values and have changed the way "modelI" is calculated. 我在Java中进行的实验已经替换了数据值,并更改了“ modelI”的计算方式。 I simplified signal strength and age values. 我简化了信号强度和年龄值。 But it's a fact, and results show, that it isn't sufficient. 但这是事实,结果表明,这还不够。

double modelI = calculateVincentyDistance(o.getY(), o.getX(), center.getY(), center.getX())* Math.min(Math.sqrt(2000.0/1000.0), 1.0) / Math.pow(-10, 2);

I also going to try https://github.com/odinsbane/least-squares-in-java , but I'm not sure to use it correctly as I don't master the way it's work. 我还将尝试使用https://github.com/odinsbane/least-squares-in-java ,但是由于我不掌握它的工作方式,因此我不确定使用是否正确。

FYI, I use Vincenty distance calculation that for example can be replaced by Haversine or Euclidean. 仅供参考,我使用Vincenty距离计算,例如可以用Haversine或Euclidean代替。

Thank you for any help ! 感谢您的任何帮助 !

The code is not easy to port because SciPy provides a more generic Least-squares minimization interface while Apache Commons Math provides curve fitting . 该代码不容易移植,因为SciPy提供了更通用的最小二乘最小化接口,而Apache Commons Math提供了曲线拟合 Still many optimization problems can be restated as curve fitting. 仍然可以将许多优化问题重申为曲线拟合。 In the Python code you minimize 在Python代码中,将最小化

F(current_point) = Sum{ (distance(known_point[i], current_point) * weight[i])^2 } -> min

Java curve fitting problem is a bit different: Java曲线拟合问题有点不同:

F(current_point) = Sum{ (target_value[i] - model[i](current_point))^2  } -> min

So equivalent fitting problem can be created by assigning all target_value s to 0 and making model[i] calculate weighted distance from current_point to known_point[i] . 因此,可以通过将所有target_value s分配为0并让model[i]计算从current_pointknown_point[i]加权距离来创建等效拟合问题。

In a general case such problems have no exact solution using formula and some numerical optimization method is used. 在一般情况下,此类问题无法使用公式精确求解,而是使用一些数值优化方法。 And here lies another difference: Java implementation explicitly requires you to provide means for the optimizer to calculate derivatives of the function being optimized. 这里还有另一个区别:Java实现明确要求您提供给优化器计算被优化函数的导数的方法。 Python code seems to use some kind of differences differentiator if Dfun is not provided. 如果未提供Dfun Python代码似乎使用某种差异区分Dfun You can do something like this in Java by hand or using FiniteDifferencesDifferentiator but for simple formulas it might be easier to code them explicitly using DerivativeStructure 您可以手动或使用FiniteDifferencesDifferentiator在Java中执行类似的操作,但是对于简单的公式,使用DerivativeStructure显式编码它们可能会更容易

static class PositionInfo {
    public final double latitude;
    public final double longitude;
    public final int ageMs;
    public final int strength;

    public PositionInfo(double latitude, double longitude, int ageMs, int strength) {
        this.latitude = latitude;
        this.longitude = longitude;
        this.ageMs = ageMs;
        this.strength = strength;
    }

    public double getWeight() {
        return Math.min(1.0, Math.sqrt(2000.0 / ageMs)) / (strength * strength);
    }
}


static DerivativeStructure getWeightedEuclideanDistance(double tgtLat, double tgtLong, PositionInfo knownPos) {
    DerivativeStructure varLat = new DerivativeStructure(2, 1, 0, tgtLat); // latitude is 0-th variable of 2 for derivatives up to 1
    DerivativeStructure varLong = new DerivativeStructure(2, 1, 1, tgtLong); // longitude is 1-st variable of 2 for derivatives up to 1
    DerivativeStructure latDif = varLat.subtract(knownPos.latitude);
    DerivativeStructure longDif = varLong.subtract(knownPos.longitude);
    DerivativeStructure latDif2 = latDif.pow(2);
    DerivativeStructure longDif2 = longDif.pow(2);
    DerivativeStructure dist2 = latDif2.add(longDif2);
    DerivativeStructure dist = dist2.sqrt();
    return dist.multiply(knownPos.getWeight());
}

// as in https://en.wikipedia.org/wiki/Haversine_formula
static DerivativeStructure getWeightedHaversineDistance(double tgtLat, double tgtLong, PositionInfo knownPos) {
    DerivativeStructure varLat = new DerivativeStructure(2, 1, 0, tgtLat);
    DerivativeStructure varLong = new DerivativeStructure(2, 1, 1, tgtLong);
    DerivativeStructure varLatRad = varLat.toRadians();
    DerivativeStructure varLongRad = varLong.toRadians();
    DerivativeStructure latDifRad2 = varLat.subtract(knownPos.latitude).toRadians().divide(2);
    DerivativeStructure longDifRad2 = varLong.subtract(knownPos.longitude).toRadians().divide(2);
    DerivativeStructure sinLat2 = latDifRad2.sin().pow(2);
    DerivativeStructure sinLong2 = longDifRad2.sin().pow(2);
    DerivativeStructure summand2 = varLatRad.cos().multiply(varLongRad.cos()).multiply(sinLong2);
    DerivativeStructure sum = sinLat2.add(summand2);
    DerivativeStructure dist = sum.sqrt().asin();
    return dist.multiply(knownPos.getWeight());
}

Using such preparation you may do something like this: 使用这种准备,您可以执行以下操作:

public static void main(String[] args) {

    // latitude/longitude/age in milliseconds/signal strength
    final PositionInfo[] data = new PositionInfo[]{
            new PositionInfo(43.48932915, 1.66561772, 1000, -20),
            new PositionInfo(43.48849093, 1.6648176, 2000, -10),
            new PositionInfo(43.48818612, 1.66615113, 3000, -50)
    };


    double[] target = new double[data.length];
    Arrays.fill(target, 0.0);

    double[] start = new double[2];

    for (PositionInfo row : data) {
        start[0] += row.latitude;
        start[1] += row.longitude;
    }
    start[0] /= data.length;
    start[1] /= data.length;

    MultivariateJacobianFunction distancesModel = new MultivariateJacobianFunction() {
        @Override
        public Pair<RealVector, RealMatrix> value(final RealVector point) {
            double tgtLat = point.getEntry(0);
            double tgtLong = point.getEntry(1);

            RealVector value = new ArrayRealVector(data.length);
            RealMatrix jacobian = new Array2DRowRealMatrix(data.length, 2);
            for (int i = 0; i < data.length; i++) {
                DerivativeStructure distance = getWeightedEuclideanDistance(tgtLat, tgtLong, data[i]);
                //DerivativeStructure distance = getWeightedHaversineDistance(tgtLat, tgtLong, data[i]);
                value.setEntry(i, distance.getValue());
                jacobian.setEntry(i, 0, distance.getPartialDerivative(1, 0));
                jacobian.setEntry(i, 1, distance.getPartialDerivative(0, 1));
            }

            return new Pair<RealVector, RealMatrix>(value, jacobian);
        }
    };


    LeastSquaresProblem problem = new LeastSquaresBuilder()
            .start(start)
            .model(distancesModel)
            .target(target)
            .lazyEvaluation(false)
            .maxEvaluations(1000)
            .maxIterations(1000)
            .build();

    LeastSquaresOptimizer optimizer = new LevenbergMarquardtOptimizer().
            withCostRelativeTolerance(1.0e-12).
            withParameterRelativeTolerance(1.0e-12);

    LeastSquaresOptimizer.Optimum optimum = optimizer.optimize(problem);
    RealVector point = optimum.getPoint();
    System.out.println("Start = " + Arrays.toString(start));
    System.out.println("Solve = " + point);
}

PS the logic of the weight seems suspicious to me. PS对我来说,重量的逻辑似乎令人怀疑。 In the question you reference the OP has some estimates for radius and then it is an obvious weight. 在您参考的问题中,OP对半径有一些估算,然后很明显。 Using reverse square of the signal strength which is measured in logarithmic dBm seems strange to me. 使用以对数 dBm为单位测量的信号强度的反平方我来说似乎很奇怪。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM