日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

機器學(xué)習(xí)開放課程(四)線性分類與線性回歸

 LibraryPKU 2019-02-11
作者:Yury Kashnitskiy
編譯:weakish

編者按:機器學(xué)習(xí)開放課程第四課,Mail.Ru數(shù)據(jù)科學(xué)家Yury Kashnitsky深入講解線性分類和線性回歸的理論與實踐。

xkcd

譯文:“如你所見,到下個月底,你會有48位丈夫。值得吃一大塊蛋糕慶祝下。”(橫軸為日期:昨天、今天;縱軸為丈夫數(shù)量。)

歡迎參加我們的第4課。這次我們將呈現(xiàn)最重要的主題——線性模型。如果你準備好了數(shù)據(jù),打算開始訓(xùn)練模型,那么你最有可能首先嘗試線性回歸或邏輯回歸(具體取決于你的任務(wù)是回歸還是分類)。

本文包括線性模型的理論和實踐(在實際任務(wù)中的使用)。此外,你將參加一項Kaggle競賽,解決一個基于瀏覽歷史識別用戶的問題。

概覽

  1. 回歸

    • 最小二乘法

    • 最大似然估計

    • 偏置-方差分解

    • 線性回歸正則化

  2. 線性分類

    • 線性分類器

    • 作為線性分類器的邏輯回歸

    • 最大似然估計和邏輯回歸

    • 邏輯損失的L2正則化

  3. 邏輯回歸正則化示例

  4. 邏輯回歸的優(yōu)缺點

    • 分析IMDB影評

    • XOR問題

  5. 驗證和學(xué)習(xí)曲線

    • 模型需要多復(fù)雜?

    • 需要多少數(shù)據(jù)?

  6. 課內(nèi)Kaggle競賽“我知道你是誰”

    • 數(shù)據(jù)下載和轉(zhuǎn)換

    • 稀疏矩陣

    • 訓(xùn)練第一個模型

  7. 作業(yè)四

  8. 相關(guān)資源

1. 回歸

我們的線性模型學(xué)習(xí)從線性回歸開始。首先,需要指定一個模型將因變量y和解釋因素(特征)聯(lián)系起來;對線性模型而言,依賴函數(shù)的形式如下:

如果我們?yōu)槊宽椨^測加上一個虛維度x0 = 1 (偏置),那么上面的線性形式可以改寫為一個略微緊湊的形式:

如果我們有一個特征觀測矩陣,其中矩陣的行是數(shù)據(jù)集中的觀測,那么需要在左邊加上一列。

我們可以定義模型為:

其中:

  • y ∈ ?n 因變量(目標變量);

  • w 模型的參數(shù)向量(在機器學(xué)習(xí)中,這些參數(shù)經(jīng)常被稱為權(quán)重);

  • X 觀測及其特征矩陣,大小為n行、m+1列(包括左側(cè)的虛列),秩:rank(X) = m + 1;

  • ? 對應(yīng)于隨機、不可預(yù)測的模型錯誤的變量。

上述表達式也可以寫成這樣(寫出每項觀察):

模型具有如下限制(否則它就不是線性回歸了):

  • 隨機誤差的期望為零:?i: ??[?i] = 0;

  • 隨機誤差具有相同的有限方差,這一性質(zhì)稱為等分散性:?i: Var(?i) = σ2 < ∞;

  • 隨機誤差不相關(guān):?i≠j: Cov(?i, ?j) = 0.

權(quán)重wi的估計滿足如下條件時,我們稱其為線性:

其中,?k,ωki僅依賴于X中的樣本,而且?guī)缀跻欢ㄒ苑蔷€性的方式。由于尋求最佳權(quán)重的解是一個線性估計,這一模型被稱為線性回歸。讓我們再引入一項定義。當期望值等于真實而未知的估計參數(shù)的值時,權(quán)重估計稱為無偏(unbiased):

計算這些權(quán)重的方法之一是普通最小二乘法(OLS)。OLS最小化因變量的實際值和模型給出的預(yù)測值之間的均方誤差:

為了解決這一優(yōu)化問題,我們需要計算模型參數(shù)的導(dǎo)數(shù)。我們將導(dǎo)數(shù)設(shè)為零,然后求解所得關(guān)于w的等式。

矩陣求導(dǎo)小抄:

讓我們開始計算:

基于上述定義和條件,我們可以說,根據(jù)高斯-馬爾可夫定理,模型參數(shù)的OLS估計是所有線性無偏估計中最優(yōu)的,即,它們給出最低的方差。

最大似然估計

有人可能會問,為何我們選擇最小化均方誤差而不是別的什么?畢竟,我們可以最小化殘差的平均絕對值。如果我們改變了最小化的值,唯一會發(fā)生的事就是我們將超出高斯-馬爾可夫定理的條件,因此我們的估計將不再是最佳的線性無偏估計。

在我們繼續(xù)之前,讓我們稍微離題一下,通過一個簡單的例子講解下最大似然估計。

許多人大概都記得乙醇的化學(xué)式,所以我決定做一個試驗判定人們是否記得簡單的甲醇化學(xué)式:CH3OH. 我們調(diào)查了400人,發(fā)現(xiàn)只有117個人記得甲醇的化學(xué)式。那么,下一個受訪者知道甲醇化學(xué)式的概率為117/400 ≈ 29%會是一個合理的假定。讓我們展示下這個直觀的估計不僅很好,同時也是最大似然估計。估計來自哪里?回憶下伯努利分布的定義:如果一個隨機變量只有兩個值(1和0,相應(yīng)的概率為θ和1 - θ),那么該隨機變量滿足伯努利分布,遵循以下概率分布函數(shù):

這一分布正是我們所需要的,分布參數(shù)θ是一個人知道甲醇化學(xué)式的概率估計。在我們的400個獨立試驗中,讓我們將試驗的結(jié)果記為x = (x1, x2, ..., x400)。讓我們寫下數(shù)據(jù)(觀測)的似然,即正好觀測到117個隨機變量θ = 1的實例和283個隨機變量θ = 0的實例:

接著,我們將最大化這一θ的表達式。最常見的情況是,我們并不最大化似然p(x | θ),轉(zhuǎn)而最大化其對數(shù)(這一單調(diào)變換不影響解答,但大大簡化了計算):

為了找到最大化上式的θ,我們求θ的導(dǎo)數(shù),設(shè)為零,求解所得等式:

結(jié)果發(fā)現(xiàn),我們的直觀估計正好是最大似然估計?,F(xiàn)在讓我們將這一推理過程應(yīng)用到線性回歸問題上,嘗試找出均方誤差背后有什么。為此,我們需要從概率論的角度來看線性回歸。我們的模型和之前是一樣的:

不過,現(xiàn)在讓我們假定隨機誤差符合均值為零的正態(tài)分布:

據(jù)此改寫模型:

由于樣本是獨立抽取的(不相關(guān)誤差是高斯-馬爾可夫定理的條件之一),數(shù)據(jù)的似然看起來會是密度函數(shù)p(yi)的積。讓我們考慮對數(shù)似然,對數(shù)似然允許我們用和替換積:

我們想要找到最大似然假設(shè),即,我們需要最大化表達式p(y ∣ Xw) 以得到wML。這和最大化其對數(shù)是一回事。注意,當我們針對某個參數(shù)最大化函數(shù)時,我們可以丟棄所有不依賴這一參數(shù)的成員:

所以,我們看到了,最大化數(shù)據(jù)的似然和最小化均方誤差是一回事(給定以上的假定)。實際上,這是因為誤差是正態(tài)分布的。

偏置-方差分解

讓我們稍微談下線性回歸預(yù)測的誤差性質(zhì)(實際上,這一討論在所有機器學(xué)習(xí)算法上都成立)。我們已經(jīng)提到:

  • 目標變量的真值是確定性函數(shù)f(x)和隨機誤差?之和:

  • 誤差符合均值為零、方差一致的正態(tài)分布:

  • 目標變量的真值亦為正態(tài)分布:

  • 我們試圖使用一個協(xié)變量線性函數(shù)逼近一個未知的確定性函數(shù)f(x),這一協(xié)變量線性函數(shù),是函數(shù)空間中估計函數(shù)f的一點(具體而言,我們限定函數(shù)空間的線性函數(shù)家族),即具均值和方差的隨機變量。

因此,點x的誤差可分解為:

為了簡明,我們將省略函數(shù)的參數(shù)。讓我們分別考慮每個成員。據(jù)以下公式:

我們很容易就能分解前兩項:

注意:

最后我們來處理和的最后一項?;貞浺幌?,誤差和目標變量相互獨立:

最后,讓我們把這些合并到一起:

我們已經(jīng)達到了我們的終極目標——最后的等式告訴我們,任何線性模型的預(yù)測誤差由三部分組成:

  • 平方偏置

  • 方差

  • 不可消除誤差

盡管我們對σ2無能為力,我們可以影響前兩項。理想情況下,我們希望同時取消這兩項(見下圖中左上),但是,在實踐中,常常需要在偏置和不穩(wěn)定(高方差)間尋找平衡。

一般而言,當模型的計算增加了(例如,自由參數(shù)的數(shù)量增加了),估計的方差(分散程度)也會增加,但偏置會下降。由于模型完全記下了訓(xùn)練集而沒能概括訓(xùn)練集,小小的變動將導(dǎo)致未預(yù)期的結(jié)果(過擬合)。另一方面,如果模型太弱,它將不能夠?qū)W習(xí)模式,導(dǎo)致學(xué)習(xí)偏離正解較遠的不同答案。

高斯-馬爾可夫定理斷言,在線性模型參數(shù)估計問題中,OLS估計是最佳的線性無偏估計。這意味著,如果存在任何無偏線性模型g,我們可以確信

線性回歸正則化

在一些情形下,我們可能會為了穩(wěn)定性(降低模型的方差)特意增加模型的偏置。高斯-馬爾可夫定理的條件之一就是矩陣X是滿秩的。否則,OLS解w = (XTX)-1XTy不存在,因為逆矩陣(XTX)-1不存在。換句話說,矩陣XTX將是奇異矩陣或退化矩陣。這被稱為病態(tài)問題。這類問題必須加以矯正,也就是說,矩陣XTX需要變?yōu)榉峭嘶仃嚮蚍瞧娈惥仃嚕ㄟ@正是這一過程叫做正則化的原因)。我們常常能在這類數(shù)據(jù)中觀察到所謂的多重共線性:當兩個或更多特征高度相關(guān),也就是矩陣X的列之間“幾乎”存在線性依賴。例如,在基于參數(shù)預(yù)測房價這一問題中,屬性“含陽臺面積”和“不含陽臺面積”會有一個“幾乎是”線性的關(guān)系。形式化地說,包含這類數(shù)據(jù)的矩陣XTX是可逆的,但由于多重共線性,一些本征值會接近零。在XTX的逆矩陣中,會出現(xiàn)一些極端巨大的本征值,因為逆矩陣的本征值為1/(λi)。這一本征值的波動會導(dǎo)致模型參數(shù)估計的不穩(wěn)定,即,在訓(xùn)練數(shù)據(jù)中加入一組新的觀測會導(dǎo)致完全不同的解。有一種正則化的方法稱為吉洪諾夫正則化,大致上是在均方誤差中加上一個新成員:

吉洪諾夫矩陣常常表達為單位矩陣乘上一個系數(shù):

在這一情形下,最小化均方誤差問題變?yōu)橐粋€L2正則限定問題。如果我們對新的損失函數(shù)求導(dǎo),設(shè)所得函數(shù)為零,據(jù)w重整等式,我們便得到了這一問題的解:

這類回歸被稱為嶺回歸。嶺為對角矩陣,我們在XTX矩陣上加上這一對角矩陣,以確保我們能得到一個常規(guī)矩陣。

這樣的解降低了分散程度,但增加了偏置,因為參數(shù)的正則向量同時最小化了,這導(dǎo)致解朝零移動。在下圖中,OLS解為白色虛線的交點。藍點表示嶺回歸的不同解。可以看到,通過增加正則化參數(shù)λ,我們使解朝零移動。

2. 線性分類

線性分類器

線性分類器背后的基本思路是,目標分類的值可以被特征空間中的一個超平面分開。如果這可以無誤差地達成,那么訓(xùn)練集被稱為線性可分。

我們之前已經(jīng)了解了線性回歸和普通最小二乘法(OLS)。現(xiàn)在考慮一個二元分類問題,將目標分類記為“+1”(正面樣本)和“-1”(負面樣本)。最簡單的線性分類器可以通過回歸定義:

其中

  • x是特征向量(包括標識);

  • w是線性模型中的權(quán)重向量(偏置為w0);

  • sign(?)是符號函數(shù),返回參數(shù)的符號;

  • a(x)是分類x的分類器。

作為線性分類器的邏輯回歸

邏輯回歸是線性分類器的一個特殊情形,邏輯回歸有一個額外的好處,可以預(yù)測樣本xi為分類“+”的概率p+。

不僅能夠預(yù)測一個回應(yīng)(“+1”或“-1”),還能預(yù)測相應(yīng)的概率,對于很多業(yè)務(wù)問題(比如,信用評分,這一問題傳統(tǒng)上使用邏輯回歸)而言,這是一個非常重要的需求。

銀行選擇一個閾值p*以預(yù)測貸款違約的概率(上圖中閾值為0.15),超過閾值就不批準貸款。此外,還可以將預(yù)測概率乘以未償還金額,以得到客戶造成的期望損失,這可以構(gòu)成有用的業(yè)務(wù)指標。

為了預(yù)測概率p+ ∈ [0, 1],我們可以從使用OLS構(gòu)造線性預(yù)測開始:

不過,為了將所得結(jié)果轉(zhuǎn)換為[0, 1]區(qū)間內(nèi)的概率,我們需要某個函數(shù)

邏輯回歸使用如下函數(shù):

  1. %matplotlib inline

  2. from matplotlib import pyplot as plt

  3. import seaborn as sns

  4. import numpy as np

  5. def sigma(z):

  6.    return 1. / (1 + np.exp(-z))

  7. xx = np.linspace(-10, 10, 1000)

  8. plt.plot(xx, [sigma(x) for x in xx]);

  9. plt.xlabel('z');

  10. plt.ylabel('sigmoid(z)')

  11. plt.title('Sigmoid function');

我們將事件X的概率記為P(X),則比值比OR(X)由下式判定P(X)/(1-P(X)),這是某一事件是否發(fā)生的概率之比。顯然,概率和比值比包含同樣的信息,不過P(X)的范圍是0到1,而OR(X)的范圍是0到∞。

如果我們計算OR(X)的對數(shù),那么顯然我們有l(wèi)og OR(X) ∈ ?. 我們在OLS中將用到這個。

讓我們看看邏輯回歸是如何做出預(yù)測的:

目前而言,讓我們假設(shè)我們已經(jīng)通過某種方式得到了權(quán)重w,即,模型已經(jīng)訓(xùn)練好了。之后我們將看下這是如何做的。

步驟一 計算

等式WTX = 0定義了將樣本分為兩類的超空間。

步驟二 計算對數(shù)比值比:

步驟三 現(xiàn)在我們已經(jīng)有了將一個樣本分配到“+”分類的概率OR+,我們可以據(jù)此計算p+

我們看到,上式的右邊我們得到了sigmoid函數(shù)。

所以,邏輯回歸預(yù)測將一個樣本分配為“+”分類的概率(假定我們已知模型的特征和權(quán)重),這一過程是通過對權(quán)重向量和特征向量的線性組合進行sigmoid變換完成的:

下面我們將看下模型是如何訓(xùn)練的。我們將再次依靠最大似然估計。

最大似然估計和邏輯回歸

現(xiàn)在,讓我們看下從MLE出發(fā)如何進行邏輯回歸優(yōu)化,也就是最小化邏輯損失函數(shù)。我們前面已經(jīng)見過了將樣本分配為“+”分類的邏輯回歸模型:

“-”分類相應(yīng)的表達式為:

這兩個表達式可以組合成一個:

表達式M(xi) = yiwTxi稱為目標xi的分類邊緣。如果邊緣非負,則模型正確選擇了目標xi的分類;如果邊緣為負,則目標xi被錯誤分類了。注意,邊緣僅針對訓(xùn)練集中的目標(真實目標分類標簽yi已知的目標)而言。

為了精確地理解我們?yōu)楹蔚贸鲞@一結(jié)論,讓我們轉(zhuǎn)向線性分類器的幾何解釋。

首先,我會建議看下線性代數(shù)的一個經(jīng)典入門問題:找出向徑xA與平面wTx = 0的距離。

答案:

從答案中,我們可以看到,表達式wTxi的絕對值越大,點xi離平面wTx = 0的距離就越遠。

因此,表達式M(xi) = yiwTxi是模型對目標xi分類的“信心”:

  • 如果邊緣的絕對值較大,且為正值,那么分類的標簽是正確的,且目標離分界超平面很遠,也就是模型對分類很自信。如下圖點x3所示;

  • 如果邊緣的絕對值較大,且為負值,那么分類的標簽是錯誤的,且目標離分界超平面很遠(目標很可能是一個異常值;例如,它可能是訓(xùn)練集中一個錯誤標記的值)。如下圖點x1所示;

  • 如果邊緣的絕對值較小,那么目標距離分界超平面很近,邊緣的符號決定目標是否被正確分類了。如下圖點x2和x4所示。

現(xiàn)在讓我們計算數(shù)據(jù)集的似然,即基于數(shù)據(jù)集X觀測到給定向量y的概率。我們將做一個強假設(shè):目標來自一個獨立分布(i.i.d.)。

其中,?為數(shù)據(jù)集X的長度(行數(shù))。

像我們經(jīng)常干的那樣,對這個表達式取對數(shù),因為和要比積容易優(yōu)化得多:

最大化似然等價于最小化以下表達式:

這就是邏輯損失函數(shù)。

用邊緣改寫邏輯損失函數(shù),我們有:

我們將這一函數(shù)的圖像和0-1損失函數(shù)的圖像繪制在一張圖上。0-1損失函數(shù)簡單地懲罰模型誤差(邊緣為負)為1:

上圖體現(xiàn)了這樣一個想法,如果我們不能夠直接最小化分類問題的誤差數(shù)量(至少無法通過梯度方法最小化——0-1損失函數(shù)在零處的導(dǎo)數(shù)趨向無窮),我們可以最小化它的上界。對邏輯損失函數(shù)而言,以下是成立的:

我們希望能通過降低分類誤差數(shù)的上界,降低分類誤差數(shù)本身。

邏輯損失的L2正則化

邏輯回歸的L2正則化和嶺回歸的情況基本一樣。我們轉(zhuǎn)而最小化下式:

在邏輯回歸中,通常使用正則化系數(shù)的倒數(shù)C = 1/λ:

下面我們將通過一個例子直觀地理解正則化。

3. 邏輯回歸正則化示例

讓我們看下正則化是如何影響分類的質(zhì)量的(數(shù)據(jù)集為吳恩達機器學(xué)習(xí)課程中的微芯片測試)。我們將使用基于多項式特征的邏輯回歸,然后改變正則化參數(shù)C. 首先,我們將看看正則化是如何影響分類器的分界的,并直觀地識別欠擬合和過擬合。接著,我們將通過交叉驗證和網(wǎng)格搜索選擇數(shù)值上接近最優(yōu)值的正則化參數(shù)。

  1. # 關(guān)閉警告

  2. # 如果你喜歡開著警告,可以注釋掉下面兩行

  3. import warnings

  4. warnings.filterwarnings('ignore')

  5. %matplotlib inline

  6. from matplotlib import pyplot as plt

  7. import seaborn as sns

  8. import numpy as np

  9. import pandas as pd

  10. from sklearn.preprocessing import PolynomialFeatures

  11. from sklearn.linear_model import LogisticRegression, LogisticRegressionCV

  12. from sklearn.model_selection import cross_val_score, StratifiedKFold

  13. from sklearn.model_selection import GridSearchCV

讓我們使用pandas庫的read_csv方法加載數(shù)據(jù)。這個數(shù)據(jù)集內(nèi)有118個微芯片(目標),其中有兩項質(zhì)量控制測試的結(jié)果(兩個數(shù)值變量)和微芯片是否投產(chǎn)的信息。變量已經(jīng)居中了,也就是列中的值已經(jīng)減去其均值。所以,“平均”微芯片的測試值為零。

  1. # 加載數(shù)據(jù)

  2. data = pd.read_csv('../../data/microchip_tests.txt',

  3.                   header=None, names = ('test1','test2','released'))

  4. # 了解數(shù)據(jù)集的基本信息

  5. data.info()

  1. <class 'pandas.core.frame.DataFrame'>

  2. RangeIndex: 118 entries, 0 to 117

  3. Data columns (total 3 columns):

  4. test1       118 non-null float64

  5. test2       118 non-null float64

  6. released    118 non-null int64

  7. dtypes: float64(2), int64(1)

  8. memory usage: 2.8 KB

讓我們看下開始五行和最后五行:

  1. data.head(5)

  1. data.tail(5)

分離訓(xùn)練集和目標分類標簽:

  1. X = data.iloc[:,:2].values

  2. y = data.iloc[:,2].values

繪制數(shù)據(jù),橙點對應(yīng)有缺陷的芯片,藍點對應(yīng)正常芯片。

  1. plt.scatter(X[y == 1, 0], X[y == 1, 1], c='blue', label='Released')

  2. plt.scatter(X[y == 0, 0], X[y == 0, 1], c='orange', label='Faulty')

  3. plt.xlabel('Test 1')

  4. plt.ylabel('Test 2')

  5. plt.title('2 tests of microchips. Logit with C=1')

  6. plt.legend();

定義一個顯示分類器的分界曲線的函數(shù)。

  1. def plot_boundary(clf, X, y, grid_step=.01, poly_featurizer=None):

  2.    x_min, x_max = X[:, 0].min() - .1, X[:, 0].max() + .1

  3.    y_min, y_max = X[:, 1].min() - .1, X[:, 1].max() + .1

  4.    xx, yy = np.meshgrid(np.arange(x_min, x_max, grid_step),

  5.                         np.arange(y_min, y_max, grid_step))

  6.    Z = clf.predict(poly_featurizer.transform(np.c_[xx.ravel(), yy.ravel()]))

  7.    Z = Z.reshape(xx.shape)

  8.    plt.contour(xx, yy, Z, cmap=plt.cm.Paired)

我們?yōu)閮蓚€變量x1和x2定義如下多形式特征:

例如,d = 3時的特征如下:

特征的數(shù)量呈指數(shù)型增長,為100個變量創(chuàng)建d較大(例如d = 10)的多項式特征成本很高。更重要的是,不需要如此。

我們將使用sklearn的邏輯回歸實現(xiàn)。我們將創(chuàng)建一個對象,為矩陣X加上多項式特征(d不超過7)。

  1. poly = PolynomialFeatures(degree=7)

  2. X_poly = poly.fit_transform(X)

  3. X_poly.shape

結(jié)果:

  1. (118, 36)

讓我們訓(xùn)練邏輯回歸,正則化系數(shù)C = 10-2。

  1. C = 1e-2

  2. logit = LogisticRegression(C=C, random_state=17)

  3. logit.fit(X_poly, y)

  4. plot_boundary(logit, X, y, grid_step=.01, poly_featurizer=poly)

  5. plt.scatter(X[y == 1, 0], X[y == 1, 1], c='blue', label='Released')

  6. plt.scatter(X[y == 0, 0], X[y == 0, 1], c='orange', label='Faulty')

  7. plt.xlabel('Test 1')

  8. plt.ylabel('Test 2')

  9. plt.title('2 tests of microchips. Logit with C=%s' % C)

  10. plt.legend();

  11. print('Accuracy on training set:',

  12.      round(logit.score(X_poly, y), 3))

  1. Accuracy on training set: 0.627

我們可以嘗試將C增加到1,也就是說,我們削弱了正則化,現(xiàn)在的模型權(quán)重可以比之前有更大的值(絕對值更大)。這使得分類器在訓(xùn)練集上的精確度改善了(提高到0.831)。

  1. C = 1

  2. logit = LogisticRegression(C=C, random_state=17)

  3. logit.fit(X_poly, y)

  4. plot_boundary(logit, X, y, grid_step=.005, poly_featurizer=poly)

  5. plt.scatter(X[y == 1, 0], X[y == 1, 1], c='blue', label='Released')

  6. plt.scatter(X[y == 0, 0], X[y == 0, 1], c='orange', label='Faulty')

  7. plt.xlabel('Test 1')

  8. plt.ylabel('Test 2')

  9. plt.title('2 tests of microchips. Logit with C=%s' % C)

  10. plt.legend();

  11. print('Accuracy on training set:',

  12.      round(logit.score(X_poly, y), 3))

  1. Accuracy on training set: 0.831

我們?yōu)槭裁床唤又M一步增加C呢?比如,將C增加到10000?這回,很明顯正則化不夠強,我們看到了過擬合。注意,C = 1時,“平滑”邊界對應(yīng)的訓(xùn)練集上的正確解答并沒有低多少。但我們很容易可以想像得到,我們之前的模型將在新數(shù)據(jù)上工作得更好。

  1. C = 1e4

  2. logit = LogisticRegression(C=C, random_state=17)

  3. logit.fit(X_poly, y)

  4. plot_boundary(logit, X, y, grid_step=.005, poly_featurizer=poly)

  5. plt.scatter(X[y == 1, 0], X[y == 1, 1], c='blue', label='Released')

  6. plt.scatter(X[y == 0, 0], X[y == 0, 1], c='orange', label='Faulty')

  7. plt.xlabel('Test 1')

  8. plt.ylabel('Test 2')

  9. plt.title('2 tests of microchips. Logit with C=%s' % C)

  10. plt.legend();

  11. print('Accuracy on training set:',

  12.      round(logit.score(X_poly, y), 3))

  1. Accuracy on training set: 0.873

為了討論以上這些結(jié)果,讓我們改寫一下邏輯回歸優(yōu)化的函數(shù):

部分和:

  • 參數(shù)C越大,模型可恢復(fù)的數(shù)據(jù)中的關(guān)系就越復(fù)雜(直觀地說,C對應(yīng)模型的“復(fù)雜度”——模型能力)。

  • 如果正則化過強,即C值很小,最小化邏輯損失函數(shù)問題的解可能是一個許多權(quán)重過小或為零的解。這樣的模型對誤差的“懲罰”也不夠(即,在函數(shù)J中,權(quán)重的平方和“權(quán)重過高”,誤差L可能相對很大)。在這一情形下,模型將會欠擬合,如我們在第一個情形下所看到的那樣。

  • 相反,如果正則化過弱,即C值很大,由絕對值很大的分量組成的向量w可能變成優(yōu)化問題的解。在這一情形下,L對優(yōu)化的函數(shù)J貢獻較大。大致上,這樣的模型對在訓(xùn)練集的目標上犯錯過于“恐懼”,因而會過擬合,如我們在第三個情形下所看到的那樣。

  • 邏輯回歸不會“理解”(或“學(xué)習(xí)”)選擇C的值,這和權(quán)重w的情況不一樣。這就是說,C的值無法通過解決邏輯回歸的優(yōu)化問題而確定。我們之前碰到過類似的情況——決策樹無法在訓(xùn)練過程中“學(xué)習(xí)”選擇深度限制。因此,C是模型的超參數(shù),通過交叉驗證調(diào)節(jié);決策樹的max_depth同理。

正則化參數(shù)調(diào)整

讓我們確定上述例子中正則化參數(shù)C的最優(yōu)值。我們可以使用LogisticRegressionCV——網(wǎng)格搜索參數(shù)后進行交叉驗證。這個類是專門為邏輯回歸設(shè)計的。對任意模型而言,使用GridSearchCVRandomizedSearchCV,或者特殊的超參數(shù)優(yōu)化算法,比如hyperopt中實現(xiàn)的算法。

  1. skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=17)

  2. c_values = np.logspace(-2, 3, 500)

  3. logit_searcher = LogisticRegressionCV(Cs=c_values, cv=skf, verbose=1, n_jobs=-1)

  4. logit_searcher.fit(X_poly, y)

  5. logit_searcher.C_

結(jié)果:

  1. array([198.8827857])

讓我們看下超參數(shù)C是如何影響模型的質(zhì)量的:

  1. plt.plot(c_values, np.mean(logit_searcher.scores_[1], axis=0))

  2. plt.xlabel('C')

  3. plt.ylabel('Mean CV-accuracy');

最后,選擇C值“最佳”(C值較大加重算力負擔,也容易導(dǎo)致過擬合)的區(qū)域:

  1. plt.plot(c_values, np.mean(logit_searcher.scores_[1], axis=0))

  2. plt.xlabel('C')

  3. plt.ylabel('Mean CV-accuracy');

  4. plt.xlim((0,10));

回憶一下,這些曲線被稱為驗證曲線。之前我們手工創(chuàng)建了驗證曲線,不過sklearn有構(gòu)建這些曲線的特殊方法,我們以后將直接使用sklearn的相應(yīng)方法。

4. 邏輯回歸的優(yōu)缺點

分析IMDB影評

現(xiàn)在讓我們做個小練習(xí)!我們想要解決IMDB影評的二元分類問題。我們有一個訓(xùn)練集,其中包含標記好的影評,12500條好評,12500條差評。直接開始機器學(xué)習(xí)并不容易,因為我們并沒有矩陣X;我們需要準備它。我們將使用一個簡單方法:詞袋模型。影評的特征將由整個語料庫中的每個詞的出現(xiàn)情況表示。語料庫是所有用戶影評。下圖展示了這一思路:

  1. %matplotlib inline

  2. import seaborn as sns

  3. import numpy as np

  4. from sklearn.datasets import load_files

  5. from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer, TfidfVectorizer

  6. from sklearn.linear_model import LogisticRegression

  7. from sklearn.svm import LinearSVC

數(shù)據(jù)集可通過以下地址下載:

http://ai./~amaas/data/sentiment/aclImdb_v1.tar.gz

數(shù)據(jù)集的簡要描述: ai./~amaas/data/sentiment/

  1. # 請修改文件路徑

  2. reviews_train = load_files('/Users/y.kashnitsky/Yandex.Disk.localized/ML/data/aclImdb/train')

  3. text_train, y_train = reviews_train.data, reviews_train.target

看看訓(xùn)練集和測試集中各有多少條數(shù)據(jù):

  1. print('Number of documents in training data: %d' % len(text_train))

  2. print(np.bincount(y_train))

  1. Number of documents in training data: 25000

  2. [12500 12500]

  1. # 請修改文件路徑

  2. reviews_test = load_files('/Users/y.kashnitsky/Yandex.Disk.localized/ML/data/aclImdb/test')

  3. text_test, y_test = reviews_test.data, reviews_test.target

  4. print('Number of documents in test data: %d' % len(text_test))

  5. print(np.bincount(y_test))

  1. Number of documents in test data: 25000

  2. [12500 12500]

下面是影評的一些例子。

  1. print(text_train[1])

  1. b'Words can\'t describe how bad this movie is. I can\'t explain it by writing only. You have too see it for yourself to get at grip of how horrible a movie really can be. Not that I recommend you to do that. There are so many clich\xc3\xa9s, mistakes (and all other negative things you can imagine) here that will just make you cry. To start with the technical first, there are a LOT of mistakes regarding the airplane. I won\'t list them here, but just mention the coloring of the plane. They didn\'t even manage to show an airliner in the colors of a fictional airline, but instead used a 747 painted in the original Boeing livery. Very bad. The plot is stupid and has been done many times before, only much, much better. There are so many ridiculous moments here that i lost count of it really early. Also, I was on the bad guys\' side all the time in the movie, because the good guys were so stupid. 'Executive Decision' should without a doubt be you\'re choice over this one, even the 'Turbulence'-movies are better. In fact, every other movie in the world is better than this one.'

上面這條影評是差評還是好評?

  1. y_train[1]

  1. 0

沒錯,和我們預(yù)料的一樣,這是一條差評。

  1. text_train[2]

  1. b'Everyone plays their part pretty well in this 'little nice movie'. Belushi gets the chance to live part of his life differently, but ends up realizing that what he had was going to be just as good or maybe even better. The movie shows us that we ought to take advantage of the opportunities we have, not the ones we do not or cannot have. If U can get this movie on video for around $10, it\xc2\xb4d be an investment!'

這條呢?

  1. y_train[2]

  1. 1

是好評。

單詞簡單計數(shù)

首先,我們使用CountVectorizer創(chuàng)建包含所有單詞的字典。

  1. cv = CountVectorizer()

  2. cv.fit(text_train)

  3. len(cv.vocabulary_)

  1. 74849

如果你查看下“單詞”的樣本(我們還是稱作token吧),你會發(fā)現(xiàn)我們省略了文本處理的許多重要步驟(自動化文本處理自身就可以成為一個獨立的文章系列)。

  1. print(cv.get_feature_names()[:50])

  2. print(cv.get_feature_names()[50000:50050])

  1. ['00', '000', '0000000000001', '00001', '00015', '000s', '001', '003830', '006', '007', '0079', '0080', '0083', '0093638', '00am', '00pm', '00s', '01', '01pm', '02', '020410', '029', '03', '04', '041', '05', '050', '06', '06th', '07', '08', '087', '089', '08th', '09', '0f', '0ne', '0r', '0s', '10', '100', '1000', '1000000', '10000000000000', '1000lb', '1000s', '1001', '100b', '100k', '100m']

  2. ['pincher', 'pinchers', 'pinches', 'pinching', 'pinchot', 'pinciotti', 'pine', 'pineal', 'pineapple', 'pineapples', 'pines', 'pinet', 'pinetrees', 'pineyro', 'pinfall', 'pinfold', 'ping', 'pingo', 'pinhead', 'pinheads', 'pinho', 'pining', 'pinjar', 'pink', 'pinkerton', 'pinkett', 'pinkie', 'pinkins', 'pinkish', 'pinko', 'pinks', 'pinku', 'pinkus', 'pinky', 'pinnacle', 'pinnacles', 'pinned', 'pinning', 'pinnings', 'pinnochio', 'pinnocioesque', 'pino', 'pinocchio', 'pinochet', 'pinochets', 'pinoy', 'pinpoint', 'pinpoints', 'pins', 'pinsent']

接著,我們將使用單詞的索引編碼訓(xùn)練集文本的句子。我們將使用稀疏矩陣。

  1. X_train = cv.transform(text_train)

  2. X_train

  1. <25000x74849 sparse matrix of type '<class 'numpy.int64'>'

  2.    with 3445861 stored elements in Compressed Sparse Row format>

讓我們看下這一轉(zhuǎn)換是如何進行的。

  1. print(text_train[19726])

  1. b'This movie is terrible but it has some good effects.'

  1. X_train[19726].nonzero()[1]

  1. array([ 9881, 21020, 28068, 29999, 34585, 34683, 44147, 61617, 66150, 66562], dtype=int32)

接下來,我們對測試集應(yīng)用同樣的操作。

  1. X_test = cv.transform(text_test)

下一步是訓(xùn)練邏輯回歸。

  1. %%time

  2. logit = LogisticRegression(n_jobs=-1, random_state=7)

  3. logit.fit(X_train, y_train)

  1. CPU times: user 40.9 s, sys: 524 ms, total: 41.4 s

  2. Wall time: 10.5 s

讓我們看下訓(xùn)練集和測試集上的精確度。

  1. round(logit.score(X_train, y_train), 3), round(logit.score(X_test, y_test), 3)

  1. (0.998, 0.86699999999999999)

模型的系數(shù)可以美觀地顯示。

  1. def visualize_coefficients(classifier, feature_names, n_top_features=25):

  2.    # 獲取絕對值較大的系數(shù)

  3.    coef = classifier.coef_.ravel()

  4.    positive_coefficients = np.argsort(coef)[-n_top_features:]

  5.    negative_coefficients = np.argsort(coef)[:n_top_features]

  6.    interesting_coefficients = np.hstack([negative_coefficients, positive_coefficients])

  7.    # 繪圖

  8.    plt.figure(figsize=(15, 5))

  9.    colors = ['red' if c < 0 else 'blue' for c in coef[interesting_coefficients]]

  10.    plt.bar(np.arange(2 * n_top_features), coef[interesting_coefficients], color=colors)

  11.    feature_names = np.array(feature_names)

  12.    plt.xticks(np.arange(1, 1 + 2 * n_top_features), feature_names[interesting_coefficients], rotation=60, ha='right');

  13. def plot_grid_scores(grid, param_name):

  14.    plt.plot(grid.param_grid[param_name], grid.cv_results_['mean_train_score'],

  15.        color='green', label='train')

  16.    plt.plot(grid.param_grid[param_name], grid.cv_results_['mean_test_score'],

  17.        color='red', label='test')

  18.    plt.legend();

  19. visualize_coefficients(logit, cv.get_feature_names())

為了讓我們的模型更好,我們可以優(yōu)化邏輯回歸的正則化系數(shù)。我們將使用sklearn.pipeline,因為CountVectorizer只應(yīng)該應(yīng)用于訓(xùn)練數(shù)據(jù)(為了避免“偷窺”測試集和在測試集上計算詞頻)。在這一情形下,pipeline確定正確的行動序列:應(yīng)用CountVectorizer,然后訓(xùn)練邏輯回歸。

  1. %%time

  2. from sklearn.pipeline import make_pipeline

  3. text_pipe_logit = make_pipeline(CountVectorizer(),

  4.                                LogisticRegression(n_jobs=-1, random_state=7))

  5. text_pipe_logit.fit(text_train, y_train)

  6. print(text_pipe_logit.score(text_test, y_test))

  1. 0.86672

  2. CPU times: user 49.9 s, sys: 571 ms, total: 50.5 s

  3. Wall time: 20.5 s

  1. %%time

  2. from sklearn.model_selection import GridSearchCV

  3. param_grid_logit = {'logisticregression__C': np.logspace(-5, 0, 6)}

  4. grid_logit = GridSearchCV(text_pipe_logit, param_grid_logit, cv=3, n_jobs=-1)

  5. grid_logit.fit(text_train, y_train)

  1. CPU times: user 26.7 s, sys: 1.33 s, total: 28.1 s

  2. Wall time: 1min 16s

讓我們查看下最佳C,以及相應(yīng)的交叉驗證評分:

  1. grid_logit.best_params_, grid_logit.best_score_

  1. ({'logisticregression__C': 0.10000000000000001}, 0.88527999999999996)

  1. plot_grid_scores(grid_logit, 'logisticregression__C')

驗證集上的結(jié)果:

  1. grid_logit.score(text_test, y_test)

  1. 0.87907999999999997

現(xiàn)在讓我們用隨機森林來分類。我們看到,邏輯回歸事半功倍。

  1. from sklearn.ensemble import RandomForestClassifier

  2. forest = RandomForestClassifier(n_estimators=200, n_jobs=-1, random_state=17)

  3. %%time

  4. forest.fit(X_train, y_train)

  1. CPU times: user 3min 39s, sys: 1.2 s, total: 3min 40s

  2. Wall time: 30.7 s

  1. RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',

  2.            max_depth=None, max_features='auto', max_leaf_nodes=None,

  3.            min_impurity_split=1e-07, min_samples_leaf=1,

  4.            min_samples_split=2, min_weight_fraction_leaf=0.0,

  5.            n_estimators=200, n_jobs=-1, oob_score=False, random_state=17,

  6.            verbose=0, warm_start=False)

  1. round(forest.score(X_test, y_test), 3)

  1. 0.85499999999999998

XOR問題

現(xiàn)在讓我們看一個線性模型表現(xiàn)不佳的例子。

線性分類定義的是一個非常簡單的分界平面——一個超平面。最著名的超平面(或直線)無法無誤差地切分的玩具例子是XOR問題。

XOR即異或,其真值表如下:

XOR是一個簡單的二元分類問題,其中兩個分類呈對角交叉分布。

  1. # 創(chuàng)建數(shù)據(jù)集

  2. rng = np.random.RandomState(0)

  3. X = rng.randn(200, 2)

  4. y = np.logical_xor(X[:, 0] > 0, X[:, 1] > 0)

  5. plt.scatter(X[:, 0], X[:, 1], s=30, c=y, cmap=plt.cm.Paired);

顯然,我們無法劃出一條直線無誤差地將兩個分類分開。因此,邏輯回歸在這一任務(wù)上的表現(xiàn)很差。

  1. def plot_boundary(clf, X, y, plot_title):

  2.    xx, yy = np.meshgrid(np.linspace(-3, 3, 50),

  3.                     np.linspace(-3, 3, 50))

  4.    clf.fit(X, y)

  5.    # 為網(wǎng)格上的每個數(shù)據(jù)點繪制判定函數(shù)

  6.    Z = clf.predict_proba(np.vstack((xx.ravel(), yy.ravel())).T)[:, 1]

  7.    Z = Z.reshape(xx.shape)

  8.    image = plt.imshow(Z, interpolation='nearest',

  9.                           extent=(xx.min(), xx.max(), yy.min(), yy.max()),

  10.                           aspect='auto', origin='lower', cmap=plt.cm.PuOr_r)

  11.    contours = plt.contour(xx, yy, Z, levels=[0], linewidths=2,

  12.                               linetypes='--')

  13.    plt.scatter(X[:, 0], X[:, 1], s=30, c=y, cmap=plt.cm.Paired)

  14.    plt.xticks(())

  15.    plt.yticks(())

  16.    plt.xlabel(r'$x_1$')

  17.    plt.ylabel(r'$x_2$')

  18.    plt.axis([-3, 3, -3, 3])

  19.    plt.colorbar(image)

  20.    plt.title(plot_title, fontsize=12);

  21. plot_boundary(LogisticRegression(), X, y,

  22.              'Logistic Regression, XOR problem')

然而,如果我們給出多項式特征作為輸入(這里d = 2),那么問題解決了。

  1. from sklearn.preprocessing import PolynomialFeatures

  2. from sklearn.pipeline import Pipeline

  3. logit_pipe = Pipeline([('poly', PolynomialFeatures(degree=2)),

  4.                       ('logit', LogisticRegression())])

  5. plot_boundary(logit_pipe, X, y,

  6.              'Logistic Regression + quadratic features. XOR problem')

這里,邏輯回歸仍然生成了一個超平面,不過是6維特征空間(1、x1、x2、x12、x1x2、x22)中的超平面。當我們將這個超平面投影到原特征空間(x1、x2)時,分界是非線性的。

在實踐中,多項式特征確實有幫助,不過顯式創(chuàng)建它們在算力上是低效的。使用核技巧的SVM要快很多。在這一方法中,只計算高維空間中目標之間的距離(由核函數(shù)定義),而不用生成大量特征組合。

5. 驗證和學(xué)習(xí)曲線

現(xiàn)在,我們對模型驗證、交叉驗證、正則化已經(jīng)有所了解,讓我們考慮一個更大的問題:

如果模型的質(zhì)量不盡人意,該怎么辦?

  • 我們應(yīng)該讓模型更復(fù)雜還是更簡單?

  • 我們應(yīng)該加入更多特征嗎?

  • 我們是否只是需要更多數(shù)據(jù)用于訓(xùn)練?

這些問題的答案并不明顯。特別是,有時候一個更復(fù)雜的模型會導(dǎo)致表現(xiàn)退化。另一些時候,增加新的觀測并不會帶來可以觀察得到的變化。事實上,做出正確決定,選擇正確方法,從而改進模型的能力區(qū)分了優(yōu)秀的專業(yè)人員和糟糕的專業(yè)人員。

讓我們回頭看看電信運營商離網(wǎng)率數(shù)據(jù)集。

  1. %matplotlib inline

  2. from matplotlib import pyplot as plt

  3. import seaborn as sns

  4. import numpy as np

  5. import pandas as pd

  6. from sklearn.preprocessing import PolynomialFeatures

  7. from sklearn.pipeline import Pipeline

  8. from sklearn.preprocessing import StandardScaler

  9. from sklearn.linear_model import LogisticRegression, LogisticRegressionCV, SGDClassifier

  10. from sklearn.model_selection import validation_curve

  11. data = pd.read_csv('../../data/telecom_churn.csv').drop('State', axis=1)

  12. data['International plan'] = data['International plan'].map({'Yes': 1, 'No': 0})

  13. data['Voice mail plan'] = data['Voice mail plan'].map({'Yes': 1, 'No': 0})

  14. y = data['Churn'].astype('int').values

  15. X = data.drop('Churn', axis=1).values

我們將使用隨機梯度下降訓(xùn)練邏輯回歸。我們將在之后的課程專門討論梯度下降。

  1. alphas = np.logspace(-2, 0, 20)

  2. sgd_logit = SGDClassifier(loss='log', n_jobs=-1, random_state=17)

  3. logit_pipe = Pipeline([('scaler', StandardScaler()), ('poly', PolynomialFeatures(degree=2)),

  4.                       ('sgd_logit', sgd_logit)])

  5. val_train, val_test = validation_curve(logit_pipe, X, y,

  6.                                       'sgd_logit__alpha', alphas, cv=5,

  7.                                       scoring='roc_auc')

我們將繪制ROC-AUC曲線,查看下不同正則化參數(shù)導(dǎo)致的模型在訓(xùn)練集和參數(shù)集上的不同表現(xiàn)。

趨勢相當明顯,這樣的趨勢在其他問題中也同樣常見:

  • 簡單模型的訓(xùn)練誤差和驗證誤差很接近,都比較大。這暗示模型欠擬合,參數(shù)數(shù)量不夠。

  • 高度復(fù)雜模型的訓(xùn)練誤差和驗證誤差差別很大。這可以解釋為過擬合。當參數(shù)數(shù)量過多或者正則化不夠嚴格時,算法可能被數(shù)據(jù)中的噪聲“轉(zhuǎn)移注意力”,沒能把握數(shù)據(jù)的整體趨勢。

需要多少數(shù)據(jù)?

數(shù)據(jù)是越多越好。但我們?nèi)绾卫斫庑聰?shù)據(jù)是否在任何情況下都有幫助呢?例如,如何評估花費N來加倍數(shù)據(jù)集是否合理呢?

由于新數(shù)據(jù)可能無法取得,合理的做法是改變訓(xùn)練集的大小,然后看解答的質(zhì)量如何依賴于訓(xùn)練數(shù)據(jù)的數(shù)量。這樣我們就得到了學(xué)習(xí)曲線。

這個想法很簡單:我們將誤差顯示為訓(xùn)練中使用的樣本數(shù)的函數(shù)。模型的參數(shù)事先固定。

  1. from sklearn.model_selection import learning_curve

  2. def plot_learning_curve(degree=2, alpha=0.01):

  3.    train_sizes = np.linspace(0.05, 1, 20)

  4.    logit_pipe = Pipeline([('scaler', StandardScaler()), ('poly', PolynomialFeatures(degree=degree)),

  5.                           ('sgd_logit', SGDClassifier(n_jobs=-1, random_state=17, alpha=alpha))])

  6.    N_train, val_train, val_test = learning_curve(logit_pipe,

  7.                                                  X, y, train_sizes=train_sizes, cv=5,

  8.                                                  scoring='roc_auc')

  9.    plot_with_err(N_train, val_train, label='training scores')

  10.    plot_with_err(N_train, val_test, label='validation scores')

  11.    plt.xlabel('Training Set Size'); plt.ylabel('AUC')

  12.    plt.legend()

讓我們看看線性模型的結(jié)果。我們將把正則化系數(shù)設(shè)定為較大的數(shù)。

  1. plot_learning_curve(degree=2, alpha=10)

一個典型的狀況:對于少量數(shù)據(jù)而言,訓(xùn)練集和交叉驗證集之間的誤差差別相當大,這暗示了過擬合。同樣的模型,不過使用大量數(shù)據(jù),誤差“收斂”,暗示了欠擬合。

如果我們加入更多數(shù)據(jù),訓(xùn)練集的誤差不會增加。另一方面,測試集上的誤差也不會下降。

所以,我們看到誤差“收斂”,加入新數(shù)據(jù)無濟于事。事實上這個情況對業(yè)務(wù)來說很重要。我們可能把數(shù)據(jù)集大小增大10倍,但是,如果我們不改變模型的復(fù)雜度,數(shù)據(jù)的增加沒有幫助。因此“設(shè)定一次,使用十次”的策略可能無法奏效。

如果我們將正則化系數(shù)降低到0.05,會怎么樣?

我們看到了好兆頭——曲線逐漸收斂,如果我們移動到更右,也就是加入更多數(shù)據(jù),我們可以進一步改善模型在驗證集上的表現(xiàn)。

  1. plot_learning_curve(degree=2, alpha=0.05)

如果我們把alpha設(shè)為10-4,讓模型更復(fù)雜,會出現(xiàn)什么情況?

我們看到了過擬合——在訓(xùn)練集和驗證集上,AUC都下降了。

  1. plot_learning_curve(degree=2, alpha=1e-4)

構(gòu)建這些曲線可以幫助我們理解朝哪個方向走,如何恰當?shù)貫樾聰?shù)據(jù)調(diào)整模型復(fù)雜度。

學(xué)習(xí)曲線和驗證曲線的一些結(jié)論:

  • 訓(xùn)練集上的誤差本身不能說明模型的質(zhì)量。

  • 交叉驗證誤差顯示了模型對數(shù)據(jù)擬合得有多好(數(shù)據(jù)的現(xiàn)有趨勢)同時保留了多少對新數(shù)據(jù)的概括能力。

  • 驗證曲線是一個根據(jù)模型復(fù)雜度顯示訓(xùn)練集和驗證集上的結(jié)果的圖形:

    • 如果兩條曲線彼此接近,兩者的誤差都很大,這標志著欠擬合

    • 如果兩條曲線彼此距離很遠,這標志著過擬合

  • 學(xué)習(xí)曲線是一個根據(jù)觀測數(shù)量顯示訓(xùn)練集和驗證集上的結(jié)果的圖形:

    • 如果兩條曲線收斂,增加新數(shù)據(jù)無濟于事,有必要改變模型復(fù)雜度

    • 如果兩條曲線沒有收斂,增加新數(shù)據(jù)可以改善結(jié)果

6. 課內(nèi)Kaggle競賽“我知道你是誰”

Kaggle競賽頁面:

https://www./c/catch-me-if-you-can-intruder-detection-through-webpage-session-tracking2

我們將解決一個入侵者檢測問題(通過分析上網(wǎng)用戶的行為)。這是一個結(jié)合了數(shù)據(jù)分析和行為心理學(xué)的復(fù)雜而有趣的問題。例如,Yandex根據(jù)用戶的行為模式解決郵箱入侵檢測問題。概括起來,入侵者的行為模式可能和郵箱所有者不一樣:

  • 郵箱所有者可能在讀完郵件后刪除郵件,而入侵者可能不會這么做;

  • 入侵者標記郵件的方式可能不一樣,甚至移動鼠標的方式也可能不一樣;

  • 等等。

所以我們可以檢測到入侵者,將其扔出郵箱,要求通過短信驗證碼認證身份。

Google Analytics研發(fā)了類似的技術(shù),并發(fā)表了相應(yīng)的論文。你可以通過搜索“Traversal Pattern Mining”(遍歷模式挖掘)和“Sequential Pattern Mining”(序列模式挖掘)了解更多關(guān)于這一主題的信息。

在這一競賽中,我們將解決一個類似的問題:我們的算法將分析特定用戶頻繁訪問的網(wǎng)站序列,預(yù)測這個人是不是一個名為Alice的用戶還是一個入侵者(其他人)。我們將使用ROC-AUC作為指標。

數(shù)據(jù)下載和轉(zhuǎn)換

如果你之前沒有注冊過Kaggle賬號的話,注冊一下。然后到競賽頁面,下載數(shù)據(jù)。

首先,加載訓(xùn)練集和測試集。探索下數(shù)據(jù):

  1. %matplotlib inline

  2. from matplotlib import pyplot as plt

  3. import seaborn as sns

  4. import pickle

  5. import numpy as np

  6. import pandas as pd

  7. from scipy.sparse import csr_matrix

  8. from scipy.sparse import hstack

  9. from sklearn.preprocessing import StandardScaler

  10. from sklearn.metrics import roc_auc_score

  11. from sklearn.linear_model import LogisticRegression

  12. # 加載訓(xùn)練集和測試集

  13. train_df = pd.read_csv('../../data/websites_train_sessions.csv',

  14.                       index_col='session_id')

  15. test_df = pd.read_csv('../../data/websites_test_sessions.csv',

  16.                      index_col='session_id')

  17. # 轉(zhuǎn)換time1、……、time10列至datetime類

  18. times = ['time%s' % i for i in range(1, 11)]

  19. train_df[times] = train_df[times].apply(pd.to_datetime)

  20. test_df[times] = test_df[times].apply(pd.to_datetime)

  21. # 據(jù)時間排序數(shù)據(jù)

  22. train_df = train_df.sort_values(by='time1')

  23. # 查看下訓(xùn)練集的開始幾行

  24. train_df.head()

訓(xùn)練集包含以下特征:

  • site1 會話中訪問的第一個網(wǎng)站;

  • time1 會話中訪問第一個網(wǎng)站的時間;

  • ……

  • site10 會話中訪問的第十個網(wǎng)站;

  • time10 會話中訪問第十個網(wǎng)站的時間;

  • target 目標變量,1表示Alice的會話,0表示其它用戶的會話。

用戶會話的選取方式保證這些會話不超過半小時或包含超過十個網(wǎng)站,也就是說,一旦一個用戶訪問過了十個網(wǎng)站,或者會話時長超過了半小時,我們就認為這一會話結(jié)束了。

表中有一些空值,意味著某些會話包含不到十個網(wǎng)站。將這些空值替換為0,并將列類型改為整數(shù)。同時,加載網(wǎng)站字典,看看是什么樣的

  1. # 將site1、……、site10列類型轉(zhuǎn)為整數(shù),同時用零填充NA值

  2. sites = ['site%s' % i for i in range(1, 11)]

  3. train_df[sites] = train_df[sites].fillna(0).astype('int')

  4. test_df[sites] = test_df[sites].fillna(0).astype('int')

  5. # 加載網(wǎng)站字典

  6. with open(r'../../data/site_dic.pkl', 'rb') as input_file:

  7.    site_dict = pickle.load(input_file)

  8. # 創(chuàng)建字典的dataframe

  9. sites_dict = pd.DataFrame(list(site_dict.keys()),

  10.                          index=list(site_dict.values()), columns=['site'])

  11. print(u'Websites total:', sites_dict.shape[0])

  12. sites_dict.head()

  1. # Websites total: 48371

簡單起見,我們將只使用會話中訪問的站點(不考慮訪問時長)。這一數(shù)據(jù)選擇背后的依據(jù)是:Alice有她偏愛的站點,我們在會話中看到更多這些站點,這一會話是Alice的會話的可能性就越高,反之亦然。

讓我們準備下數(shù)據(jù)。首先,我們從訓(xùn)練集中排除目標變量,這樣,訓(xùn)練集和測試集就有了相同數(shù)量的列,我們可以將其合并為一個dataframe,對其一并進行轉(zhuǎn)換。

  1. # 目標變量

  2. y_train = train_df['target']

  3. # 合并為一個dataframe

  4. full_df = pd.concat([train_df.drop('target', axis=1), test_df])

  5. # 分割訓(xùn)練集和測試集的索引

  6. idx_split = train_df.shape[0]

只保留dataframe的site1, site2, ..., site10。

  1. # 訪問過的網(wǎng)站索引構(gòu)成的dataframe

  2. full_sites = full_df[sites]

  3. full_sites.head()

會話是網(wǎng)站索引的序列,這一數(shù)據(jù)表示不便于通過線性方法處理。我們需要將其轉(zhuǎn)換為如下表示形式:每個網(wǎng)站對應(yīng)一個特征(列),其值為會話中訪問該網(wǎng)站的次數(shù)。

  1. # 索引序列

  2. sites_flatten = full_sites.values.flatten()

  3. # 我們想要的矩陣

  4. full_sites_sparse = csr_matrix(([1] * sites_flatten.shape[0],

  5.                                sites_flatten,

  6.                                range(0, sites_flatten.shape[0] + 10, 10)))[:, 1:]

如果你明白剛剛發(fā)生了什么,那你可以直接跳到下一節(jié)(也許你也可以處理邏輯回歸?),否則,讓我們一起來搞明白發(fā)生了什么。

稀疏矩陣

讓我們估計一下,在上面的例子中,儲存我們的數(shù)據(jù)需要多少內(nèi)存。合并后的dataframe包含336k樣本,每個樣本包含48k整數(shù)特征。因此我們大致需要的內(nèi)存為:

  1. 336K * 48K * 8 bytes = 16G * 8 Bytes = 128 GB

顯然,凡夫俗子沒有這么多內(nèi)存(嚴格來說,Python可能允許你創(chuàng)建這樣一個矩陣,但對其進行任何操作都不容易)。一個有趣的事實是,我們的矩陣的大多數(shù)元素值為零。如果我們計算非零元素,那么大概有一百八十萬,所占比例略高于所有矩陣元素的0.01%. 這樣一種大多數(shù)元素為零的矩陣,稱為稀疏矩陣,零元素數(shù)量和總元素的比例則稱為矩陣的稀疏度。

scipy.sparse庫可用于處理稀疏矩陣,參考它的文檔了解稀疏矩陣的類型,如何處理,何時使用稀疏矩陣最高效。注意,稀疏矩陣只包含非零元素。最后,讓我們看看稀疏矩陣占用的內(nèi)存大?。@然,稀疏矩陣顯著節(jié)省了內(nèi)存):

  1. # 稀疏矩陣占用多少內(nèi)存?

  2. print('{0} elements * {1} bytes = {2} bytes'.

  3.      format(full_sites_sparse.count_nonzero(), 8,

  4. full_sites_sparse.count_nonzero() * 8))

  5. # 或者直接:

  6. print('sparse_matrix_size = {0} bytes'.

  7.      format(full_sites_sparse.data.nbytes))

  1. 1866898 elements * 8 bytes = 14935184 bytes

  2. sparse_matrix_size = 14935184 bytes

讓我們通過一個迷你的例子探索下這一矩陣是如何形成的。假設(shè)我們的用戶會話表是這樣的:

總共有3個會話,每次會話不超過3個站點。用戶訪問的不同站點總數(shù)為4(表中的1到4表示這4個站點)。讓我們假定這4個站點是:

  1. vk.com

  2. habrahabr.ru

  3. yandex.ru

  4. ods.ai

如果用戶在會話時訪問了不到三個站點,后面的值將為零。我們想要將原dataframe轉(zhuǎn)換為每個會話顯示某一特定站點的訪問數(shù),如下表所示:

為此,我們使用csr_matrix ((data, indices, indptr))創(chuàng)建一個頻率表。這里,我們顯式地設(shè)定所有參數(shù),讓你能更清楚地理解這一過程:

  1. # 創(chuàng)建由1組成的列表,長度等于原dataframe中元素的數(shù)量(9)

  2. data = [1] * 9

  3. # 網(wǎng)站id

  4. indices = [1, 0, 0, 1, 3, 1, 2, 3, 4]

  5. # 切分行(會話)的索引

  6. indptr = [0, 3, 6, 9]

  7. # 將上述三個變量聚合為一個元組,然后構(gòu)建矩陣

  8. # 顯示矩陣時,將其轉(zhuǎn)為通常的“密集”矩陣

  9. csr_matrix((data, indices, indptr)).todense()

  1. matrix([[2, 1, 0, 0, 0],

  2.          [0, 2, 0, 1, 0],

  3.          [0, 0, 1, 1, 1]])

你也許注意到了,所得矩陣的列數(shù)目不是四(不同網(wǎng)站的總數(shù)),而是五。第零列是額外增加的列,顯示每次會話少訪問了幾個站點(在我們的迷你例子中,會話的長度為3)。這一列是多余的,需要從dataframe中移除。

使用稀疏矩陣的另一個好處是,有為其特制的矩陣操作和機器學(xué)習(xí)算法實現(xiàn),有時能通過利用數(shù)據(jù)結(jié)構(gòu)的特點顯著加速操作。邏輯回歸也適用?,F(xiàn)在,萬事俱備,我們可以創(chuàng)建第一個模型了。

訓(xùn)練第一個模型

我們將使用sklearn的邏輯回歸實現(xiàn)(默認參數(shù))。數(shù)據(jù)集的前90%用于訓(xùn)練(數(shù)據(jù)集按時間排序),剩余10%用于驗證。

  1. def get_auc_lr_valid(X, y, C=1.0, seed=17, ratio = 0.9):

  2.    # 將數(shù)據(jù)分為訓(xùn)練集和驗證集

  3.    idx = int(round(X.shape[0] * ratio))

  4.    # 訓(xùn)練分類器

  5.    lr = LogisticRegression(C=C, random_state=seed,

  6.                            solver='lbfgs', n_jobs=-1).fit(X[:idx, :], y[:idx])

  7.    # 為驗證集做出預(yù)測

  8.    y_pred = lr.predict_proba(X[idx:, :])[:, 1]

  9.    # 計算質(zhì)量

  10.    score = roc_auc_score(y[idx:], y_pred)

  11.    return score

  12. %%time

  13. # 選擇訓(xùn)練集

  14. X_train = full_sites_sparse[:idx_split, :]

  15. # 在驗證集上計算量度

  16. print(get_auc_lr_valid(X_train, y_train))

  1. 0.9198622553850315

  2. CPU times: user 138 ms, sys: 77.1 ms, total: 216 ms

  3. Wall time: 2.74 s

第一個模型在驗證集上的表現(xiàn)接近0.92 ROC-AUC。讓我們將其作為第一條基線(作為一個開始)。為了在測試集上進行預(yù)測,我們需要在整個訓(xùn)練集上再次訓(xùn)練模型(到此為止,我們的模型只使用了數(shù)據(jù)的一部分進行訓(xùn)練),這將增加模型的概括能力:

  1. # 將預(yù)測寫入文件的函數(shù)

  2. def write_to_submission_file(predicted_labels, out_file,

  3.                             target='target', index_label='session_id'):

  4.    predicted_df = pd.DataFrame(predicted_labels,

  5.                                index = np.arange(1,

  6.                                                  predicted_labels.shape[0] + 1),

  7.                                columns=[target])

  8.    predicted_df.to_csv(out_file, index_label=index_label)

  9. # 在整個訓(xùn)練數(shù)據(jù)集上訓(xùn)練模型

  10. # 為了可重復(fù)性,將random_state設(shè)為17

  11. # 顯式設(shè)置C=1(這是默認值)

  12. lr = LogisticRegression(C=1.0, solver='lbfgs',

  13.                        random_state=17).fit(X_train, y_train)

  14. # 在測試數(shù)據(jù)集上進行預(yù)測

  15. X_test = full_sites_sparse[idx_split:,:]

  16. y_test = lr.predict_proba(X_test)[:, 1]

  17. # 寫入預(yù)測結(jié)果至提交文件

  18. write_to_submission_file(y_test, 'baseline_1.csv')

如果你遵循這些步驟,并將答案上傳到競賽頁面,你將在公開排行榜上取得ROC AUC = 0.91707的成績。

7. 作業(yè)四

你的任務(wù)是通過特征工程、特征縮放和正則化進一步改善模型。你將首先嘗試添加一些明顯的特征(比如瀏覽網(wǎng)站的時刻,會話中的網(wǎng)站數(shù)目,等等)。我們鼓勵你在課程的學(xué)習(xí)過程中嘗試新的想法和模型,并參與競賽——這很有趣!

截止日期:March 11, 23:59 CET

8. 相關(guān)資源


  • I. Goodfellow、Y. Bengio、A. Courville所著《Deep Learning》(深度學(xué)習(xí))一書提供了緊湊而出色的線性模型綜述。

  • 基本上每本ML教材都涉及線性模型。我們推薦C. Bishop的《Pattern Recognition and Machine Learning》和K. Murphy的《Machine Learning: A Probabilistic Perspective》。

  • 如果你打算從統(tǒng)計學(xué)的視角概覽線性模型,可以看下T. Hastie、R. Tibshirani、J. Friedman的《The elements of statistical learning》。

  • P. Harrington的《Machine Learning in Action》將引導(dǎo)你完全使用Python實現(xiàn)經(jīng)典ML算法。

  • scikit-learn庫。scikit-learn的開發(fā)者致力于編寫極為清晰的文檔。

  • Scipy 2017 scikit-learn教程(Alex Gramfort、Andreas Mueller)。

  • MTH594課程 Advanced data mining: theory and applications包含很多非常好的材料。

  • GitHub倉庫rushter/MLAlgorithms里有許多ML算法的實現(xiàn),其中包括線性回歸和邏輯回歸。

  • 歡迎留言分享其他資源。

原文地址:https:///open-machine-learning-course/open-machine-learning-course-topic-4-linear-classification-and-regression-44a41b9b5220

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多