1 拉普拉斯平滑:
上一篇博客的最後留下了一個問題,那就是如果檢測的詞列表中包含概率爲0的字那麼最後結果總是0。那麼此時可以引入拉普拉斯平滑,也就是說,可以將所有的字初始化爲1,然後分母初始化爲2.
還有一個問題就是要防止下溢出,即小數與小數項城越乘越小,到最後保留小數可能就成爲了0,對此採用取對數的方式來進行解決,取對數不會有任何的損失。
圖片出處
代碼改進:
def Trainer(trans_list,classVec):
"""用來得出每個字的條件概率
Params:
trans_list是轉碼後的字分佈列表
classVec是每個句子的類別列表
return 侮辱性句子中每個字的條件概率,非侮辱性句子中每個字的條件概率,侮辱性句子的概率"""
numSentences = len(trans_list) #計算出要遍歷句子的數量
numBad = sum(classVec) #算出出現侮辱性句子的總數
numWords = len(trans_list[0]) #words_list的長度
pAbusive = numBad / numSentences #求出出現侮辱性句子的概率
p0list = np.ones(numWords) #初始化一個非侮辱的列表
p1list = np.ones(numWords)
p0Denom = 2.0 #初始化分母
p1Denom = 2.0
for class_ in range(numSentences): #進入遍歷
if classVec[class_] == 1: #如果是侮辱性的句子進入下面的這個循環
p1list += trans_list[class_] #統計每個字出現的頻率
p1Denom += sum(trans_list[class_])
else:
p0list += trans_list[class_]
p0Denom += sum(trans_list[class_])
p0Vec = np.log(p0list / p0Denom)
p1Vec = np.log(p1list / p1Denom)#防止下溢出
return p0Vec,p1Vec,pAbusive
def Classify(p0Vec,p1Vec,pAbusive,Test_sentence):
"""計算貝葉斯的分子比較大小
Params:
p0Vec:非侮辱性句子當中每個字出現的條件概率
p1Vec:侮辱性句子當中每個字出現的條件概率
pAbusive:出現侮辱性句子的概率
Test_sentence:輸入的文字"""
trans_sentence = Encoder(Test_sentence,words_list) #首先對文字進行轉碼
p0 = sum(p0Vec * trans_sentence) +(1-pAbusive) #因爲結果取了對數,log(a*b) = log(a) +log(b)
#reduce函數的功能爲:按照前面的函數對列表中的值依此運算,這裏reduce就是求獨立的條件概率的乘積,本意是:P(has|非侮辱)*P(is|非侮辱)*P(非侮辱)
p1 = sum(p1Vec*trans_sentence) +pAbusive #
if p1>p0: #通過比較大小就可判斷
print('該句帶有侮辱性詞彙')
elif p1<p0:
print('該句爲非侮辱語句')
else:
print('抱歉,出現bug,暫時無法識別...')
2 實例二:垃圾郵件過濾器:
樸素貝葉斯對電子郵件進行分類的步驟:
- 收集數據:提供文本文件。
- 準備數據:將文本文件解析成詞條向量。
- 分析數據:檢查詞條確保解析的正確性。
- 訓練算法:使用我們之前建立的trainNB0()函數。
- 測試算法:使用classifyNB(),並構建一個新的測試函數來計算文檔集的錯誤率。
- 使用算法:構建一個完整的程序對一組文檔進行分類,將錯分的文檔輸出到屏幕上。
- 有兩個文件夾ham和spam,spam文件下的txt文件爲垃圾郵件。
首先要將一個個文件差分爲詞條向量,因爲數據都是英文字母或數字,所以可以用正則表達式的split來拆分。
import re
import numpy as np
def Words_Split(BigString):
words = re.split(r'\W',BigString) #利用正則表達式庫的split \W表示非數字或字母
return [each.lower() for each in words if len(each)>2] #如果單詞長度大於二,那麼就將其變成小寫,除了I這種單個的大
- 生成詞彙表
def Words_Split(BigString):
words = re.split(r'\W',BigString) #利用正則表達式庫的split \W表示非數字或字母
return [each.lower() for each in words if len(each)>2] #如果單詞長度大於二,那麼就將其變成小寫,除了I這種單個的大
def Words_List(dataset):
vect = set([])
for each in dataset:
vect = vect | set(each)
return list(vect)
if __name__ == '__main__':
docList=[] #創建一個字典用來存放所有郵件的單詞
classList=[] #創建一個存放分類的列表
for i in range(1,26):
doc = Words_Split(open('E:/Data/ham/%d.txt' % i,'r' ).read())
docList.append(doc)
classList.append(0)
for j in range(1,26):
doc2 = Words_Split(open('E:/Data/spam/%d.txt' % i,'r' ).read())
docList.append(doc2)
classList.append(1)
words_list = Words_List(docList)
print(words_list)
結果如下:
- 生成詞袋模型:
def Encoder(words_list,Sentence):
"""講一個句子當中出現過的字顯示爲1,沒出現過的字顯示爲0,根據words_list,創建一個詞袋模型
Params:
words_list:Words_List輸出的詞彙表
Sentence:放入的每一條詞條向量
return:
轉碼爲0-1分佈的詞袋模型"""
trans_list = [0]*len(words_list)
for each in Sentence:
if each in words_list:
trans_list[words_list.index(each)] = 1
else:
print('該文字不在目錄當中')
return trans_list
- 貝葉斯訓練器:
def Trainer(transform,classList):
"""得出垃圾郵件和普通郵件每個字的條件概率以及垃圾郵件出現的概率
Params:
transform:Encoder輸出合成的列表
classList:分類列表
return:
普通郵件各個字的條件概率;垃圾郵件各個字的條件概率;總的垃圾郵件出現的概率"""
Line_num = len(transform)#確定要遍歷句子的數量
Bad_num = sum(classList)
pAbusive = Bad_num / float(Line_num)
p0List = np.ones(len(transform[0])) #高斯平滑
p1List = np.ones(len(transform[0])) #高斯平滑
p0Denom = 2.0
p1Denom = 2.0
for i in range(Line_num):
if classList[i] == 1:
p1List+=transform[i]
p1Denom += sum(transform[i])
else:
p0List+=transform[i]
p0Denom += sum(transform[i])
p0 = np.log(p0List/p0Denom) #防止向下溢出
p1 = np.log(p1List/p1Denom)
return p0,p1,pAbusive
- 分類器:
def Classfier(p0,p1,pAbusive,Test):
"""對郵件進行分類
Params:
p0:普通郵件中每個字的條件概率
p1:垃圾郵件中每個字的條件概率
pAbusive:總的出現垃圾郵件的概率
Test:Encoder轉碼後的詞袋模型"""
P0 = sum(p0*Test) + np.log((1-pAbusive))
P1=sum(p1*Test) + np.log(pAbusive)
if P0>P1:
return 0
elif P0<P1:
return 1
- 測試精確讀:
import re
import numpy as np
import random
def Words_Split(BigString):
words = re.split(r'\W',BigString) #利用正則表達式庫的split \W表示非數字或字母
return [each.lower() for each in words if len(each)>2] #如果單詞長度大於二,那麼就將其變成小寫,除了I這種單個的大
def Words_List(dataset):
vect = set([])
for each in dataset:
vect = vect | set(each)
return list(vect)
def Encoder(words_list,Sentence):
"""講一個句子當中出現過的字顯示爲1,沒出現過的字顯示爲0,根據words_list,創建一個詞袋模型
Params:
words_list:Words_List輸出的詞彙表
Sentence:放入的每一條詞條向量
return:
轉碼爲0-1分佈的詞袋模型"""
trans_list = [0]*len(words_list)
for each in Sentence:
if each in words_list:
trans_list[words_list.index(each)] = 1
else:
print('該文字不在目錄當中')
return trans_list
def Trainer(transform,classList):
"""得出垃圾郵件和普通郵件每個字的條件概率以及垃圾郵件出現的概率
Params:
transform:Encoder輸出合成的列表
classList:分類列表
return:
普通郵件各個字的條件概率;垃圾郵件各個字的條件概率;總的垃圾郵件出現的概率"""
Line_num = len(transform)#確定要遍歷句子的數量
Bad_num = sum(classList)
pAbusive = Bad_num / float(Line_num)
p0List = np.ones(len(transform[0])) #高斯平滑
p1List = np.ones(len(transform[0])) #高斯平滑
p0Denom = 2.0
p1Denom = 2.0
for i in range(Line_num):
if classList[i] == 1:
p1List+=transform[i]
p1Denom += sum(transform[i])
else:
p0List+=transform[i]
p0Denom += sum(transform[i])
p0 = np.log(p0List/p0Denom) #防止向下溢出
p1 = np.log(p1List/p1Denom)
return p0,p1,pAbusive
def Classfier(p0,p1,pAbusive,Test):
"""對郵件進行分類
Params:
p0:普通郵件中每個字的條件概率
p1:垃圾郵件中每個字的條件概率
pAbusive:總的出現垃圾郵件的概率
Test:Encoder轉碼後的詞袋模型"""
P0 = sum(p0*Test) + np.log((1-pAbusive))
P1=sum(p1*Test) + np.log(pAbusive)
if P0>P1:
return 0
elif P0<P1:
return 1
def spamTest():
"""測試貝葉斯的精確度"""
docList=[] #用來存放所有的文字
classList=[] #用來存放每個郵件的類別
fulltext=[]
for i in range(1,26): #遍歷所有郵件
doc = Words_Split(open('E:/Data/ham/%d.txt' % i,'r' ).read())
docList.append(doc)
fulltext.append(doc)
classList.append(0)
doc = Words_Split(open('E:/Data/spam/%d.txt' % i,'r' ).read())
docList.append(doc)
fulltext.append(doc)
classList.append(1)
words_list = Words_List(docList) #輸出詞彙表
train_set = list(range(50)) #總共50個文件,用來存放40個訓練集的索引
test_set = [] #存放10個測試集的索引
for i in range(10): #取十個作爲測試集
index = int(random.uniform(0,len(train_set))) #用random.uniform來隨機取出一個索引
test_set.append(train_set[index]) #加入到測試集當中
del(train_set[index]) #並且在訓練集中刪去
trainMat = [] #用來存放訓練集單詞轉碼後的詞袋模型
trainClass = [] #用來存放訓練集的類別
for each_index in train_set:
each_train = Encoder(words_list,docList[each_index]) #依此轉碼
trainMat.append(each_train)
trainClass.append(classList[each_index])
p0,p1,pAbusive = Trainer(np.array(trainMat),np.array(trainClass)) #得出訓練集的數據
errocount=0 #初始化誤差值
for index in test_set:
word = Encoder(words_list,docList[index])
if Classfier(p0,p1,pAbusive,word) != classList[index]: #如果不相等就表示出現了誤差
errocount+=1
print('測試完畢,錯誤率爲:{:.2f}%'.format((errocount/len(test_set))*100))
if __name__=='__mian__':
spamTest()
結果: