LR模型常见问题小议
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請注明出處。
目錄(?)[+]
// 畢竟不是什么大牛,只是總結(jié)一下自己的一些認識和想法,如果有不正確的,還請大牛們斧正。
經(jīng)常說的2/8原則,LR肯定就是能解決80%問題中那20%的工具。所以LR還是值得好好研究的。發(fā)現(xiàn)以前對LR重視不夠,總想著趕緊把其他算法也學(xué)了,才能拉小跟同事之間機器學(xué)習(xí)的gap。其實LR用得還是挺多的,而且效果還是不錯的。一些高大上的算法,在公司這種大數(shù)據(jù)面前不一定跑得動,即使跑得動,效果也不一定好,而且還有可解釋性和工程維護方面復(fù)雜度的問題。這倒是挺殘酷的現(xiàn)實。
發(fā)現(xiàn)學(xué)完coursera的機器學(xué)習(xí)課程后,離具體實踐還是有不少距離,也沒找到什么好的資料可以學(xué)習(xí)(如果誰發(fā)現(xiàn)有的話,麻煩告訴我一聲吧),耳濡目染了一些奇技淫巧,總結(jié)一下,有一些其實之前的筆記也零散提到了。
數(shù)據(jù)歸一化
仔細區(qū)分的話,有兩種:
反正就是把數(shù)據(jù)縮放到大小差不多,在1左右。這樣起到的作用是加速迭代。根本原因其實是因為你偷懶,沒有為每一個特征單獨設(shè)置一個a。既然用了同一個a,那你也要保證數(shù)據(jù)scale也差不多。
特征離散化&組合
剛開始覺得,機器學(xué)習(xí)公司里有現(xiàn)成的包可以調(diào)用,然后把數(shù)據(jù)灌進去就好了,機器學(xué)習(xí)到底有啥搞頭呢? 后來才搞明白,現(xiàn)實中,機器學(xué)習(xí)里面重要的一環(huán)其實就是搞“特征工程”,如果你對數(shù)據(jù)有足夠的敏銳,能抽取出一些有效的特征,往往比算法本身的優(yōu)化來得有效得多。怎么抽取特征這里就不多說,這里所說常見的特征處理方法:離散化和特征組合。
離散化
離散化就是把數(shù)值型特征離散化到幾個固定的區(qū)間段。比如說成績0-100,離散化成A、B、C、D四檔,然后用4個01特征來one-hot編碼,比如
A為1,0,0,0
B為0,1,0,0
C為0,0,1,0
D為0,0,0,1
那第一位就表示是否為A,第二位表示是否為B……
這里起到的作用就是減少過擬合,畢竟95和96分的兩個學(xué)生能力不見得就一定有差別,但是A的學(xué)生跟D的比起來還是有明顯差別的。其實就是把線性函數(shù)轉(zhuǎn)換成分段階躍函數(shù)了。
另外一種,比如把汽車時速按10公里/小時之類的分一些檔,就像這樣:
0-10
10-20
20-30
……
如果現(xiàn)在我們想學(xué)習(xí)的目標是油耗
這里以某款國內(nèi)比較熱銷的車型做了下面的幾項測試:
120km/h勻速行駛時,油耗為7.81升/100km
90km/h勻速行駛時, 油耗為5.86升/100km
60km/h勻速行駛時, 油耗為4.12升/100km
30km/h勻速行駛時 ,油耗為4.10升/100km
顯然油耗不是線性的,不離散化肯定不行。仔細想想,這樣離散化之后,其實可以近似擬合任意函數(shù)了。
特征組合
特征組合就比較簡單,比如現(xiàn)在有兩個特征A和B,再新增一個A and B的特征。
小結(jié)
LR的劣勢就是線性模型的線性假設(shè)過強,但我們發(fā)現(xiàn)通過上面這些trick,其實也可以學(xué)習(xí)”非線性“的特征,大大增強了LR的能力,所以LR才能這么流行。
高緯度01特征
就是one-hot編碼,上面就用了。
還有一個優(yōu)點沒提到,就是沒有數(shù)據(jù)歸一化(標準化)的問題。
高緯度的特征我們一般不會存一個<特征名,下標值>這樣一個hashmap,而是直接用
哈希(特征名)%特征數(shù)
以前還以為這樣做是因為在算法預(yù)測能力上會有神奇的提升,結(jié)果不是,只是出于工程方便的考慮。
好處只是少維護了一個索引,方便多了。當(dāng)然,哈希沖突是有的,而且這個對算法是有害的,只不過沖突一般比較少,還hold得住。這里有個資料(http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.HashingVectorizer.html#sklearn.feature_extraction.text.HashingVectorizer)不錯,說的是處理文本,其實對LR也一樣。
正負樣本不均衡問題
(注意這里只討論LR,不要亂推廣到其他算法)感覺怎么這問題有點像”玄學(xué)”了,坊間通用的說法是樣本太不均衡是會有問題的,正負樣本比例多大合適? 一些經(jīng)驗主義的說法是頂多負樣本是正樣本的幾(5以內(nèi)吧)倍。
這問題的分析,顯然要從loss function入手。
假設(shè)現(xiàn)在負樣本復(fù)制多一份,從loss function的角度看來,就是負樣本的cost前面乘以2,這個2其實可以當(dāng)做負樣本的權(quán)重。所以,誰樣本多,誰占的權(quán)重其實就大一些。要看看loss function的優(yōu)化目標是不是跟你的目標一致,如果正負樣本對你來說一樣重要,那可以不用管,但通常都不是,所以是個問題。通常情況都是比較少的樣本反而比較重要的,這真是一個大問題。假設(shè)正負樣本是1:1w,你關(guān)心的是正樣本,直接學(xué)出來的模型可能就直接把樣本全判別為負樣本。但這顯然不是你想要的結(jié)果。
像我這樣的懶人,直覺是覺得保持1:1最好,或者說至少沒壞處。那通常采用的方法就是up-sampling或者down-sampling,具體操作方法很簡單,少的樣本復(fù)制多份,或者多的樣本只抽樣一些。但我感覺前者容易過擬合,后者數(shù)據(jù)利用得又不夠充分。難道咸魚與熊掌就不可得兼?后來某大牛指點了一下,告訴我一個簡單的trick:
用down-sampling,然后采樣多次,訓(xùn)練多個模型,跟隨機森林一樣,求個平均即可
這里還有另外一個問題,我們知道LR學(xué)出來是一個概率值,樣本不均衡,我們調(diào)一下閾值不就行了么?比如從原來的0.5調(diào)整到0.3,這樣就會多判斷一些正樣本,唯一的問題就是樣本不均衡時候的分類邊界跟均衡時的邊界平行么?
好吧,作為一個懶惰的“工程學(xué)派”,懶得去推到公式從理論上去證明,還是來做個直觀的實驗吧。
用R語言來做。我知道用R比較簡單,但是R本身我不熟,還要google一番+?<函數(shù)名> 命令,勉強還是把代碼擼出來了。代碼邏輯很簡單,自己定好一個斜率K=5,然后在邊界上下生成一些隨機數(shù),高斯分布,均勻分布都可以。
<code class="language-r hljs has-numbering"> LABEL_1_NUM <- <span class="hljs-number">100</span> LABEL_0_NUM <- <span class="hljs-number">100</span>K <- <span class="hljs-number">5</span>x1 <-runif(LABEL_1_NUM, <span class="hljs-number">0</span>, <span class="hljs-number">10</span>) x2 <-runif(LABEL_0_NUM , <span class="hljs-number">0</span>, <span class="hljs-number">10</span>)D <-<span class="hljs-number">1</span> x2 <- rep(x2,D) Y <- c( x1* K + rnorm(LABEL_1_NUM, mean=<span class="hljs-number">15</span>, sd=<span class="hljs-number">19</span>), x2 * K + rnorm(LABEL_0_NUM, mean=-<span class="hljs-number">15</span>, sd=<span class="hljs-number">19</span>) ) <span class="hljs-comment">#Y <- c( x1* K + runif(LABEL_1_NUM, min=-15, max=50), x2 * K + runif(LABEL_0_NUM, min=-50, max=15) )</span>X <- c( x1, x2 ) label <- as.factor( c(rep(<span class="hljs-number">1</span>,LABEL_1_NUM), rep(<span class="hljs-number">0</span>,LABEL_0_NUM*D) ) )data <- data.frame(X,Y, label)model <- glm(label~Y+X, data=data, family=<span class="hljs-string">'binomial'</span>, control = list(maxit = <span class="hljs-number">600</span>))plot( data$X , data$Y , type=<span class="hljs-string">"p"</span>, pch=<span class="hljs-number">19</span>, col=<span class="hljs-string">"red"</span>) points( data$X[ data$label==<span class="hljs-number">1</span>], data$Y[ data$label==<span class="hljs-number">1</span>], type=<span class="hljs-string">"p"</span>, pch=<span class="hljs-number">19</span>, col=<span class="hljs-string">"blue"</span>) points( data$X[ data$label==<span class="hljs-number">0</span>], data$Y[ data$label==<span class="hljs-number">0</span>], type=<span class="hljs-string">"p"</span>, pch=<span class="hljs-number">19</span>, col=<span class="hljs-string">"red"</span>) co = coef(model) lines(c(<span class="hljs-number">0</span>,<span class="hljs-number">100</span>),c(-co[<span class="hljs-number">1</span>]/co[<span class="hljs-number">2</span>], -(co[<span class="hljs-number">1</span>]+co[<span class="hljs-number">3</span>]*<span class="hljs-number">100</span>)/co[<span class="hljs-number">2</span>]), type=<span class="hljs-string">"l"</span>, col=<span class="hljs-string">'green'</span>, lwd=<span class="hljs-number">3</span>)print(co) <span class="hljs-comment">#截距</span> print(-co[<span class="hljs-number">1</span>]/co[<span class="hljs-number">2</span>]) <span class="hljs-comment">#斜率</span> print(-co[<span class="hljs-number">3</span>]/co[<span class="hljs-number">2</span>]) </code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li></ul><div class="save_code tracking-ad" style="display: none;" data-mod="popu_249"><a target=_blank href="javascript:;" target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li></ul>
正負 100:100
正負 100:1000
有點遺憾,采樣多次取平均,斜率居然都是5左右。后來想想,負樣本多的時候,其實正樣本都不用怎么看了,而負樣本形成的帶狀物,邊緣斜率肯定也是接近5。
改變方法,負樣本生成的時候斜率改成-K,然后標準差調(diào)大一些。這樣分類邊界應(yīng)該是Y=0。
注意,一定要多抽樣幾次。能發(fā)現(xiàn),負樣本多的話,邊界還是會向下偏一些的(至少比1:1的時候。 斜率 (-∞, -1] vs [-1,1] )。這個問題能舉一個反例證明就夠了。注意這里為了方便展示,取的只有2維,高維的就更不好說了。不過這里還發(fā)現(xiàn),邊界向下其實偏離得不會特別大,LR還是有一些容錯能力的,但比賽的話,能提升0.5%都已經(jīng)很不錯了。
總結(jié)
以上是生活随笔為你收集整理的LR模型常见问题小议的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 入门lr
- 下一篇: 真实而震撼:同班同学20年后,身价15亿