將特征轉換為正態分布的一種方法示例
統計學領域的很大一部分研究都是假設數據是正態分布的,所以如果我們的數據具有是正態分布,那么則可以獲得更好的結果。但是一般情況下我們的數據都并不是正態分布,所以如果我們能將這些數據轉換成正態分布那么對我們建立模型來說是一件非常有幫助的事情。
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 時輸出波動小。如果不是這種情況會給模型提供對特征的錯誤解釋,可能會損害其性能。
作者:Jasraj Singh
*博客內容為網友個人發布,僅代表博主個人觀點,如有侵權請聯系工作人員刪除。