如何在python中实现平滑钳位功能?

How to implement a smooth clamp function in python?

clamp函数是clamp(x, min, max) = min if x < min, max if x > max, else x

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

普通夹具:

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 是一个可逆函数——没有信息丢失。

出于其他目的,您可能需要确定 f(x) = xmax 对于所有 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) )

您正在寻找的是类似 Smoothstep 函数的东西,它有一个自由参数 N,给出 "smoothness",即应该有多少导数是连续的。它是这样定义的:

这在几个库中使用,可以在 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()

结果如下: