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