优化函数(例如,电容器充电)曲线的参数以拟合数据
Optimize parameters of a function (e.g., capacitor-charging) curve to fit data
在尝试将 y = a * (1 - exp(-x / b))
形式的函数拟合到某些给定数据时,我有点迷路了。我怀疑 optimization package of 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
接口的实现吗?如果是这样,它需要看起来像什么?
找到了使用 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
在尝试将 y = a * (1 - exp(-x / b))
形式的函数拟合到某些给定数据时,我有点迷路了。我怀疑 optimization package of 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
接口的实现吗?如果是这样,它需要看起来像什么?
找到了使用 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