1. 程式人生 > >機器學習實戰(Python)總結

機器學習實戰(Python)總結

機器學習實戰總結

正在整理中……

你看完這本書了麼,一起復習一下吧
整本書建議結合中英文、原始碼一起看

1KNN

knn的主要思想是計算與特徵最近的已知資料,選取照頻率最大的特徵。
注意計算距離的時候用的dataset的格式,否則可能報錯
在計算距離時為避免部分特徵權重過大,故將每個特徵都歸一化
在手寫數字的辨別系統中,將32*32的影象轉化為了有1024特徵的陣列,在讀取訓練樣本中使用了from os import listdir來讀取眾多的檔名然後批量處理計算得到最近的型別
主要用的函式有tile sum(axis=1) argsort get(label,0)+1 sorted listdir

另外在讀取文字的時候用的是readlines 在把影象轉化為向量時在迴圈內部用的是readline 可以體會下不同點噢

2決策樹decision trees(ID3)

首先要了解概念,書裡都有了就不敲了

決策樹分為兩部分,第一部分是創立決策樹字典,第二部分是繪製決策樹,可以看成是遞迴的練習,好多遞迴呼叫/(ㄒoㄒ)/~~

2.1創立決策樹字典

2.1.1 選取最佳特徵
首先要理解上面關於夏農熵的概念,之後據此選取最佳特徵作為決策節點,大體思路是利用了迴圈巢狀,對於每個特徵比較各個特徵值作用下的熵,看那個增益最大,找特徵值時用了set相當於MATLAB裡面的unique
2.1.2建立字典
遞迴開始爆發了!


神奇之處就是建立了個keys值為字典的字典的字典的字典……
看下式
myTree = {bestFeatLabel:{}}
即:
先將特徵為字典的keys
然後將特徵的特徵值作為新字典的特徵,如果有不同的特徵值就可能有多個新字典

myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)

遞迴啥時候結束呢,1全部分類相同時就不需要在分類了,2全部遍歷完了所有的特徵都考慮了,注意這個時候可能相同的特徵不同的結果如四個特徵值都為1111但是其分類卻是nononoyes 此時一般選取多的返回
主要用的函式有:extend append count 列表推導 sorted

2.2繪製註解決策樹

2.2.1 整體思路
將字典利用Python的annotate功能繪製成樹狀圖。
2.2.2 具體實施
因為繪製每一個樹枝用的方法相近,所以可以使用遞迴呼叫,繪製每個決策節點和葉子節點。
- 利用annatate時需要的帶求引數有座標及註解內容
座標的選取很有技巧性,看下圖:
決策樹座標選取對於縱座標比較好選,按照深度d,每次下沉1/d。對於橫座標的選取利用以下公式:

fx:=fx+1+n2w其中w是指寬度
公式是咋來的呢,這裡提示一下,看圖中bbb的橫座標是如何定位的呢,首先所有的葉子節點均勻分佈的,
顯然它應該處於它下屬的三個葉子節點的中間,
而最左端的no節點如果能點位那麼bbb也就可以定位了,
no距離最左邊佔了121w
再來看ccc是如何定位的呢,
同樣,其下屬的no節點如與左邊的no節點相差了一個1w,
也就是說除了第一個決策節點外,其他的決策節點都是根據最近的已經確定的葉子節點的橫座標的設定的,為了能遞迴將初始橫座標設定為12w即可
關於註釋的內容:
先來看下我們的決策樹字典{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}
若先畫出第一個決策節點,那麼我們發現每個中間特徵值(0或者1)和特徵都是這個字典的key而葉子節點為key的value(非dict)。
- 下面就要關注遞迴的結束條件
在遞迴計算座標和annotate內容時都是看key的value是否是一個字典(注意此時所用的key),若是字典就畫出決策節點和中間特徵值
決策節點是字典的第一個key,而中間的特徵值就是這個字典的key(它的上一層),所以最外層的特徵值是沒有上層的key的用‘’代替。此處巧妙的地方需要自己慢慢體會哇
另外,葉子節點數量和書的深度也是用遞迴呼叫來完成的,具體原理與上面類似就不解釋了
主要用的函式annotate text isinstance figure clf show subpolt

2.3測試和儲存

測試時主要用到了index函式用來選取label對應的特徵值,獲得樹上的下級value,判斷是否是字典,然後遞迴呼叫,直到不是字典返回value作為分類
儲存和呼叫主要使用了pickle.dump pickle.load
如果上面理解了例項運用也就簡單了

3樸素貝葉斯 Naive Bayes

首先這個演算法的名字就很好簡稱NB哈哈
首先要了解條件概率
我們要做的就是通過這個演算法在知道x的條件下計算P(|x),因為這裡得到的是概率,所以要計算出所有的類別,然後比較各類出現 概率,概率最大的我們就認為是合理的分類
為了方便表述,假設我們要求在x出現的時候類別為垃圾郵件的概率

P(|x)=P(x)P(x)=P(x|)P()P(x)
如果有很多x,比如後面要說的垃圾郵件中有很多的字,這個演算法中我們假設每個字都是相互獨立的,彼此不認識,所以這個演算法叫樸素貝葉斯。
如上文所說,我們計算P(|x)主要是為了和P(|x)比較大小,在上面的公式中我們發現分母都是一樣的因此比較的時候可以不用管,另外,如果要計算很多個x,他們的P(x|)就是P(x|)=P(xi|)如果每個概率都很小,那麼成績也就很小了,在Python容易近似為0,為了方便比較我們對分子求log
知道了這些下一步就是計算分子了:
以垃圾郵件分類為例:
首先應建立字型檔,也就是在這些郵件裡都出現了那些字,然後統計每個字在垃圾郵件中出現的次數及在正常郵件中出現的次數儲存在兩個矩陣中,然後除以出現的總數就分別得到了每個字在垃圾郵件出現的概率和在正常郵件中的概率{P(x|垃圾){P(x|正常) 然後分別乘以垃圾的和正常的概率{P(垃圾){P(正常)(程式裡因為是求log所以是求和)
這樣就結束了,先把其中的得到概率的程式與接受文字的程式貼上
def trainNB0(trainMatrix,trainCategory):
    numTrainDocs = len(trainMatrix)#0軸長度=郵件數目
    numWords = len(trainMatrix[0])#字型檔長度
    pAbusive = sum(trainCategory)/float(numTrainDocs)#stam概率
    p0Num = ones(numWords); p1Num = ones(numWords)      #change to ones() 
    p0Denom = 2.0; p1Denom = 2.0                        #change to 2.0
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:
            p1Num += trainMatrix[i]#字型檔在正常郵件中出現數量的矩陣(考慮出現次數)
            p1Denom += sum(trainMatrix[i])#全部字型檔出現的總數量
        else:
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    p1Vect = log(p1Num/p1Denom)          #change to log()變log方便變乘為加 而且避免在很多很小的數相乘時容易太接近零
    p0Vect = log(p0Num/p0Denom)          #change to log()
    return p0Vect,p1Vect,pAbusive  
def spamTest():
    docList=[]; classList = []; fullText =[]
    for i in range(1,26):#注意不包括26
        wordList = textParse(open('email/spam/%d.txt' % i).read())
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(1)
        wordList = textParse(open('email/ham/%d.txt' % i).read())
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)
    vocabList = createVocabList(docList)#create vocabulary建立無重複字型檔
    trainingSet = range(50); testSet=[]           #create test set
    for i in range(10):#0,1,2,……,9
        randIndex = int(random.uniform(0,len(trainingSet)))#random.uniform的函式原型為:random.uniform(a, b),用於生成一
        testSet.append(trainingSet[randIndex])             #個指定範圍內的隨機符點數,兩個引數其中一個是上限,一個是下限。如果a > b,則生成的隨機數n: a <= n <= b。如果 a <b, 則 b <= n <= a。
        del(trainingSet[randIndex])                        #刪除trainingset中的序號防止重複
    trainMat=[]; trainClasses = []
    for docIndex in trainingSet:#train the classifier (get probs) trainNB0
        trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))#統計每篇文章中重複出現次數
        trainClasses.append(classList[docIndex])
    p0V,p1V,pSpam = trainNB0(array(trainMat),array(trainClasses))
    errorCount = 0
    for docIndex in testSet:        #classify the remaining items
        wordVector = bagOfWords2VecMN(vocabList, docList[docIndex]#統計待測數在字型檔中出現的的次數
        if classifyNB(array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
            errorCount += 1
            print "classification error",docList[docIndex]
    print 'the error rate is: ',float(errorCount)/len(testSet)
    #return vocabList,fullText

注意的是在書後面的例子中剔除了出現次數最多的若干單詞,要合理好多,另外在其他書籍上的貝葉斯還加入了風險因子等資訊。
另外在處理文字時用到了正則表示式

4logistic 迴歸 邏輯迴歸

首先,關於要理解關於引數θ是如何更新的,本文中用的是w,這裡做簡要的總結
logistic迴歸的中的每一個特徵都遵循sigmoid函式h(z)=11+ez其中z=w0x0+w1x1++wnxn為方便討論 設hw(x)=P(y=1|x;w)其中y是類別,下面的例子中可以設當y=1時有病,將有病和沒病時的概率寫在一起可以寫成下式

p(y|x;w)=(hw(x))y(1hw(x))1y
當有很多個x時,將所有的概率相乘,求對數,然後利用最大似然,對wi求導就得到了梯度公式:
wiJ(w)=(yihθ(xi))xi
知道梯度了 就可以利用批量梯度、隨機梯度或者mini批量梯度來算了
文中的初始值設為了1
批量演算法時的Python程式碼如下:
for k in range(maxCycles):              #heavy on matrix operations
        h = sigmoid(dataMatrix*weights)     #matrix mult 100*1(設datamatrix是100*3的矩陣,weights是3*1的矩陣)
        error = (labelMat - h)              #vector subtraction
        weights = weights + alpha * dataMatrix.transpose()* error #用到了矩陣的乘積

隨機梯度時,可以不用matrix格式,因為每次只迭代一個

 weights = weights + alpha * error * dataMatrix[randIndex]

要注意這樣算的時候datamatrix不可以是list噢,書的源程式logregres.py**使用時需要修改**
其實除了梯度上升法之外還可以用牛頓法,它的迭代次數要少,但是需要計算hessian矩陣
在文中第一個例子中是畫出一條直線來將散點分類,而直線所在的位置就是使x1和x2滿足sigmoid等於0.5的地方
這裡寫圖片描述
值得注意的是在使用批量梯度下降時因為權重開始時設為weights = ones((n,1)),所以劃線時如果直接用weight[0]就會使y變為1l,60l的矩陣與x不同,繪圖容易出錯;量外圖片上是迭代了500次的結果,如果迭代1000次效果會更好些
對於隨機梯度上升,書裡將alpha的值設為逐漸變小的值
本章中的預測馬的疾病情況,有一點非常值得學習的就是如何處理非完整資料:將不完整的資料用0補充(在logistic迴歸中)

5支援向量機 SVM

6 Adaboost 演算法

這個演算法的道理比較簡單,就是將幾個弱分類器組合成一個強分類器。主要要弄清楚加權錯誤率、分類器權重、樣本權重之間的關係,同時要知道最終分類結果與每次每類結果之間的關係。
流程如下:
1. 初步設定所有樣本的權重,取相同的值
2. 選擇弱分類器
3. 計算弱分類器的加權錯誤率(錯誤的樣本權重相加)
4. 根據權重計算分類器權重alpha(分類器分的好權重大)
5. 計算更新的樣本權重(分類正確樣本權重變小)
6. 計算各個帶權重的分類器之和,建造一個總的分類器(不懂可以看下面的例子),看是否錯誤率為零,如果不為零,繼續代入第二步計算,注意再次計算步驟2時的權重更新了,錯誤的樣本被重視,更有可能被新的若分離器成功分類,與前面的分類器互補,它的分類樣本是前面分類器的結果(這就話比較重要,理解了它就理解了adaboost),這也就決定了當分類器按照權重相加時錯誤率逐漸減小。
在李航博士http://blog.sina.com.cn/u/2060750830的《統計學習方法》中有關於誤差的證明有興趣的可以看一下
還是不太明白的話,讓我們一起看個例子~
有5個數據
1、權重相同(0.2)
2、弱分類器分錯了第一個,分類器權重alpha為0.69
3、第一個資料的權重變為0.5
4、第二次分類,如果選用第一次同樣的分類器就會發現,此時分錯的資料權重很大,導致所計算的加權錯誤率很大,所以,更改弱分類器,直到得到加權錯誤率最小的那個分類器。此時alpha為0.97.
!注意:此時得到的分類結果是什麼呢?此時的分類結果是第一次的分類標籤×0.69+第二次的分類標籤×0.97,這樣一來,假設第一個資料的標籤為+1,那麼兩次的分類結果為sign(-1*0.69+1*0.97)=sign(0.28),那麼現在第一個標籤的結果就是正確的。但是同時,可能有第一次分類正確的標籤,在第二次分類中出現了錯誤。
5、經過第二次分類後,第五個元素分類出現錯誤,於是第五個元素的權重變大,為0.5
6、進行第三次分類,此時每個元素的最終類別並不是最後一次分類的結果,而是三次分類的權重之和,假設第三個弱分類器的alpha 是0.89,那麼最終的分類結果就是0.69X1+