# 前文回顾

• 朴素贝叶斯算法的基本原理
• 公式推导贝叶斯准则(条件概率公式)
• 构建训练、测试简易文本分类算法
• 拉普拉斯平滑修正

# 文本预处理

``````#将评分划分成1-5五个等级
def rating(e):
if '50' in e:
return 5
elif '40' in e:
return 4
elif '30' in e:
return 3
elif '20' in e:
return 2
elif '10' in e:
return 1
else:
return 'none'
# 利用map方法依据rating函数创建新一列
data['new_rating'] = data['rating'].map(rating)``````

``````# 删去评分为3的短评，判定评分为3的情感持中性
data = data[data['new_rating'] != 3]
#将4、5评分标注成1，视为正面情绪；将1、2评分标注成0，视为负面情绪
data['sentiment'] = data['new_rating'].apply(lambda x: +1 if x > 3 else 0)``````

``````# 删去短评中的符号、英文字母
punc = '~`!#\$%^&*()_+-=|\';":/.,?><~·！@#￥%……&*（）——+-=“：’；、。，？》《{}'
def remove_fuhao(e):
return re.sub(r"[%s]+" % punc, " ", e)
def remove_letter(new_short):
return re.sub(r'[a-zA-Z]+', '', new_short)
# 利用jieba切割文本
def cut_word(text):
text = jieba.cut(str(text))
return ' '.join(text)
# 同apply方法依据以上三个自定义函数为依据创建新一列
data['new_short'] = data['short'].apply(remove_fuhao).apply(remove_letter).apply(cut_word)``````

``````# 读取停用词表函数
def stopwordslist(filepath):
stopwords = [line.strip() for line in open(filepath, 'r', encoding='utf-8').readlines()]
return stopwords
# 将短评中的停用词删去
def sentence_div(text):
# 将短评按空格划分成单词并形成列表
sentence = text.strip().split()
# 加载停用词的路径
stopwords = stopwordslist(r'中文停用词表.txt')
#创建一个空字符串
outstr = ' '
# 遍历短评列表中每个单词
for word in sentence:
if word not in stopwords: # 判断词汇是否在停用词表里
if len(word) > 1:  # 单词长度要大于1
if word != '\t':  # 单词不能为tab
if word not in outstr:  # 去重：如果单词在outstr中则不加入
outstr += ' '  # 分割
outstr += word # 将词汇加入outstr
#返回字符串
return outstr
data['the_short'] = data['new_short'].apply(sentence_div)``````

``````data['split'] = data['the_short'].apply(lambda x: 1 if len(x.split()) > 3 else 0)
data = data[~data['split'].isin(['0'])]
# 将需要的两列数据索引出，合并成一个新的DataFrame
new_data1 = data.iloc[:, 3]
new_data2 = data.iloc[:, 5]
new_data = pd.DataFrame({'short': new_data2, 'sentiment': new_data1})``````

``````def splitDataSet(new_data):
# 获取数据集中随机的10%作为测试集，获取测试数据集的索引
test_index = random.sample(new_data.index.tolist(), int(len(new_data.index.tolist()) * 0.10))
# 剩下的部分作为训练集，获取训练数据集的索引
train_index = [i for i in new_data.index.tolist() if i not in test_index]
#分别索引出训练集和测试集
test_data = new_data.iloc[test_index]
train_data = new_data.iloc[train_index]
# 分别保存为csv文件
train_data.to_csv('bayes_train.csv', encoding='utf_8_sig', index=False)
test_data.to_csv('bayes_test.csv', encoding='utf_8_sig', index=False)``````

# 构建分类器

### 构建词向量

``````def loadDataSet(filename):
postingList = []
#文本语句切分
for sentence in data['short']:
word = sentence.strip().split()# split方法返回一个列表
postingList.append(word)#  将每个词汇列表添至一个列表中
#类别标签的向量
classVec = data['sentiment'].values.tolist()
return postingList,classVec``````

createVocabList函数的作用是通过set方法已经取并集的方式返回一个包含文本中所有出现的不重复词的集合。

``````#创建词汇表
def createVocabList(dataSet):
#创建一个空的不重复列表
vocabSet = set([])
for document in dataSet:
#取两者并集
vocabSet = vocabSet | set(document)
return list(vocabSet)``````

setOfWords2Vec函数的作用是将短评向量化，输入参数为总词汇表和某个短评，输出的是文本向量，向量的元素包括1或0，分别表示词汇表中的单词是否出现在输入的文本中，思路是首先创建一个同词汇表等长的向量，并将其元素都设置为0，然后遍历输入文本的单词，若词汇表中出现了本文的单词，则将其对应位置上的0置换为1。

``````#词条向量化函数
def setOfWords2Vec(vocabList, inputSet):
#创建一个元素都为0的向量
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in vocabList:
#若词汇表包含该词汇，则将该位置的0变为1
returnVec[vocabList.index(word)] = 1
return returnVec``````

getMat函数的作用是将所有处理后的词条向量汇总合并成一个词条向量矩阵，方便测试算法时调用。

``````#词条向量汇总
def getMat(inputSet):
trainMat = []
vocabList = createVocabList(inputSet)
for Set in inputSet:
returnVec = setOfWords2Vec(vocabList,Set)
trainMat.append(returnVec)
return trainMat``````

### 训练算法

trainNB函数的输入参数包括短评矩阵trainMatrix和每个词条的情感标注所构成的向量trainCategory。首先短评属于正面情绪的概率只需要将正面情绪短评的个数除以总词条个数即可；计算P(W | C1)和P(W | C0)时，需要将其分子和分母初始化，遍历输入文本时，一旦某个词语(正面情绪or负面情绪)在某一文档中出现，则该词对应的个数(p1Num或p0Num)就加1，并且在总文本中，该词条的总次数也相应加1。

``````def trainNB(trainMatrix,trainCategory):
#训练文本数量
numTrainDocs = len(trainMatrix)
#每篇文本的词条数
numWords = len(trainMatrix[0])
#文档属于正面情绪(1)的概率
pAbusive = sum(trainCategory)/float(numTrainDocs)
#创建两个长度为numWords的零数组
p0Num = np.ones(numWords)
p1Num = np.ones(numWords)
#分母初始化
p0Denom = 2.0
p1Denom = 2.0
for i in range(numTrainDocs):
if trainCategory[i] == 1:
#统计正面情绪的条件概率所需的数据，即P(w0|1),P(w1|1),P(w2|1)···
p1Num += trainMatrix[i]
#print(p1Num)
p1Denom += sum(trainMatrix[i])
#print(p1Denom)
else:
#统计负面情绪的条件概率所需的数据，即P(w0|0),P(w1|0),P(w2|0)···
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
#计算词条出现的概率
p1Vect = np.log(p1Num/p1Denom)
p0Vect = np.log(p0Num/p0Denom)
#print("\n",p0Vect,"\n\n",p1Vect,"\n\n",pAbusive)
return p1Vect,p0Vect,pAbusive``````

### 测试算法

classifyNB函数是一个判断类别的函数，输入参数为向量格式的测试数据和训练函数trainNB的三个返回值，如p1的概率大于p0的概率则代表该测试数据为正面情绪，返回值为1；返之则是负面情绪，返回值为0。

``````def classifyNB(ClassifyVec, p1V,p0V,pAb):
#将对应元素相乘
print(pAb)
p1 = sum(ClassifyVec * p1V) + np.log(pAb)
p0 = sum(ClassifyVec * p0V) + np.log(1.0 - pAb)
print('p1:',p1)
print('p0:',p0)
if p1 > p0:
return 1
else:
return 0``````

testNB为测试函数，通过调用上述函数对测试集进行预测，并通过比较真实结果和测试结果以得到分类器的准确率。

``````def testNB():
#加载训练集数据
#创建词汇表
vocabSet = createVocabList(train_postingList)
#将训练样本词条向量汇总
trainMat = getMat(train_postingList)
#训练算法
p1V,P0V,PAb = trainNB(trainMat,train_classVec)
#加载测试集数据
# 将测试文本向量化
predict = []
for each_test in test_postingList:
testVec = setOfWords2Vec(vocabSet,each_test)
#判断类别
if classifyNB(testVec,p1V,P0V,PAb):
print(each_test,"正面情绪")
predict.append(1)
else:
print(each_test,"负面情绪")
predict.append(0)
corr = 0.0
for i in range(len(predict)):
if predict[i] == test_classVec[i]:
corr += 1
print("朴素贝叶斯分类器准确率为：" + str(round((corr/len(predict)*100),2)) + "%")``````