首页 > 解决方案 > 优化函数(例如,电容器充电)曲线的参数以拟合数据

问题描述

在我尝试将表单的函数拟合y = a * (1 - exp(-x / b))到某些给定数据时,我有点迷茫。我怀疑apache-common-math 的优化包可能会有所帮助,但我还没有成功使用它。您可以在下面找到一些代码来解释我想要实现的目标。

import kotlin.math.exp
import kotlin.random.Random

// Could be interpreted as a capacitor-charging curve with Vs = a and t = b
fun fGeneric(a: Double, b: Double, x: Double) = a * (1 - exp(-x / b))

fun fGiven(x: Double) = fGeneric(a = 10.0, b = 200.0, x = x)

fun fGivenWithNoise(x: Double) = fGiven(x) + Random.nextDouble(-0.1, 0.1)

fun main() {
    val xs = (0..1000).map(Int::toDouble).toDoubleArray()
    val ys = xs.map { x -> fGivenWithNoise(x) }.toDoubleArray()
    // todo: From data, find a and b, such that fGeneric fits optimally.
}

我需要提供MultivariateDifferentiableVectorFunction接口的实现吗?如果是这样,它需要是什么样子?

标签: mathkotlinmathematical-optimizationgradient-descentnonlinear-optimization

解决方案


通过使用找到解决方案lbfgs4j

package com.jaumo.ml.lifetimevalue

import com.github.lbfgs4j.LbfgsMinimizer
import com.github.lbfgs4j.liblbfgs.Function
import kotlin.math.exp
import kotlin.random.Random

// Could be interpreted as a capacitor-charging curve with Vs = a and t = b
fun fGeneric(a: Double, b: Double, x: Double) = a * (1 - exp(-x / b))

fun fGiven(x: Double) = fGeneric(a = 10.0, b = 200.0, x = x)

fun fGivenWithNoise(x: Double) = fGiven(x) + Random.nextDouble(-0.1, 0.1)

private fun subtractVectors(a: DoubleArray, b: DoubleArray): DoubleArray {
    assert(a.size == b.size)
    val result = DoubleArray(a.size)
    (a.indices).forEach { dim ->
        result[dim] = a[dim] - b[dim]
    }
    return result
}

fun main() {
    val xs = (0..1000).map(Int::toDouble).toDoubleArray()
    val ys = xs.map { x -> fGivenWithNoise(x) }.toDoubleArray()

    val f = object : Function {
        override fun getDimension(): Int {
            return 2
        }

        override fun valueAt(x: DoubleArray): Double {
            val maxVal = x[0]
            val slowness = x[1]
            val capacitorFunc = { x0: Double ->
                maxVal * (1 - exp(-x0 / slowness))
            }
            return subtractVectors(xs.map(capacitorFunc).toDoubleArray(), ys)
                .map { it * it }
                .sum()
        }

        override fun gradientAt(x: DoubleArray): DoubleArray {
            val a = valueAt(doubleArrayOf(x[0] - 0.001, x[1]))
            val b = valueAt(doubleArrayOf(x[0] + 0.001, x[1]))
            val c = valueAt(doubleArrayOf(x[0], x[1] - 0.001))
            val d = valueAt(doubleArrayOf(x[0], x[1] + 0.001))
            return doubleArrayOf(b - a, d - c)
        }
    }

    val minimizer = LbfgsMinimizer()
    val x = minimizer.minimize(f, doubleArrayOf(1.0, 10.0))
    println(x[0])
    println(x[1])
}

结果看起来不错:

9.998170586347115
200.14238710377768

推荐阅读