创建自定义损失函数以最小化态度误差

问题描述 投票:0回答:2

我想使用 IMU(加速度计和陀螺仪)读数通过神经网络计算姿态。输入为

input_shape = (time steps, 6)
,输出为四元数形式
output_shape = (time steps,4)

根据数学计算,参考四元数与预测四元数之间的误差为

y_pred[i,]=w0,x0,y0,z0
y_true[i,]=w1,x1,y1,z1 
w = w0*w1 - x0*x1 - y0*y1 - z0*z1
x = w0*x1 + x0*w1 + y0*z1 - z0*y1
y = w0*y1 - x0*z1 + y0*w1 + z0*z1
z = w0*z1 + x0*y1 - y0*x1 + z0*w1
error_quaternion = [w, x, y, z]

为了最小化误差,误差四元数 (w) 的缩放部分必须最小化。 (请忽略数学) 因此,为了达到最佳预测,必须最小化

w
(w 是预测姿态和参考姿态之间的最短角度)-

参考 = {Markley、F. Landis 和 John L. Crassidis。基本原理 航天器姿态确定与控制。卷。 1286.纽约, 美国纽约::Springer 纽约,2014 年。}

我写了这个损失函数

def LossQuat2(y_true, y_pred):
        a, b = y_true.get_shape()
        error = []
        for i in range(a):
            w0,x0,y0,z0 = tf.unstack(y_pred[i,])
            w1,x1,y1,z1 = tf.unstack(y_true[i,])
            x1 = -x1
            y1 = -y1
            z1 = -z1
            w = w0*w1 - x0*x1 - y0*y1 - z0*z1
            error.append(2*tf.math.acos(K.clip(tf.math.sqrt(w*w), -1., 1.)))
        return tf.reduce_mean(error)

为了验证它确实计算了错误,我尝试了这段代码并精确计算了错误

w0,x0,y0,z0 = y_pred[i,]
w1,x1,y1,z1 = y_true[i,]
x1 = -x1
y1 = -y1
z1 = -z1
w = w0*w1 - x0*x1 - y0*y1 - z0*z1
error = 2*math.acos(K.clip(np.sqrt(w*w), -1., 1.))

但是使用这个损失函数训练模型后,输出误差比MSE损失函数大得多。而且,它比 MSE 太慢了。

  1. 为什么这个损失函数在数学上是正确的,但不能正确地减少误差?
  2. 如何减少损失函数的执行时间?
  3. 真的可以使用for循环函数吗?有没有办法去掉for循环?

更新:

数学

四元数

四元数是具有 4 个元素的态度表示 q=[w x y z]

w
是标量部分或实部

x y z
是矢量部分或虚部

此外,四元数可以写成:

q = [cos(theta/2) e*sin(theta/2)] , e is a unit vector (e=[i j k]
  • 我打算通过神经网络来估计四元数

四元数逆

四元数逆或四元数共轭可以通过以下方式计算:

quaternion = [w x y z]
inverse(quaternion) = [w -x -y -z]

四元数乘法

为了找到估计姿态和真实(参考)姿态之间的差异,必须使用四元数乘法将估计姿态(NN 输出)乘以四元数参考。

四元数乘法:

q_m = q1 * inverse(q2)

q_m = q2 * inverse(q1)

两者是一样的。

如果

q1=w0,x0,y0,z0
q2=w1,x1,y1,z1 

然后

q_m = [w x y z]
可以通过以下方式计算:

w = w0*w1 - x0*x1 - y0*y1 - z0*z1
x = w0*x1 + x0*w1 + y0*z1 - z0*y1
y = w0*y1 - x0*z1 + y0*w1 + z0*z1
z = w0*z1 + x0*y1 - y0*x1 + z0*w1

q1 和 q2 之间的最短角度是 theta:

Theta = 2*acos(sqrt(w*w))

我需要的是写一个损失函数来最小化

theta
,如果theta=0,
w
将等于1,所以,最优的q_m是:

q_m=[1 0 0 0]

非常感谢大卫哈里斯@david-harris:

def loss(y_true, y_pred):
    z = y_true * y_pred * tf.constant([[1., -1., -1., -1.]])
    wtot = tf.reduce_sum(z, axis=1)
    return tf.reduce_mean(2*tf.math.acos(tf.math.sqrt(wtot*wtot)))

它快得多,但似乎它减少了四元数的所有值,因此它无法正常工作。

**

很抱歉有很多数学。

**

更新2

根据大卫建议的代码,我写了这个:

def loss(y_true, y_pred):
z = y_true * (y_pred * tf.constant([1., -1., -1., -1.000000000]))
wtot = tf.reduce_sum(z,1)
return tf.reduce_mean(2*tf.math.acos(K.clip(tf.math.sqrt(wtot*wtot), -1.,1.)))

这段代码减少了损失,但 MSE 呈指数增长。我知道这段代码不会直接针对 MSE 进行优化,但由于数学原因,MSE 也必须降低。 10 个纪元后

loss: 0.0124 - mse: 227.4045 

基于自定义损失的输出之一

橙色=参考

蓝色 = 由 NN 估计

基于MSE损失函数的输出之一

tensorflow keras time-series loss-function
2个回答
1
投票

您应该能够使用这种方法对计算进行矢量化(并加速)。 (我不确定所有标志都正确 - 不明白为什么你的行 'x1 = -x1' 在那里。我已经暂时删除了 'clip' 部分,如果你想要它)

def loss(y_true, y_pred):
    z = y_true * y_pred * tf.constant([[1., -1., -1., -1.]])
    wtot = tf.reduce_sum(z, axis=1)
    return tf.reduce_mean(2*tf.math.acos(tf.math.sqrt(wtot*wtot)))

看不出数学错误是什么,抱歉


0
投票

你的公式有错误:

y = w0*y1 - x0*z1 + y0*w1 + z0*z1

应该是这样

y = w0*y1 - x0*z1 + y0*w1 + z0*x1
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.