將特征轉(zhuǎn)換為正態(tài)分布的一種方法示例
統(tǒng)計(jì)學(xué)領(lǐng)域的很大一部分研究都是假設(shè)數(shù)據(jù)是正態(tài)分布的,所以如果我們的數(shù)據(jù)具有是正態(tài)分布,那么則可以獲得更好的結(jié)果。但是一般情況下我們的數(shù)據(jù)都并不是正態(tài)分布,所以如果我們能將這些數(shù)據(jù)轉(zhuǎn)換成正態(tài)分布那么對(duì)我們建立模型來說是一件非常有幫助的事情。
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()
如果你正在處理一個(gè)密度(大約)呈線性下降的特性(見下圖)。
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()
要將這個(gè)特征轉(zhuǎn)換為具有鐘形分布的變量,可能沒有那么簡單,我如果我使用某種變換將密度最高的左端放到中心,那么中心兩側(cè)的其余點(diǎn)怎么辦?
如果變換是將點(diǎn)從中間和右邊的[0,1]移到均值的任意一邊(N(0,1) =0)那么本質(zhì)上是一個(gè)非單調(diào)的變換,這不是很好因?yàn)槟菢拥脑?,變換后的特征值就沒有什么意義了。雖然我們能夠得到一個(gè)鐘形分布,但是對(duì)轉(zhuǎn)換后的值沒有意義,排序也不再被保留(見下圖3中轉(zhuǎn)換后的特征值的散點(diǎn)圖)。
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()
特征的密度是單調(diào)遞減的。目標(biāo)是使用范圍(-∞,∞)的變換來拉伸和壓縮不同點(diǎn)周圍的[0,1]范圍,并且變換空間中每個(gè)點(diǎn)的密度應(yīng)該是N(0,1)所給出的。所以是不是可以嘗試使用其他的方法呢?
先看看原始特征的CDF函數(shù):
如果確保變換函數(shù)將原始分布的 (i-1)?? 和 i?? 百分位數(shù)之間的點(diǎn)映射到 N( 0,1)那會(huì)怎么樣呢?
g 是我們正在尋找的變換,Φ 是 N(0,1) 的 CDF。
但是這可能只是最終目標(biāo)只是這種方法的延伸。因?yàn)槲覀兊姆椒ú粦?yīng)限制在由百分位數(shù)定義的區(qū)間,而是想要一個(gè)函數(shù),它可以滿足上面原始CDF公式中的每個(gè)區(qū)間的要求。于是就得到了下面的公式:
如果你對(duì)概率論比較熟悉,那么回想一下概率的特征在于它的分布函數(shù)(Jean Jacod 和 Philip Protter 的 Probability Essentials 中的定理 7.1)。我將把自己限制在了單調(diào)遞增函數(shù)的空間中。
單調(diào)遞增函數(shù)的約束假設(shè)集,如果我能找到一個(gè)函數(shù)使變換后的特征的CDF等于N(0,1)的CDF,那不就可以了嗎。這與上面公式中的單調(diào)遞增約束一起,得到了下面的公式。
將函數(shù)g變換為Φ的逆函數(shù)和F的復(fù)合函數(shù)。
下面看看結(jié)果,我們使用上面總結(jié)的結(jié)果來轉(zhuǎn)的特征,使其具有標(biāo)準(zhǔn)正態(tài)分布。
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()
任何分布(只要它是一個(gè)連續(xù)分布函數(shù))都可以使用這個(gè)方法。但是在使用它之前,還是需要看看用例中使用它是否有意義。
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()
我們的轉(zhuǎn)函數(shù)看起來是這樣的,這個(gè)過程給出了如圖5所示的轉(zhuǎn)換。需要注意的是:這個(gè)特征取值接近 0 或接近 1 時(shí)輸出波動(dòng)大,但當(dāng)值接近 0.5 時(shí)輸出波動(dòng)小。如果不是這種情況會(huì)給模型提供對(duì)特征的錯(cuò)誤解釋,可能會(huì)損害其性能。
作者:Jasraj Singh
*博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。