學(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)確度可以計算如下:
當(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)計量評價分類器:
預(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')