机器学习-P5 朴素贝叶斯算法(书P53)

一,概述

在这里插入图片描述

1,条件概率(Condittional probability)

条件概率,就是指在事件B发生的情况下,事件A发生的概率,用 P(A|B) 来表示
在这里插入图片描述
根据文氏图可知:在事件B发生的情况下,事件A发生的概率如下:↓
在这里插入图片描述
同理可得:P(A∩B) = P(B|A)*P(A)
所以:(将上述两个式子联立)
在这里插入图片描述

2,全概率公式

如果事件A1,A2,A3, … ,An构成一个完备事件且都有正改率,那么对于任何一个B事件满足以下公式:
在这里插入图片描述

3,贝叶斯推断

根据条件概率和全概率公式,可以得到贝叶斯概率:
在这里插入图片描述
P(A)称为“先验概率”(Prior prob),即在B发生之前,我们队A事件概率的一个判断。
P(A|B)称为“后验概率”(Posterior prob),即在B事件发生之后,我们对A事件概率的重新评估。
P(A|B)/P(B)称为“可能性函数”(Likely hood),这是一个调整因子,使得预估概率更加接近真实概率。

所以条件概率可以理解为:
后验概率 = 先验概率 * 调整因子

如果“可能性函数” > 1,意味着“先验概率”被增强,事件A的可能性变大;
如果“可能性函数” = 1,意味着B事件无助于判断A事件的可能性;
如果“可能性函数” < 1,意味着“先验概率”被削弱,事件A的可能性变小。

二,举个“栗子”(手动星标)

在这里插入图片描述
问:某男(帅,性格不好,不上进)向女生求婚,该女生嫁还是不嫁?
在这里插入图片描述
这个栗子,按照朴素贝叶斯的求解可转化为一下两个式子:在这里插入图片描述
在这里插入图片描述
最后根据表格和公式算出数据就好啦~

先来算分母
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
所以该女生应该是选择不嫁。

三,朴素贝叶斯的种类

在sklearn中,一共有3个朴素贝叶斯的分类算法。
分别是GaussianNB,MultinomialNB和BernoulliNB。

如何选择

  • 一般来说,如果样本特征的分布大部分是连续值,使用GaussianNB会比较好。
  • 如果样本特征的分布大部分是多元离散值,使用MultinomialNB较为合适。
  • 而样本特征是二元离散值或者稀疏的多元离散值,则应该使用BernoulliNB

1,高斯分布的朴素贝叶斯算法

GaussianNB就是先验为高斯分布(正态分布)的朴素贝叶斯,假设每个标签的数据都服从简单的正态分布。
在这里插入图片描述
其中Ck为Y的第k类类别。mean(k) 和 Var(k) 为需要从训练集估计的值。

GaussianNB在sklearn中的实现

1,会使用到的包

import pandas as pd  # 数据预览
from sklearn.model_selection import train_test_split  #数据切分
from sklearn.naive_bayes import GaussianNB  # GaussianNB方法
from sklearn.metrics import accuracy_score  # 计算准确的

2,导入sklearn中的鸢尾花数据

from sklearn import datasets
iris = datasets.load_iris()

3,使用pandas预览
传送门:Pandas的数据前处理

pf = pd.DataFrame(iris.data, columns=iris.feature_names)
pf['类别']=(iris.target)
pf.head()

在这里插入图片描述
5,切分数据集(训练 + 测试)

from sklearn.model_selection import train_test_split  
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, random_state=12)

6,建立算法模型

clf = GaussianNB()
clf.fit(x_train, y_train)
>>>GaussianNB(priors=None, var_smoothing=1e-09)

7,在测试集上进行预测

直接返回得出标签(分类结果)

clf.predict(x_test)
>>>array([0, 2, 0, 1, 2, 2, 2, 0, 2, 0, 1, 0, 0, 0, 1, 2, 2, 1, 0, 1, 0, 1,
       2, 1, 0, 2, 2, 1, 0, 0, 0, 1, 2, 0, 2, 0, 1, 1])

返回每一个测试数据所对应的每一个标签的可能性(概率),概率最高的标签就会返回到上边的那个方法中

clf.predict_proba(x_test)
>>>array([[1.00000000e+000, 2.32926069e-017, 1.81656357e-023],
       [4.28952299e-154, 2.48576754e-002, 9.75142325e-001],
       [1.00000000e+000, 7.45528845e-018, 3.79800436e-024],
       [3.59748710e-076, 9.99751806e-001, 2.48194200e-004],
       [2.20411871e-239, 4.45798016e-009, 9.99999996e-001],
       [1.23795145e-173, 1.95814902e-003, 9.98041851e-001],
       [2.45866589e-206, 2.34481513e-007, 9.99999766e-001],
       [1.00000000e+000, 2.61810906e-017, 2.67446831e-023],
       ......]

结果是一一对应的

len(clf.predict(x_test)) == len(clf.predict_proba(x_test))
>>>True

8,模型准确率(跑分啦)

accuracy_score(y_test, clf.predict(x_test))
>>>0.9736842105263158

2,多项式分布的朴素贝叶斯算法

MultinomialNB就是先验为多项式分布的朴素贝叶斯算法。
他假设特征是由一个简单地多项式分布生成的。多项式分布可以描述各种类型样本出现次数的概率,因此多项式朴素贝叶斯非常适合用于描述出现次数或者出现次数比例的特征。该模型常用于文本分类,特征表示是次数,例如某个词语的出现次数。

多项式分布公式如下:
在这里插入图片描述
其中,P(Xj = xjl | Y = Ck) 是第k个类别的第j维特征的第l个取值的条件概率。mk是训练集中输出位第k类的样本个数。入为一个大于 0 的常数,常常取 1,即拉普拉斯平滑。也可以取其他值。

MultinomialNB在sklearn中的实现

from sklearn.naive_bayes import MultinomialNB

mlf = MultinomialNB()
mlf.fit(x_train, y_train)
>>>MultinomialNB(alpha=1.0, class_prior=None, fit_prior=True)

(其他的和高斯分布贝叶斯都一样,嘎嘎)

3,伯努利分布的朴素贝叶斯

BernoulliNB就是先验为伯努利分布的朴素贝叶斯。
假设特征的先验概率为二元伯努利分布,即如下式:
在这里插入图片描述
此时 l 只有两种取值。xjl 只能取值 0或1。
在伯努利模型中,每个特征的取值是布尔值,即True和False(或者1和0)。在文本分类中,就是一个特征有没有在一个文档中出现

BernoulliNB在sklearn中的实现

from sklearn.naive_bayes import BernoulliNB

mlf = BernoulliNB()
mlf.fit(x_train, y_train)
>>>BernoulliNB(alpha=1.0, binarize=0.0, class_prior=None, fit_prior=True)

(其他的和高斯分布贝叶斯都一样,嘎嘎)

四,朴素贝叶斯之鸢尾花数据实验

应用GaussianNB对鸢尾花数据进行分类

1,导入数据集

import pandas as pd

dataSet = pd.read_csv('D:\Python\pycharm\机器学习\朴素贝叶斯算法\iris.txt',header=None)
dataSet.head()

2,切分出训练集和测试集

函数
在这里明没有将整个数据进行乱序,而只是对索引进行了乱序,然后再由乱序的索引随机提取样本,好处在于切分后不会改变数据原本的排列方式

import random

def randSplit(dataSet, rate):
    """
    数据集切分函数
    :param dataSet:原始数据集
    :param rate: 训练集所占原始数据集比例[0, 1]
    :return: 训练集,测试集
    """
    index = list(dataSet.index)    # 取出索引
    random.shuffle(index)   # 打乱索引
    dataSet.index = index   # 将打乱后的索引重新赋给原数据集
    n = dataSet.shape[0]    # 最大行数(共有几组数据)
    m = int(n * rate)       # 训练集所占的行数(训练集样本个数)
    # 依照比例提取训练集和测试集
    train = dataSet.loc[range(m), :]
    test = dataSet.loc[range(m, n), :]
    # 恢复原始数据集索引
    dataSet.index = range(dataSet.shape[0])
    # 恢复测试集索引
    test.index = range(test.shape[0])
    return train, test

使用

train, test = randSplit(dataSet, 0.8)

3,分类器的代码实现

以下为高斯朴素贝叶斯分类器
(注意:会对test进行格式上的更改,所以不能反复对text进行预测)

def gnd_classify(train, test):
    """
    高斯朴素贝叶斯分类器
    :param train: 训练集
    :param test: 测试集(包括属性与分类)
    :return: 测试集(追加一列为预测结果)
    """
    labels = train.iloc[:, -1].value_counts().index  # 提取训练集的标签样本种类(无重复)
    mean = []       # 存放每个类别的均值
    std = []        # 存放每个类别的方差
    result = []     # 存放测试集预测结果

    for i in labels:
        item = train.loc[train.iloc[:, -1] == i, :]                 # 取出每一种类别
        m = item.iloc[:, :-1].mean()                                # 当前类别的平均值
        mean.append(m)
        s = np.sum((item.iloc[:, :-1] - m) ** 2) / (item.shape[0])  # 当前类别的方差
        std.append(s)

    # 转换为DataFrame格式,索引为类标签
    means = pd.DataFrame(mean, index=labels)
    stds = pd.DataFrame(std, index=labels)

    for j in range(test.shape[0]):
        iset = test.iloc[j, :-1].tolist()  # 取出当前测试样本,并将其转换为list格式
        iprod = np.exp(-1 * (iset - means) ** 2 / (stds ** 2)) / (np.sqrt(2 * np.pi * stds))  # 正态分布公式
        prob = 1
        # 遍历每一个特征
        for k in range(test.shape[1] - 1):
            prob *= iprod[k]                          # 特征概率之积即为当前实例概率
            cla = prob.index[np.argmax(prob.values)]  # 返回最大概率类别
        result.append(cla)
        
    test['预测结果'] = result                                # 将预测结果加入测试集
    acc = (test.iloc[:, -1] == test.iloc[:, -2]).mean()    # 计算准确率
    print(f'模型预测准确率为:{acc}')
    return test

代码测试
(因为之前对数据集的切分是随机的所以这边的准确率可能会不大一样)

gnd_classify(train, test)
>>>模型预测准确率为:0.9333333333333333

在这里插入图片描述

五,使用朴素贝叶斯进行文档分类

在这里插入图片描述
此案例所有函数:

  • loadDataSet():创建实验数据集合
  • createVocabList(dataSet):生成不重复词汇表
  • setofWords2Vec(vocabList, inputSet):生成词向量(输入的是一个词条)-- 辅助函数
  • get_trainMat(dataSet):生成所有词条的向量
  • trainNB(trainMat, classVec):朴素贝斯分类器训练函数(以标题六代码为准)
  • classifyNB(vec2Classify, p0V, p1V, pAb):朴素贝叶斯分类器分类函数(以标题六代码为准)
  • testingNB(testVec):朴素贝叶斯测试函数

1,构建词向量

留言文本已经被切分好,并且认为标注好类别,用于训练模型。
类别有两类:侮辱性(1);非侮辱性(2)。

创建数据集

def loadDataSet():
    """
    创建实验数据集合
    :return: 切分好的样本词条,类标签向量
    """
    # 切分好的样本词条
    dataSet = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
               ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
               ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
               ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
               ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
               ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
    # 类标签向量
    classVec = [0, 1, 0, 1, 0, 1]   # 1代表侮辱性词汇;0代表非侮辱性词汇
    return dataSet, classVec

dataSet, classVec

dataSet, classVec = loadDataSet()

生成不重复词汇表(set类型中会去除掉相同元素)

def createVocabList(dataSet):
    """
    将切分好的样本词条整理成词汇表(不重复)
    :param dataSet: 切分好的样本词条
    :return: 不重复词汇表
    """
    vocabSet = set()                    # 创建一个空集合
    for doc in dataSet:                 # 遍历每一条言论
        vocabSet = vocabSet | set(doc)  # 取并集
        vocabList = list(vocabSet)
    return vocabList

vocabList

vocabList = createVocabList(dataSet)
>>>['flea', 'has', 'cute', 'licks', 'posting', 'please', 'ate', 'maybe', 'dog',
    'quit', 'I', 'him', 'buying', 'problems', 'steak', 'to', 'food', 'take', 
    'not', 'how', 'help', 'mr', 'stupid', 'so', 'stop', 'dalmation', 'love',
    'park', 'worthless', 'garbage', 'my', 'is']

生成词向量(输入的是一个词条)-- 辅助函数

def setofWords2Vec(vocabList, inputSet):
    """
    生成词向量
    :param vocabList: 词汇表
    :param inputSet: 切分好的词条列表中的一条
    :return: 文档向量,词集模型
    """
    returnVec = [0]*len(vocabList)                # 创建一个与词汇表等长的0向量
    for word in inputSet:                         # 遍历每一个词条
        if word in vocabList:                     # 如果词条存在于词汇表中,则变为1
            returnVec[vocabList.index(word)] = 1  # 更改在该词在词汇表中对应的位置!
        else:
            print(f'{word} is not in my Vocabulary!')
    return returnVec  

生成所有词条的向量

def get_trainMat(dataSet):
    """
    所有词条向量列表
    :param dataSet:切分好的样本词条 
    :return: 所有词条向量组成的列表
    """
    trainMat = []                                         # 初始化向量列表
    vocabList = createVocabList(dataSet)                  # 生成词汇列表              
    for inputList in dataSet:                             # 遍历样本词条中的每一条
        returnVec = setofWords2Vec(vocabList, inputList)  # 将当前词条向量化
        trainMat.append(returnVec)                        # 追加到向量列表中
    return trainMat

trainMat

trainMat = get_trainMat(dataSet)
>>>[[1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], 
    [0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0], 
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1], 
    [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0],
    [0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0], 
    [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0]]

2,朴素贝叶斯分类器训练函数

词向量构建好之后,我们就可以来构建分类器的训练函数了。
(下边代码有问题,经过平滑后的正确代码在“标题六”)

def trainNB(trainMat, classVec):
    """
    朴素贝斯分类器训练函数
    :param trainMat: 训练文档矩阵
    :param classVec: 训练类别标签向量
    :return: p0V:非侮辱类条件概率数组
             p1V:侮辱类条件概率数组
             pAb:文档属于侮辱类的概率
    """
    n = len(trainMat)  # 计算训练函数的文档数目
    m = len(trainMat[0])  # 计算每篇文档的词条数目
    pAb = sum(classVec) / n  # 文档属于侮辱类的概率
    p0Num = np.zeros(m)  # 非侮辱类词条出现数初始化为0
    p1Num = np.zeros(m)  # 侮辱类词条出现数初始化为0
    p0Denom = 0  # 非侮辱类分母初始化为0
    p1Denom = 0  # 侮辱类分母初始化为0

    # 遍历每一个文档
    for i in range(n):
        # 统计侮辱类的条件概率所需要的数据
        if classVec[i] == 1:
            p1Num += trainMat[i]
            p1Denom += sum(trainMat[i])
        # 统计非侮辱类的条件概率所需要的数据
        else:
            p0Num += trainMat[i]
            p0Denom += sum(trainMat[i])

    p1V = p1Num / p1Denom
    p0V = p0Num / p0Denom
    return p0V, p1V, pAb
p0V, p1V, pAb = trainNB(trainMat, classVec)

下边来看一下,这些参数都表达了什么
我们以词汇表中的第一个词‘flea’(蚤)为例

vocabList = createVocabList(dataSet)
>>>['flea', 'has', 'cute', 'licks', 'posting', 'please', 'ate', 'maybe', 'dog',
    'quit', 'I', 'him', 'buying', 'problems', 'steak', 'to', 'food', 'take', 
    'not', 'how', 'help', 'mr', 'stupid', 'so', 'stop', 'dalmation', 'love',
    'park', 'worthless', 'garbage', 'my', 'is']

对应他是非侮辱性词汇的条件概率为 0.04166667

p0V
>>>array([0.04166667, 0.04166667, 0.04166667, 0.04166667, 0.        ,
          0.04166667, 0.04166667, 0.        , 0.04166667, 0.        ,
          0.04166667, 0.08333333, 0.        , 0.04166667, 0.04166667,
          0.04166667, 0.        , 0.        , 0.        , 0.04166667,
          0.04166667, 0.04166667, 0.        , 0.04166667, 0.04166667,
          0.04166667, 0.04166667, 0.        , 0.        , 0.        ,
          0.125     , 0.04166667])

对应他是侮辱性词汇的条件概率为 0.

p1V
>>>array([0.        , 0.        , 0.        , 0.        , 0.05263158,
          0.        , 0.        , 0.05263158, 0.10526316, 0.05263158,
          0.        , 0.05263158, 0.05263158, 0.        , 0.        ,
          0.05263158, 0.05263158, 0.05263158, 0.05263158, 0.        ,
          0.        , 0.        , 0.15789474, 0.        , 0.05263158,
          0.        , 0.        , 0.05263158, 0.10526316, 0.05263158,
          0.        , 0.        ])

可以说 vocabList – p0V – p1V 中的元素一一对应
最后看一下该文档属于侮辱类的概率

pAb
>>>0.5

3,测试朴素贝叶斯分类器

传送门:有关lambda讲解
关于functools中的reduce函数,我把源码放在下边了 ↓
(这里不想看可以直接跳过)

def reduce(function, sequence, initial=None): # real signature unknown; restored from __doc__
    """
    reduce(function, sequence[, initial]) -> value
    
    将两个参数的函数累加应用于序列的项,
	从左到右,以便将序列减少到单个值。
    For example, 
    reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])
    表示((((1+2)+3)+4)+5).  
	如果有首字母,则将其放在项目之前计算中的序列,
	并在序列为空。
    """
    pass

朴素贝叶斯分类器分类函数
(下边代码有问题,经过平滑后的正确代码在“标题六”)

from functools import reduce


def classifyNB(vec2Classify, p0V, p1V, pAb):
    """
    朴素贝叶斯分类器分类函数
    :param vec2Classify:待分类词条数组
    :param p0V: 非侮辱类条件概率数组
    :param p1V: 侮辱类条件概率数组
    :param pAb: 文档属于侮辱类的概率
    :return: 0(非侮辱类),1(侮辱类)
    """
    # 对应元素相乘
    p1 = reduce(lambda x, y: x * y, vec2Classify * p1V) * pAb
    p0 = reduce(lambda x, y: x * y, vec2Classify * p0V) * (1 - pAb)
    print('p0:', p0)
    print('p1:', p1)
    if p1 > p0:
        return 1
    else:
        return 0

朴素贝叶斯测试函数

def testingNB(testVec):
    """
    朴素贝叶斯测试函数
    :param testVec:测试样本 (词条)
    :return: 测试样本的类别
    """
    dataSet, classVec = loadDataSet()  # 创建实验样本
    vocabList = createVocabList(dataSet)  # 创建词汇表
    trainMat = get_trainMat(dataSet)  # 将实验样本向量化
    p0V, p1V, pAb = trainNB(trainMat, classVec)  # 训练分类器
    thisone = setofWords2Vec(vocabList, testVec)  # 测试样本向量化

    # 执行分类,并打印结果
    if classifyNB(thisone, p0V, p1V, pAb):
        print(testVec, '属于侮辱类')
    else:
        print(testVec, '属于非侮辱类')

手动星标(一定要看)

测试样本1

testVec1 = ['love','my','bed']
testingNB(testVec1)
>>>p0: 0.0
   p1: 0.0
['love', 'my', 'bed'] 属于非侮辱类

测试样本2

testVec2 = ['stupid','garbage']
testingNB(testVec2)
>>>p0: 0.0
   p1: 0.0
['stupid', 'garbage'] 属于非侮辱类

这里会发现,这样写的算法无法进行分类,p0和p1的计算结果都为0,显示结果错误。这是为什么呢?
(还记p0V和p1V中的那些0吗,就是他们在搞事情)

六,朴素贝叶斯改进之拉普拉斯平滑

利用贝叶斯分类器对文档进行分类时,要计算多个概率乘积以获得文档属于某个类别的概率,即计算P(w1|1)P(w2|1)P(w3|1)。如果其中一个值为0,那么最后的乘积也为0。显然这样是不合理的,为了降低这种影响可以将所有词的出现数初始化为1,并将分母初始化为2。这种做法就叫做拉普拉斯平滑(Laplace Smoothing)又被称为加1平滑,是比较常用的平滑方法,他就是为了解决0概率问题。

另外一个遇到的问题就是下溢出,这是由于太多很小的数相乘造成的。我们在计算乘积时,由于大部分因子都很小,所以程序会下溢或者得不到正确答案。为了解决这个问题,对乘积的结果取自然对数。通过求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。

下图给出函数 f(x)ln(f(x)) 的曲线:
在这里插入图片描述
检查这两条曲线就会发现他们在相同区域内同时增加或者减少(x相同时,导数的正负相同),并且在相同点上取到极值。他们的取值虽然不同但不影响最终结果。因此可以将代码修改如下:

def trainNB(trainMat, classVec):
    """
    朴素贝斯分类器训练函数
    :param trainMat: 训练文档矩阵
    :param classVec: 训练类别标签向量
    :return: p0V:非侮辱类条件概率数组
             p1V:侮辱类条件概率数组
             pAb:文档属于侮辱类的概率
    """
    n = len(trainMat)  # 计算训练函数的文档数目
    m = len(trainMat[0])  # 计算每篇文档的词条数目
    pAb = sum(classVec) / n  # 训练文档属于侮辱类的概率
    p0Num = np.ones(m)  # 非侮辱类词条出现数初始化为1
    p1Num = np.ones(m)  # 侮辱类词条出现数初始化为1
    p0Denom = 2  # 非侮辱类分母初始化为2
    p1Denom = 2  # 侮辱类分母初始化为2

    # 遍历每一个文档
    for i in range(n):
        # 统计侮辱类的条件概率所需要的数据
        if classVec[i] == 1:
            p1Num += trainMat[i]
            p1Denom += sum(trainMat[i])
        # 统计非侮辱类的条件概率所需要的数据
        else:
            p0Num += trainMat[i]
            p0Denom += sum(trainMat[i])

    p1V = np.log(p1Num / p1Denom)
    p0V = np.log(p0Num / p0Denom)
    return p0V, p1V, pAb

(修改:分子初始化改为1,分母初始化改为2,对概率取log)

def classifyNB(vec2Classify, p0V, p1V, pAb):
    """
    朴素贝叶斯分类器分类函数
    :param vec2Classify:待分类词条数组
    :param p0V: 非侮辱类条件概率数组
    :param p1V: 侮辱类条件概率数组
    :param pAb: 文档属于侮辱类的概率
    :return: 0(非侮辱类),1(侮辱类)
    """
    # 对应元素相乘
    p1 = sum(vec2Classify * p1V) + np.log(pAb)
    p0 = sum(vec2Classify * p0V) + np.log(1 - pAb)
    
    if p1 > p0:
        return 1
    else:
        return 0

(因为loga*logb = log(a+b),所以改成sum就ok)

然后再重新测试一下代码

testVec1 = ['love','my','bed']
testingNB(testVec1)
>>>bed is not in my Vocabulary!
['love', 'my', 'bed'] 属于非侮辱类
testVec2 = ['stupid','dog']
testingNB(testVec2)
>>>['stupid', 'dog'] 属于侮辱类

(这会就可以啦~)

(2020年3月28日21:20:47)

发布了17 篇原创文章 · 获赞 4 · 访问量 511

猜你喜欢

转载自blog.csdn.net/weixin_46072771/article/details/105156333
今日推荐