在 python 中使用 log(1+e^x) 的泰勒级数展开 1 个暗向量

expand 1 dim vector by using taylor series of log(1+e^x) in python

我需要使用特定非线性函数的泰勒级数展开 (e^x or log(x) or log(1+e^x)) 对来自 1 个暗像素向量的每个像素值进行非线性展开 (e^x or log(x) or log(1+e^x)),但我目前的实现至少不适合我基于泰勒级数的概念。背后的基本直觉是将像素阵列作为 CNN 模型的输入神经元,其中每个像素都应使用非线性函数的泰勒级数展开进行非线性展开。

新更新 1:

根据我对泰勒级数的理解,泰勒级数是针对变量 x 的函数 F 根据函数 F 的值及其导数编写的变量 x0 的另一个值。在我的问题中,F 是特征(a.k.a,像素)的非线性变换函数,x 是每个像素值,x0 是麦克劳林级数在 0 处的近似值。

新更新2

如果我们使用近似阶数为2的log(1+e^x)的泰勒级数,每个像素值将通过取泰勒级数的第一和第二展开项产生两个新像素。

图示

这是上述公式的图形说明:

其中X为像素数组,p为泰勒级数的逼近阶数,α为泰勒展开系数

我想用非线性函数的泰勒级数展开来非线性展开像素向量,如上图所示。

我目前的尝试

这是我目前的尝试,对于像素阵列无法正常工作。我在想如何让同样的想法适用于像素阵列。

def taylor_func(x, approx_order=2):
    x_ = x[..., None] 
    x_ = tf.tile(x_, multiples=[1, 1, approx_order+ 1])  
    pows = tf.range(0, approx_order + 1, dtype=tf.float32) 
    x_p = tf.pow(x_, pows) 
    x_p_ = x_p[..., None]
    return x_p_

x = Input(shape=(4,4,3))
x_new = Lambda(lambda x: taylor_func(x, max_pow))(x)

我的新更新尝试:

x_input= Input(shape=(32, 32,3))

def maclurin_exp(x, powers=2):
    out= 0
    for k in range(powers):
        out+= ((-1)**k) * (x ** (2*k)) / (math.factorial(2 * k))
    return res

x_input_new = Lambda(lambda x: maclurin_exp(x, max_pow))(x_input)

此尝试不会产生上述数学公式所描述的结果。我敢打赌我在进行扩展时错过了一些东西。谁能指出我如何纠正这个问题?有更好的主意吗?

目标

我想用像素向量做非线性分布或者用某个非线性函数的泰勒级数展开来展开。有什么办法可以做到这一点?有什么想法吗?谢谢

这是一个非常有趣的问题,但我现在还不能说我很清楚。所以,虽然我有一些想法,但我可能会错过你想要做的事情的主旨。

看来您想开发自己的激活函数而不是使用 RELU 或 softmax。那里当然没有坏处。你给了三个候选人:e^x, log(x), and log(1+e^x).

注意 log(x) 渐近地接近负无穷大 x --> 0。所以,log(x) 是正确的。如果那是为了检查你得到的答案,或者是在你入睡时记下的东西,不用担心。但如果不是,你应该花一些时间确保你理解你所做事情的基础,因为后果可能非常严重。

你表示你正在寻找一个规范的答案,你在这里得到了一个二选一的答案。您会同时获得规范的答案和高性能的代码。

考虑到您不太可能编写出比 SciPy、Numpy 或 Pandas 的人更快、更精简的代码。或者,PyPy。或者 Cython 就此而言。他们的东西是标准的。所以不要试图通过编写您自己的、性能较差(并且可能有错误)的版本来与他们竞争,随着时间的推移,您将不得不维护这些版本。相反,通过使用它们来最大化您的发展和 运行 次。

让我们看一下 e^x 在 SciPy 中的实现,并提供一些代码供您使用。我知道您现阶段不需要图表,但它们很漂亮,可以帮助您了解泰勒(或麦克劳林,又名欧拉-麦克劳林)如何随着近似值的变化而工作。碰巧 SciPy 内置了泰勒近似。

import scipy
import numpy as np
import matplotlib.pyplot as plt

from scipy.interpolate import approximate_taylor_polynomial

x = np.linspace(-10.0, 10.0, num=100)

plt.plot(x, np.exp(x), label="e^x", color = 'black')

for degree in np.arange(1, 4, step=1):

    e_to_the_x_taylor = approximate_taylor_polynomial(np.exp, 0, degree, 1, order=degree + 2)

    plt.plot(x, e_to_the_x_taylor(x), label=f"degree={degree}")

plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0.0, shadow=True)

plt.tight_layout()
plt.axis([-10, 10, -10, 10])
plt.show()

产生这个:

但是假设您擅长 'the maths',可以这么说,并且如果 'mathy' 更好,则愿意使用稍微慢一些的东西,因为它可以很好地处理符号表示法。为此,让我推荐 SymPy。

考虑到这一点,这里有一些带有图表的 SymPy 代码,因为它看起来不错,而且因为我们需要返回并再次点击另一个点。

from sympy import series, Symbol, log, E
from sympy.functions import exp
from sympy.plotting import plot
import matplotlib.pyplot as plt
%matplotlib inline

plt.rcParams['figure.figsize'] = 13,10
plt.rcParams['lines.linewidth'] = 2

x = Symbol('x')

def taylor(function, x0, n):
    """ Defines Taylor approximation of a given function
    function -- is our function which we want to approximate
    x0 -- point where to approximate
    n -- order of approximation
    """    
    return function.series(x,x0,n).removeO()

# I get eyestain; feel free to get rid of this
plt.rcParams['figure.figsize'] = 10, 8
plt.rcParams['lines.linewidth'] = 1

c = log(1 + pow(E, x))

plt = plot(c, taylor(c,0,1), taylor(c,0,2), taylor(c,0,3), taylor(c,0,4), (x,-5,5),legend=True, show=False)

plt[0].line_color = 'black'
plt[1].line_color = 'red'
plt[2].line_color = 'orange'
plt[3].line_color = 'green'
plt[4].line_color = 'blue'
plt.title = 'Taylor Series Expansion for log(1 +e^x)'
plt.show()

我认为这两种选择都能让您到达需要的地方。

好的,现在谈谈另一点。经过一些修改后,您明确表示 log(1 +e^x) 是您的首选。但其他人没有通过嗅探测试。 e^x 随着多项式次数的变化而剧烈波动。由于算法的不透明性以及很少有人能从概念上理解这些东西,数据科学家可以把事情搞砸到人们甚至无法想象的程度。因此,请确保您的理论非常扎实。

最后一件事,考虑将 Erlang 分布的 CDF 视为激活函数(假设我是对的,并且您希望将自己的激活函数作为一个研究领域)。我认为没有人看过它,但它看起来很有希望。我想你可以把RGB的每个通道分解成两个参数之一,另一个是物理坐标。

您可以使用 tf.tile and tf.math.pow to generate the elements of the series expansion. Then you can use tf.math.cumsum 来计算部分和 s_i。最终你可以乘以权重 w_i 并计算最终总和。

这是一个代码示例:

import math
import tensorflow as tf

x = tf.keras.Input(shape=(32, 32, 3))  # 3-channel RGB.

# The following is determined by your series expansion and its order.
# For example: log(1 + exp(x)) to 3rd order.
# https://www.wolframalpha.com/input/?i=taylor+series+log%281+%2B+e%5Ex%29
order = 3
alpha = tf.constant([1/2, 1/8, -1/192])  # Series coefficients.
power = tf.constant([1.0, 2.0, 4.0])
offset = math.log(2)

# These are the weights of the network; using a constant for simplicity here.
# The shape must coincide with the above order of series expansion.
w_i = tf.constant([1.0, 1.0, 1.0])

elements = offset + alpha * tf.math.pow(
    tf.tile(x[..., None], [1, 1, 1, 1, order]),
    power
)
s_i = tf.math.cumsum(elements, axis=-1)
y = tf.math.reduce_sum(w_i * s_i, axis=-1)