python編程導(dǎo)論_第十五課

學(xué)習(xí)安排(8月24日-8月26日)
1.主要學(xué)習(xí)視頻Week8
鏈接(http://www.xuetangx.com/courses/MITx/6_00_2x/2014_T2/courseware/d39541ec36564a88af34d319a2f16bd7/
2.參考書第21、24章

chapter24 分類方法

最常見的監(jiān)督式學(xué)習(xí)應(yīng)用就是建立分類模型好芭。分類模型也稱為分類器哄啄,用于對樣本進(jìn)行標(biāo)注粗截,標(biāo)明這個樣本屬于一個有限的類別集合中的哪個類墙牌。

在單分類學(xué)習(xí)中羡蛾,訓(xùn)練集中的數(shù)據(jù)僅來自一個類別俊柔,目標(biāo)是學(xué)習(xí)一個模型以預(yù)測某個樣本是否屬于這個類別狭归。當(dāng)難以找到不屬于這個類別的訓(xùn)練數(shù)據(jù)時,單分類學(xué)習(xí)是比較有用的鹦马,它通常用于建立異常檢測機(jī)制胧谈,例如在計算機(jī)網(wǎng)絡(luò)中檢測未知攻擊。

在二分類學(xué)習(xí)(常稱為二元分類)中荸频,訓(xùn)練集中的樣本全部來自兩個類別(通常稱為陽性和陰性) 菱肖,目標(biāo)是找到一個可以區(qū)分兩個類別的邊界。 多分類學(xué)習(xí)的目標(biāo)則是找到可以將多個類別區(qū)分開來的邊界旭从。

本章將介紹兩種廣泛使用的監(jiān)督式學(xué)習(xí)方法來解決分類問題: K最近鄰方法和回歸方法稳强。介
紹這些方法之前,我們先要解決一個問題:如何評價由這些方法產(chǎn)生的分類器和悦?

分類器評價

對于二分類問題退疫,可將樣例根據(jù)其真是類別與分類器預(yù)測類別的組合劃分為真陽性、假陽性鸽素、真陰性褒繁、假陰性四種情況,可用混淆矩陣表示模型的分類結(jié)果馍忽。
每種分類器在訓(xùn)練數(shù)據(jù)上的準(zhǔn)確度可以計算如下:
準(zhǔn)確度=\frac{真陽性+真陰性}{真陽性+假陽性+真陰性+假陰性}
當(dāng)兩個類的大小差不多時棒坏,用準(zhǔn)確度評價分類器是非常合適的。存在嚴(yán)重的類別不平衡時遭笋,用準(zhǔn)確度評價分類器會得到非常糟糕的結(jié)果坝冕。想像一下,如果你負(fù)責(zé)評價這樣一種分類器瓦呼,它用來預(yù)測某個人是否患有某種潛在的致命疾病喂窟,這種疾病的發(fā)病率大約是0.1%。這時央串,準(zhǔn)確度就不是一個合適的統(tǒng)計量谎替,因為只要簡單地宣布所有患者都沒有病,就可以得到99.9%的準(zhǔn)確度蹋辅。這種分類器對于那些要為治療付錢的人來說真是太好了(因為沒有人需要治療!)悯姊,但對于那些對自己可能患病憂心忡忡的人來說烙懦,就太不公平了。

類別不平衡時侥加,可用如下統(tǒng)計量評價分類器:靈敏度=\frac{真陽性}{真陽性+假陰性}
特異度=\frac{真陰性}{真陰性+假陽性}
陽性預(yù)測值=\frac{真陽性}{真陽性+假陽性}
陰性預(yù)測值=\frac{真陰性}{真陰性+假陰性}

預(yù)測跑步者的性別

我們的任務(wù)是通過跑步者的年齡和完成時間來預(yù)測跑步者的性別褒傅。
函數(shù)getBMData從一個文件讀出數(shù)據(jù)并建立一個樣本集合弃锐。每個樣本都是Runner類的一個實例(instance)。每名跑步者都有一個標(biāo)簽(性別)和一個特征向量(年齡和完成時間)殿托。 Runner類中的方法featureDist返回兩名跑步者特征向量之間的歐氏距離霹菊。下一步就是,將樣本劃分為一個訓(xùn)練集和一個先保留不用的測試集支竹。最常用的做法是旋廷,使用80%的數(shù)據(jù)訓(xùn)練模型,使用剩余的20%數(shù)據(jù)對模型進(jìn)行測試礼搁。函數(shù)divide80_20可以完成這一任務(wù)饶碘。請注意,訓(xùn)練數(shù)據(jù)是隨機(jī)選取的馒吴。

class Runner(object):
    def __init__(self, gender, age, time):
        self.featureVec = (age, time)
        self.label = gender

    def featureDist(self, other):
        dist = 0.0
        for i in range(len(self.featureVec)):
            dist += abs(self.featureVec[i] - other.featureVec[i]) ** 2
        return dist ** 0.5

    def getTime(self):
        return self.featureVec[1]
    def getAge(self):
        return self.featureVec[0]
    def getLabel(self):
        return self.label
    def getFeatures(self):
        return self.featureVec

    def __str__(self):
        return str(self.getAge()) + ', ' + str(self.getTime()) \
               + ', ' + self.label

def buildMarathonExamples(fileName):
    data = getBMData(fileName)
    examples = []
    for i in range(len(data['age'])):
        a = Runner(data['gender'][i], data['age'][i],
                   data['time'][i])
        examples.append(a)
    return examples

def divide80_20(examples):
    sampleIndices = random.sample(range(len(examples)),
                                  len(examples) // 5)
    trainingSet, testSet = [], []
    for i in range(len(examples)):
        if i in sampleIndices:
           testSet.append(examples[i])
        else:
          trainingSet.append(examples[i])
    return trainingSet, testSet

現(xiàn)在我們已經(jīng)做好了準(zhǔn)備扎运,可以通過各種不同的方法使用訓(xùn)練集來建立分類器,以預(yù)測跑步者的性別饮戳。通過對數(shù)據(jù)的檢查豪治,我們知道訓(xùn)練集中有58%的跑步者是男性。所以扯罐,如果我們總是猜測跑步者是男性负拟,將會得到58%的準(zhǔn)確度。

K最近鄰方法

K最近鄰方法可能是最簡單的分類算法篮赢。通過這種方法“學(xué)習(xí)”的模型就是訓(xùn)練集本身齿椅。對新樣本進(jìn)行標(biāo)注時,就是根據(jù)它們與訓(xùn)練集樣本的相似度而進(jìn)行的启泣。

如下實現(xiàn)了一個K最近鄰分類器涣脚,可以基于跑步者的年齡和完成時間對其性別進(jìn)行預(yù)測。這個實現(xiàn)其實是一種暴力算法寥茫。函數(shù) findKNearest 的復(fù)雜度與 exampleSet 中的樣本數(shù)量 成 線 性 關(guān)系 遣蚀, 因 為 它要 計 算example 與 exampleSet 中 每 個元 素 之 間 的特 征 距 離 。函 數(shù) KNearestClassify 使用簡單的多數(shù)票勝出原則來進(jìn)行分類纱耻,它的復(fù)雜度是 O(len(training) * len(testSet))芭梯,因為它要對函數(shù) findNearest 進(jìn)行總共 len(testSet) 次調(diào)用。

def findKNearest(example, exampleSet, k):
    kNearest, distances = [], []
    # 建立一個列表弄喘,包含最初7個樣本和它們的距離
    for i in range(k):
        kNearest.append(exampleSet[i])
        distances.append(example.featureDist(exampleSet[i]))
    maxDist = max(distances)  # 找出最大距離
    # 檢查其余樣本
    for e in exampleSet[k:]:
        dist = example.featureDist(e)
        if dist < maxDist:
            # 替換距離更遠(yuǎn)的鄰居
            maxIndex = distances.index(maxDist)
            kNearest[maxIndex] = e
            distances[maxIndex] = dist
            maxDist = max(distances)
    return kNearest, distances


def KNearestClassify(training, testSet, label, k):
    """假設(shè)training和testSet是兩個樣本列表玖喘, k是整數(shù)
    使用K最近鄰分類器預(yù)測testSet中的每個樣本是否具有給定的標(biāo)簽
    whether each example in testSet has the given label
    返回真陽性、假陽性蘑志、真陰性和假陰性的數(shù)量"""
    truePos, falsePos, trueNeg, falseNeg = 0, 0, 0, 0
    for e in testSet:
        nearest, distances = findKNearest(e, training, k)
        # 進(jìn)行投票
        numMatch = 0
        for i in range(len(nearest)):
            if nearest[i].getLabel() == label:
                numMatch += 1
            if numMatch > k // 2:  # 具有標(biāo)簽
                if e.getLabel() == label:
                    truePos += 1
                else:
                    falsePos += 1
            else:  # 不具有標(biāo)簽
                if e.getLabel() != label:
                    trueNeg += 1
                else:
                    falseNeg += 1
    return truePos, falsePos, trueNeg, falseNeg

基于回歸的分類器

下面使用線性回歸累奈,根據(jù)訓(xùn)練集數(shù)據(jù)為男性和女性分別建模贬派。


男性和女性的線性回歸模型

如下代碼使用LogisticRegression類為波士頓馬拉松數(shù)據(jù)建立了一個模型,并進(jìn)行了測試澎媒。實現(xiàn)applyModel時搞乏,代碼首先使用列表推導(dǎo)式(參見5.3.2節(jié))建立一個列表,列表中的元素是testSet中樣本的特征向量戒努。然后请敦,代碼調(diào)用model.predict_proba方法得到一個數(shù)組,數(shù)組的元素是一個值對储玫,對應(yīng)每個特征變量的預(yù)測值侍筛。最后,代碼將預(yù)測值與具有該特征向量的樣本的標(biāo)簽進(jìn)行比較缘缚,記錄并返回真陽性勾笆、假陽性、真陰性和假陰性結(jié)果的數(shù)量桥滨。

def applyModel(model, testSet, label, prob = 0.5):
    #為所有測試樣本創(chuàng)建一個包含特征向量的向量
    testFeatureVecs = [e.getFeatures() for e in testSet]
    probs = model.predict_proba(testFeatureVecs)
    truePos, falsePos, trueNeg, falseNeg = 0, 0, 0, 0
    for i in range(len(probs)):
        if probs[i][1] > prob:
            if testSet[i].getLabel() == label:
                truePos += 1
            else:
                falsePos += 1
        else:
            if testSet[i].getLabel() != label:
                trueNeg += 1
            else:
                falseNeg += 1
    return truePos, falsePos, trueNeg, falseNeg

examples = buildMarathonExamples('bm_results2012.txt')
training, test = divide80_20(examples)

featureVecs, labels = [], []
for e in training:
    featureVecs.append([e.getAge(), e.getTime()])
    labels.append(e.getLabel())
model = sklearn.linear_model.LogisticRegression().fit(featureVecs, labels)
print('Feature weights for label M:',
    'age =', str(round(model.coef_[0][0], 3)) + ',',
    'time =', round(model.coef_[0][1], 3))
truePos, falsePos, trueNeg, falseNeg = \
    applyModel(model, test, 'M', 0.5)
getStats(truePos, falsePos, trueNeg, falseNeg)

我們可以在applyModel函數(shù)中調(diào)整概率閾值窝爪,使靈敏度與KNN方法的靈敏度近似相等。要找到這個閾值概率齐媒,我們可以對prob的值進(jìn)行遍歷蒲每,直到得到一個與KNN方法非常接近的靈敏度。
如果使用prob = 0.578——而不是0.5——調(diào)用applyModel喻括,會得到如下結(jié)果:
準(zhǔn)確度=0.659
靈敏度=0.714
特異度=0.586
陽性預(yù)測值=0.695
可以看出邀杏,這兩個模型的性能幾乎是一樣的。

對于線性回歸模型唬血,知道改變決策閾值所帶來的影響非常容易望蜡。因此,人們通常使用受試者工作特征曲線拷恨,或稱ROC曲線脖律,來形象地表示靈敏度和特異度之間的折衷關(guān)系。這種曲線可以繪制出多個決策閾值的真陽性率(靈敏度)和假陽性率(1 – 特異度)之間的關(guān)系腕侄。

通過計算曲線下面積小泉,可以在多個ROC曲線之間進(jìn)行比較。這個面積實際上是個概率冕杠,對于一個隨機(jī)選擇的陽性樣本微姊,一個好的模型將其標(biāo)注為陽性的概率應(yīng)該高于將一個隨機(jī)選擇的陰性樣本標(biāo)注為陽性的概率。這就是人們所說的模型的判別能力分预。請一定注意兢交,判別能力和準(zhǔn)確度是不同的,它也常被稱為對概率的校準(zhǔn)笼痹。例如配喳,我們可以將所有估計出的概率都除以2飘诗,這時不會
改變模型的判別能力,但顯然改變了模型估計的準(zhǔn)確度界逛。

def buildROC(model, testSet, label, title, plot = True):
    xVals, yVals = [], []
    p = 0.0
    while p <= 1.0:
        truePos, falsePos, trueNeg, falseNeg =\
            applyModel(model, testSet, label, p)
        xVals.append(1.0 - specificity(trueNeg, falsePos))
        yVals.append(sensitivity(truePos, falseNeg))
        p += 0.01
    auroc = sklearn.metrics.auc(xVals, yVals, True)
    if plot:
        pylab.plot(xVals, yVals)
        pylab.plot([0,1], [0,1,], '--')
        pylab.title(title + ' (AUROC = '+ str(round(auroc, 3)) + ')')
        pylab.xlabel('1 - Specificity')
        pylab.ylabel('Sensitivity')
    return auroc

buildROC(model, test, 'M', 'ROC for Predicting Gender')
ROC曲線和AUROC
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市纺座,隨后出現(xiàn)的幾起案子息拜,更是在濱河造成了極大的恐慌,老刑警劉巖净响,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件少欺,死亡現(xiàn)場離奇詭異,居然都是意外死亡馋贤,警方通過查閱死者的電腦和手機(jī)赞别,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來配乓,“玉大人仿滔,你說我怎么就攤上這事∮糖郏” “怎么了崎页?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長腰埂。 經(jīng)常有香客問我飒焦,道長,這世上最難降的妖魔是什么屿笼? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任牺荠,我火速辦了婚禮,結(jié)果婚禮上驴一,老公的妹妹穿的比我還像新娘休雌。我一直安慰自己,他們只是感情好蛔趴,可當(dāng)我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布挑辆。 她就那樣靜靜地躺著,像睡著了一般孝情。 火紅的嫁衣襯著肌膚如雪鱼蝉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天箫荡,我揣著相機(jī)與錄音,去河邊找鬼羔挡。 笑死间唉,一個胖子當(dāng)著我的面吹牛利术,可吹牛的內(nèi)容都是我干的呈野。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼印叁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了轮蜕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤跃洛,失蹤者是張志新(化名)和其女友劉穎率触,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體汇竭,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡葱蝗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年垒玲,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片合愈。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡击狮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出彪蓬,到底是詐尸還是另有隱情,我是刑警寧澤档冬,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布酷誓,位于F島的核電站,受9級特大地震影響盐数,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一谜诫、第九天 我趴在偏房一處隱蔽的房頂上張望攻旦。 院中可真熱鬧,春花似錦牢屋、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽皱炉。三九已至狮鸭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間歧蕉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工赌髓, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留催跪,地道東北人。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓荣倾,卻偏偏與公主長得像骑丸,于是被迫代替她去往敵國和親舌仍。 傳聞我的和親對象是個殘疾皇子通危,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容