機器學習實戰0:評論爬蟲+貝葉斯模型標注惡意評論+分布式形式


  

一 引言

  本程序是一個完整的機器學習過程,先編寫基於python的爬蟲腳本,爬取目標論壇網站的評論到本地存儲,然后使用貝葉斯分類模型對評論進行分類,預測新 的評論是否為垃圾評論。如果遇到大數據量的問題,可以把貝葉斯算法寫成mapreduce模式,map負責把數據集划分成鍵值對格式,類序號為key,屬 性向量為value,reduce進行匯總每類的先驗概率和條件概率,主server匯總所有類的統計量。

二 爬蟲腳本

  1 編寫爬蟲腳本,爬取目標論壇的評論。其中,headers是必須的,因為我們需要偽裝成瀏覽器在訪問論壇的服務器。使用requests包獲取指定url的數據流。

mport requests
from bs4 import BeautifulSoup
import re
import json
import time

headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
'Cookie': '__cfduid=d653bf931cbde10f9243b63e991f70dc41466778585; loid=a5WUnHRHlleKL9OSSR; loidcreated=2016-06-24T14%3A29%3A45.413Z; _recent_srs=t5_2qu49; _ga=GA1.2.54465388.1466778724; pc=ne; __utma=55650728.54465388.1466778724.1466778728.1466843492.2; __utmz=55650728.1466778728.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utmb=55650728.0.10.1466843492; __utmc=55650728',
'Host': 'www.reddit.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0',
}

url = 'https://www.reddit.com/r/AskReddit/comments/4qfh01/what_are_some_classes_you_must_take_in/'
r = requests.get(url,headers=headers)
r.encoding = r.apparent_encoding

  2 使用BeautifulSoup解析爬去的html文件,css定位我們需要的字段,輸出到本地文件comments.txt保存即可。

soup = BeautifulSoup(r.text)
res = soup.select("div.md")
comments = []
for item1 in res[1:]:
    comments.append(item1.contents)
print comments

fd = open('comments.txt','w+')

p_soup = BeautifulSoup(str(comments))
res2 = p_soup.findAll('p')
for item2 in res2:
    ct = str(item2.contents).encode('utf-8')
    print ct[3:-2]
    fd.write(ct[3:-2] + '\n')

fd.close()

 

三 實戰1 -文本分類(應用過濾惡意留言等)

  下面是二分類問題,文檔只能屬於0和1兩個類別,

  1 載入數據集:本地讀取文件comments.txt中爬蟲爬取的評論。

from numpy import *
def loadDataSet():
    fd = open('comments.txt')
    postingList = []
    classVec = []
    for line in fd.readlines():
        tmp = line.split()
        postingList.append(tmp[1:])
        classVec.append(int(tmp[0]))
    return postingList,classVec

  2 創建詞匯表:利用集合結構內元素的唯一性,創建一個包含所有詞匯的詞表。

def createVocabList(dataSet):
    vocabSet = set([])  #create empty set
    for document in dataSet:
        vocabSet = vocabSet | set(document) #union of the two sets
    return list(vocabSet)

  3 把輸入文本根據詞表轉化為計算機可處理的01向量形式:

  eq,測試文本1: ['love', 'my', 'dalmation']

     詞匯表:['cute', 'love', 'help', 'garbage', 'quit', 'I', 'problems', 'is', 'park', 'stop', 'flea', 'dalmation', 'licks', 'food', 'not', 'him', 'buying', 'posting', 'has', 'worthless', 'ate', 'to', 'maybe', 'please', 'dog', 'how', 'stupid', 'so', 'take', 'mr', 'steak', 'my']

    向量化結果:[0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]

def setOfWords2Vec(vocabList, inputSet):
    returnVec = [0]*len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] = 1
        else: print "the word: %s is not in my Vocabulary!" % word
    return returnVec

  4訓練模型:在訓練樣本中計算先驗概率 p(Ci) 和 條件概率 p(x,y | Ci),本實例有0和1兩個類別,所以返回p(x,y | 0),p(x,y | 1)和p(Ci)。

  此處有兩個改進的地方:

    (1)若有的類別沒有出現,其概率就是0,會十分影響分類器的性能。所以采取各類別默認1次累加,總類別(兩類)次數2,這樣不影響相對大小。

    (2)若很小是數字相乘,則結果會更小,再四舍五入存在誤差,而且會造成下溢出。采取取log,乘法變為加法,並且相對大小趨勢不變。

def trainNB0(trainMatrix,trainCategory):
    numTrainDocs = len(trainMatrix)
    numWords = len(trainMatrix[0])
    pAbusive = sum(trainCategory)/float(numTrainDocs)
    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()
    p0Vect = log(p0Num/p0Denom)          #change to log()
    return p0Vect,p1Vect,pAbusive

  5 分類:根據計算后,哪個類別的概率大,則屬於哪個類別。

def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): p1 = sum(vec2Classify * p1Vec) + log(pClass1) #element-wise mult p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1) if p1 > p0: return 1 else: return 0

  6 測試函數:

    加載數據集+提煉詞表;

    訓練模型:根據六條訓練集計算先驗概率和條件概率;

    測試模型:對訓練兩條測試文本進行分類。

def testingNB():
    listOPosts,listClasses = loadDataSet()
    myVocabList = createVocabList(listOPosts)
    trainMat=[]
    for postinDoc in listOPosts:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
    p0V,p1V,pAb = trainNB0(array(trainMat),array(listClasses))
    testEntry = ['friends', 'my', 'take']
    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
    print testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb)
    testEntry = ['stupid', 'garbage']
    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
    print testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb)

  缺點:詞表只能記錄詞匯是否出現,不能體現這個詞匯出現的次數。改進方法:采用詞袋模型,見下面垃圾郵件分類實戰。

  結果圖:['friends', 'wish', 'classes'] 被分到 正常評論類;
      ['stupid', 'garbage'] 被分到垃圾評論類;分類結果正確。

四 算法的MapReduce形式

  本人正在把這個貝葉斯分類算法轉換成分布式算法,初步思想是,可以把貝葉斯算法寫成mapreduce模式,map負責把數據集划分成鍵值對格式,類序號為key,屬 性向量為value,reduce進行匯總每類的先驗概率和條件概率,主server匯總所有類的統計量。

   1 mapper程序

if __name__ ==  '__main__':
    for line in sys.stdin:
        line = line.strip()
        word = line.split()
        key = word[0]
        value = '' 
        for item in word[1:]:
            value += item+ ' '
        print "%s\t%s"%(key,value)

  2 本次mapper測試結果,鍵值對成功分離:  執行命令$ cat data.txt | python bayes_mapper.py

1    0.270252528981 0.102916847315 
0    0.772917922479 0.182969066019 
1    0.817848764874 0.743666751784 
0    0.197846533796 0.835258987261 
1    0.174895157684 0.31219280438 
1    0.16756664003 0.529593388634 
1    0.56918073026 0.0624409762296 
1    0.292857532814 0.152683257148 
0    0.971077138206 0.712432682621 
0    0.775544377315 0.163165909954 

  3 reducer程序,正在編寫,下次更新

 

 

備注:數據集生成腳本

#!/usr/bin/python
import sys
import random

if __name__ == '__main__':
    if len(sys.argv) < 3:
        row = 5
        col = 5
    else:
        row = int(sys.argv[1])
        col = int(sys.argv[2])
    for r in range(0,row):
        tmp = str(random.randint(0,1))
        for c in range(0,col):
            num = random.uniform(0,1)
            tmp += ' ' + str(num)
        print tmp 

生成數據集如下:

1 0.270252528981 0.102916847315
0 0.772917922479 0.182969066019
1 0.817848764874 0.743666751784
0 0.197846533796 0.835258987261
1 0.174895157684 0.31219280438
1 0.16756664003 0.529593388634
1 0.56918073026 0.0624409762296
1 0.292857532814 0.152683257148
0 0.971077138206 0.712432682621
0 0.775544377315 0.163165909954

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM