如何在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()
结果如下:
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()
结果如下: