哈尔滨工程大学-537
首先导入将会使用到的库:numpy、re、random
import numpy as np
import re
import random
定义一个text_parse函数,将文档进行分词(将整篇文档切分成单词)解析,得到长度大于2的词的列表。
def text_parse(big_string):
list_of_tokens = re.split(r"\W*", big_string) # W为大写
return [tok.lower() for tok in list_of_tokens if len(tok) > 2]
# 遍历列表中每个元素,取出长度大于2的,转化成小写,并以列表的形式return
创建一个所有文档中出现的不重复词的列表,构造向量空间。
def create_vocab_list(data_set):
vocab_set = set([]) # 创建一个空集合
for document in data_set: # 取文档集合中的每一个文档
vocab_set = vocab_set | set(document) # 依次与vocab_set取并集,再赋值给vocab_set
return list(vocab_set) # 得到所有文档中的词汇的并集,以列表形式返回
基于bag of word词袋模型,将分词后的文档转化为词向量。
def bow2vec(vocab_list, input_set): # 输入参数为词汇表(向量空间)和分词后的文档
return_vec = [0] * len(vocab_list) # 初始化一个和词汇表等长的“0”向量
for word in input_set: # 遍历文档中的每个词汇
if word in vocab_list: # 如果文档中出现了词汇表中的词
return_vec[vocab_list.index(word)] += 1 # 找到该词在词汇表中的位置,在return_vec对应位置上加1
else:
print("the word %s is not in my Vocabulary!" % word)
return return_vec # 返回return_vec向量,其中每个元素代表了对应位置上的词出现的次数
输入参数为文档矩阵train_matrix,以及由每篇文挡类别标签所构成的向量train_category;
为了避免p(w1|c1)*p(w2|c1)…等于0的情况,将每个词出现的次数初始化为1,分母初始化为2。
def train_NB(train_matrix, train_category):
num_train_docs = len(train_matrix) # 文档数量,即train_matrix矩阵长度(行数)
num_words = len(train_matrix[0]) # 词汇数量,即train_matrix矩阵第一行长度
p_abusive = sum(train_category) / float(num_train_docs) # 侮辱性文档的概率
p0_num = np.ones(num_words) # 初始化分子p0和p1,长度等于词汇数的“1”数组
p1_num = np.ones(num_words) # p0和p1中的每个元素代表对应位置上的词汇出现的次数(初始化为出现1次)
p0_denom = 2.0 # (denominator:分母)
p1_denom = 2.0 # 初始化分母
for i in range(num_train_docs): # 遍历训练集中train_matrix中的所有文档
if train_category[i] == 1: # 若某个词出现在“1”类文档出现,则该次对应的个数加1
p1_num += train_matrix[i]
p1_denom += sum(train_matrix[i]) # 该文档的总词数也对应加1
else:
p0_num += train_matrix[i] # 对“0”类文档,同样操作
p0_denom += sum(train_matrix[i])
p1_vect = np.log(p1_num/p1_denom) # 最终的p1_num的每个元素表示对应每个词在“1”类文档里的出现次数
p0_vect = np.log(p0_num/p0_denom) # 最终的p1_denom表示“1”类文档里出现的词的总数
return p0_vect, p1_vect, p_abusive
# 最终p1_vect的每个元素表示对应每个词在“1”类文档里出现的频率,取对数避免“下溢”,p0_vect同理
输入为四个参数:要分类的文档的词向量、p(w|c0)、p(w|c1)以及“1”类文档的概率
将待分类文档的词向量的每个元素与p1_vec每个元素相乘,由于待分类文档中不存在的词在词向量对应位置上为0;
故相乘之后为待分类文档中出现的词的p(w|c1)取对数值,其中w为待分类文档中出现的词w1、w2…wn
再将各个对数值相加,得到p(w1|c1)*p(w2|c1)…*p(wn|c1)的取对数值,再加上“1”类文档的概率去对数值;
得到p(w1|c1)*p(w2|c1)…*p(wn|c1)*p(c1)的取对数值,即p(w1,w2…wn|c1)p(c1)的取对数值。
def classify_NB(vec2classify, p0_vec, p1_vec, p_class1):
p1 = sum(vec2classify * p1_vec) + np.log(p_class1)
p0 = sum(vec2classify * p0_vec) + np.log(1.0 - p_class1) # p0同理
if p1 > p0: # 比较p(w1,w2...wn|c1)p(c1)和p(w1,w2...wn|c0)p(c0)的大小;
return 1 # 即比较p(c1|w1,w2...wn)和p(c2|w1,w2...wn)的大小
else:
return 0
最后是实现主要功能函数。
def spam_test():
doc_list = []; class_list = []; full_text = []
head_name_spam = "D:\python_data\MachineLearningInaction\machinelearninginaction\Ch04\email\spam"
head_name_ham = "D:\python_data\MachineLearningInaction\machinelearninginaction\Ch04\email\ham"
for i in range(1,26): # 遍历spam文件夹下的25个文档(ham同理),将每个文档内的全部内容作为一个大字符串
word_list = text_parse(open(head_name_spam + "\\%d.txt" % i).read()) # 调用text_parse函数进行分词
doc_list.append(word_list) # 将每个文档分词后得到的列表追加到doc_list列表中
full_text.extend(word_list) # 将分词后得到列表中的每个元素追加到full_text列表中
class_list.append(1) # 由于是spam文件夹下,所以在class_list列表中追加1,对应该文档的类别
word_list = text_parse(open(head_name_ham + "\\%d.txt" % i).read())
doc_list.append(word_list)
full_text.extend(word_list)
class_list.append(0)
# 最终doc_list中的每一个元素是文档进行分词解析后得到的列表;
# full_text中的每一个元素是文档分词解析后得到的列表中的每一个元素
# class_list中每一个元素是word_list中对应文档的类别
# 从50个数字中,随机选择10个作为测试集,剩下的40个作为训练集(留存交叉验证)
vocab_list = create_vocab_list(doc_list) # 输入文档集合,得到词汇表(向量空间)
training_set = list(range(50)); test_set = [] # traing_set是一个整数列表,元素为0-49
for i in range(10): # 进行10次循环
rand_index = int(random.uniform(0, len(training_set)))# 每次产生一个0-len(training_set)之间的随机整数
test_set.append(training_set[rand_index]) # 将training_set对应下标的数字追加到测试集列表test_set
del(training_set[rand_index]) # 在training_set中删掉对应下标的数字
# 得到训练集文档的词向量矩阵,和对应的文档类别向量
train_mat = []; train_classes = []
for doc_index in training_set: # 遍历训练集中的每个数字
train_mat.append(bow2vec(vocab_list, doc_list[doc_index]))# 得到下标与数字doc_index对应的文档的词向量
train_classes.append(class_list[doc_index]) # 得到下表与数字doc_index对应的文档的类别
# 得到取对数的p(w|c0)和p(w|c1)以及p(c0)
p0_v, p1_v, p_spam = train_NB(train_mat, train_classes)
error_count = 0 # 初始化错误计数为0
for doc_index in test_set: # 遍历测试集中每个数字
word_vector = set_of_words2vec(vocab_list, doc_list[doc_index])
# 得到下标与数字doc_index对应的文档的0-1词向量
if classify_NB(word_vector, p0_v, p1_v, p_spam) != class_list[doc_index]:
# 如果该下标文档的分类与对应class_list中的类别不同,错误计数加1
error_count += 1
print("the error rate is:", float(error_count/len(test_set)))
调用10次spam_test函数,查看10次的错误率。
spam_test()
spam_test()
spam_test()
spam_test()
spam_test()
spam_test()
spam_test()
spam_test()
spam_test()
spam_test()
最后运行代码,得到如下错误率。
the error rate is: 0.1
the error rate is: 0.0
the error rate is: 0.0
the error rate is: 0.0
the error rate is: 0.1
the error rate is: 0.1
the error rate is: 0.0
the error rate is: 0.1
the error rate is: 0.1
the error rate is: 0.0