乐趣区

关于机器学习:将特征转换为正态分布的一种方法示例

正态(高斯)散布在机器学习中起着核心作用,线性回归模型中要假如随机误差等方差并且遵从正态分布,如果变量遵从正态分布,那么更容易建设实践后果。

统计学畛域的很大一部分钻研都是假如数据是正态分布的,所以如果咱们的数据具备是正态分布,那么么则能够取得更好的后果。然而个别状况下咱们的数据都并不是正态分布,所以 如果咱们能将这些数据转换成正态分布那么对咱们建设模型来说是一件十分有帮忙的事件。

 standard_normal = np.random.normal(0, 1, size=1_000_000)
 fontdict = {'family':'serif', 'color':'darkgreen', 'size':16}
 fig, axs = plt.subplots(1, 1, figsize=(8, 8))
 axs.hist(standard_normal, bins=1000, density=True, fc=(0,0,1,0.4))
 
 axs.set_title('Standard Normal Distribution', fontdict=fontdict, fontweight='bold', pad=12)
 axs.set_xlabel('X', fontdict=fontdict, fontweight='normal', labelpad=12)
 axs.set_ylabel('Density', fontdict=fontdict, fontweight='normal', labelpad=12)
 axs.grid()

如果你正在解决一个密度 (大概) 呈线性降落的个性(见下图)。

 x = np.linspace(0, 1, 1001)
 sample = (3 - np.sqrt(9 - 8 * np.random.uniform(0, 1, 1_000_000))) / 2fontdict = {'family':'serif', 'color':'darkgreen', 'size':16}
 fig, axs = plt.subplots(1, 1, figsize=(8, 8))
 axs.hist(sample, bins=1000, density=True, fc=(0,0,1,0.4))
 axs.scatter(x, np.full_like(x, 0.01), c=x, cmap=cmap)
 axs.set_title('Original Feature Distribution', fontdict=fontdict, fontweight='bold', pad=12)
 axs.set_xlabel('X', fontdict=fontdict, fontweight='normal', labelpad=12)
 axs.set_ylabel('Density', fontdict=fontdict, fontweight='normal', labelpad=12)
 axs.grid()

要将这个特色转换为具备钟形散布的变量,可能没有那么简略,我如果我应用某种变换将密度最高的左端放到核心,那么核心两侧的其余点怎么办?

如果变换是将点从两头和左边的 [0,1] 移到均值的任意一边 (N(0,1) =0) 那么实质上是一个非枯燥的变换,这不是很好因为那样的话,变换后的特征值就没有什么意义了。尽管咱们可能失去一个钟形散布,然而对转换后的值没有意义,排序也不再被保留(见下图 3 中转换后的特征值的散点图)。

 log_transform = lambda ar: np.multiply(1.6 * np.log10(ar+1e-8), np.random.choice((-1, 1), size=ar.size)
 fontdict = {'family':'serif', 'color':'darkgreen', 'size':16}
 fig, axs = plt.subplots(1, 1, figsize=(8, 8))
 axs.hist(standard_normal, bins=1_000, density=True, fc=(0,0,1,0.4), label='Standard Normal')
 axs.hist(log_transform(sample), bins=1_000, density=True, fc=(1,0,0,0.4), label='Log Transform')
 
 axs.scatter(log_transform(x), np.full_like(x, 3e-3), c=x, cmap=cmap)
 axs.set_xlim(-5, 5)
 axs.set_title('Log Transform', fontdict=fontdict, fontweight='bold', pad=12)
 axs.set_xlabel('$\pm$1.6log(X)', fontdict=fontdict, fontweight='normal', labelpad=12)*
 axs.set_ylabel('Density', fontdict=fontdict, fontweight='normal', labelpad=12)
 
 axs.legend()
 axs.grid()

特色的密度是枯燥递加的。指标是应用范畴 (-∞,∞) 的变换来拉伸和压缩不同点四周的 [0,1] 范畴,并且变换空间中每个点的密度应该是 N(0,1)所给出的。所以是不是能够尝试应用其余的办法呢?

先看看原始特色的 CDF 函数

如果确保变换函数将原始散布的 (i-1)ᵗʰ 和 iᵗʰ 百分位数之间的点映射到 N(0,1)那会怎么样呢?

g 是咱们正在寻找的变换,Φ 是 N(0,1) 的 CDF

然而这可能只是最终目标只是这种办法的延长。因为咱们的办法不应限度在由百分位数定义的区间,而是想要一个函数,它能够满足下面原始 CDF 公式中的每个区间的要求。于是就失去了上面的公式

如果你对概率论比拟相熟,那么回忆一下概率的特色在于它的散布函数(Jean Jacod 和 Philip Protter 的 Probability Essentials 中的定理 7.1)。我将把本人限度在了枯燥递增函数的空间中。

枯燥递增函数的束缚假如集,如果我能找到一个函数使变换后的特色的 CDF 等于 N(0,1)的 CDF,那不就能够了吗。这与下面公式中的枯燥递增束缚一起,失去了上面的公式。

将函数 g 变换为 Φ 的逆函数和 F 的复合函数

上面看看后果,咱们应用下面总结的后果来转的特色,使其具备规范正态分布。

 fontdict = {'family':'serif', 'color':'darkgreen', 'size':16}
 fig, axs = plt.subplots(1, 1, figsize=(8, 8))
 axs.hist(standard_normal, bins=1_000, density=True, fc=(0,0,1,0.4), label='Standard Normal')
 axs.hist(scipy.stats.norm.ppf(1.5*sample - 0.5*(sample**2)), bins=1000, density=True, fc=(1,0,0,0.4), label='Equation 4 Transform')
 axs.scatter(norm.ppf(1.5*x - 0.5*(x**2)), np.full_like(x, 3e-3), c=x, cmap=cmap)
 
 axs.set_xlim(-5, 5)
 axs.set_title("Transformed Feature's Density", fontdict=fontdict, fontweight='bold', pad=12)
 axs.set_xlabel('$\Phi^{-1}(F$(X))', fontdict=fontdict, fontweight='normal', labelpad=12)
 axs.set_ylabel('Density', fontdict=fontdict, fontweight='normal', labelpad=12)
 
 axs.legend()
 axs.grid()

任何散布(只有它是一个间断散布函数)都能够应用这个办法。然而在应用它之前,还是须要看看用例中应用它是否有意义。

 fontdict = {'family':'serif', 'color':'darkgreen', 'size':16}
 fig, axs = plt.subplots(1, 1, figsize=(8, 8))
 axs.scatter(x, norm.ppf(1.5*x - 0.5*(x**2)), c=x, cmap=cmap)
 
 axs.set_xlim(0, 1)
 axs.set_title('Transform', fontdict=fontdict, fontweight='bold', pad=12)
 axs.set_xlabel('X', fontdict=fontdict, fontweight='normal', labelpad=12)
 axs.set_ylabel('$\Phi^{-1}(F$(X))', fontdict=fontdict, fontweight='normal', labelpad=12)
 axs.grid()

咱们的转函数看起来是这样的,这个过程给出了如图 5 所示的转换。须要留神的是:这个特色取值靠近 0 或靠近 1 时输入稳定大,但当值靠近 0.5 时输入稳定小。如果不是这种状况会给模型提供对特色的谬误解释,可能会侵害其性能。

https://avoid.overfit.cn/post/3774e5c55c1845c0a53ff65a4dfbec55

作者:Jasraj Singh

退出移动版