12

钳位函数为clamp(x, min, max) = min if x < min, max if x > max, else x

我需要一个行为类似于钳位函数但平滑的函数(即具有连续导数)。

4

2 回答 2

12

您正在寻找的是类似于Smoothstep函数,它有一个自由参数N,给出“平滑度”,即有多少导数应该是连续的。它是这样定义的:

在此处输入图像描述

这在几个库中使用,可以在 numpy 中实现为

import numpy as np
from scipy.special import comb

def smoothstep(x, x_min=0, x_max=1, N=1):
    x = np.clip((x - x_min) / (x_max - x_min), 0, 1)

    result = 0
    for n in range(0, N + 1):
         result += comb(N + n, n) * comb(2 * N + 1, N - n) * (-x) ** n

    result *= x ** (N + 1)

    return result

它减少到给定的常规钳位函数N=0(0 次可微),并随着 N 的增加而增加平滑度。您可以像这样可视化它:

import matplotlib.pyplot as plt

x = np.linspace(-0.5, 1.5, 1000)

for N in range(0, 5):
    y = smoothstep(x, N=N)
    plt.plot(x, y, label=str(N))

plt.legend()

这给出了这个结果:

在此处输入图像描述

于 2017-07-18T11:57:04.440 回答
9

普通夹:

np.clip(x, mi, mx)

Smoothclamp(保证与 x < min 和 x > max 的正常钳位一致):

def smoothclamp(x, mi, mx): return mi + (mx-mi)*(lambda t: np.where(t < 0 , 0, np.where( t <= 1 , 3*t**2-2*t**3, 1 ) ) )( (x-mi)/(mx-mi) )

Sigmoid(近似钳位,从不小于最小值,从不大于最大值)

def sigmoid(x,mi, mx): return mi + (mx-mi)*(lambda t: (1+200**(-t+0.5))**(-1) )( (x-mi)/(mx-mi) )

出于某些目的,Sigmoid 会比 Smoothclamp 更好,因为 Sigmoid 是一个可逆函数 - 不会丢失任何信息。

对于其他目的,您可能需要确定所有x > xmax的f(x) = xmax - 在这种情况下 Smoothclamp 更好。此外,正如另一个答案中提到的,有一个完整的 Smoothclamp 函数家族,尽管这里给出的函数足以满足我的目的(除了需要平滑导数之外,不需要特殊属性)

绘制它们:

import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1)
x = np.linspace(-4,7,1000)
ax.plot(x, np.clip(x, -1, 4),'k-', lw=2, alpha=0.8, label='clamp')
ax.plot(x, smoothclamp(x, -1, 4),'g-', lw=3, alpha=0.5, label='smoothclamp')
ax.plot(x, sigmoid(x, -1, 4),'b-', lw=3, alpha=0.5, label='sigmoid')
plt.legend(loc='upper left')
plt.show()

图形

同样具有潜在用途的是这两者的算术平均值:

def clampoid(x, mi, mx): return mi + (mx-mi)*(lambda t: 0.5*(1+200**(-t+0.5))**(-1) + 0.5*np.where(t < 0 , 0, np.where( t <= 1 , 3*t**2-2*t**3, 1 ) ) )( (x-mi)/(mx-mi) )
于 2017-07-18T11:25:29.460 回答