程序员

朴素贝叶斯分类-实战篇-如何进行文本分类

2021-01-20  本文已影响0人  码农充电站pro

上篇介绍了朴素贝叶斯的原理,本篇来介绍如何用朴素贝叶斯解决实际问题。

朴素贝叶斯最擅长的领域是文本分析,包括:

要对文本进行分类,首先要做的是如何提取文本的主要信息,如何衡量哪些信息是文本中的主要信息呢?

1,对文档分词

我们知道,一篇文档是由若干词汇组成的,也就是文档的主要信息是词汇。从这个角度来看,我们就可以用一些关键词来描述文档。

这种处理文本的方法叫做词袋(bag of words)模型,该模型会忽略文本中的词语出现的顺序以及相应的语法,将文档看做是由若干单词组成的,且单词之间互相独立,没有关联。

要想提取文档中的关键词,就得先对文档进行分词。分词方法一般有两种:

停用词是一些非常普遍使用的词语,对文档分析作用不大,在文档分析之前需要将这些词去掉。比如:

另外分词阶段,还需要处理同义词,很多时候一件东西有多个不同的名字。比如“番茄”和“西红柿”,“凤梨”和“菠萝”等。

中文分词与英文分词是不同的,我们分别介绍一个著名的分词包:

2,计算单词权重

哪些关键词对一个文档才是重要的?比如可以通过单词出现的次数,次数越多就表示越重要。

更合理的方法是计算单词的TF-IDF 值。

2.1,单词的 TF-IDF 值

单词的TF-IDF 值可以描述一个单词对文档的重要性,TF-IDF 值越大,则越重要。



IDF 是一个相对权重值,公式中log 的底数可以自定义,一般可取2,10,e 为底数。

假设我们现在有一篇文章,文章中共有2000 个单词,“中国”出现100 次。假设全网共有1 亿篇文章,其中包含“中国”的有200 万篇。现在我们要求“中国”的TF-IDF值。

计算过程如下:

TF(中国) = 100 / 2000 = 0.05
IDF(中国) = log(1亿/(200万+1)) = 1.7 # 这里的log 以10 为底
TF-IDF(中国) = 0.05 * 1.7 = 0.085

通过计算文档中单词的TF-IDF 值,我们就可以提取文档中的特征属性,就是把TF-IDF 值较高的单词,作为文档的特征属性。

2.2,TfidfVectorizer 类

sklearn 库的 feature_extraction.text 模块中的 TfidfVectorizer 类,可以计算 TF-IDF 值。

TfidfVectorizer 类的原型如下:

TfidfVectorizer(*, 
  input='content', 
  encoding='utf-8', 
  decode_error='strict', 
  strip_accents=None, 
  lowercase=True, 
  preprocessor=None, 
  tokenizer=None, 
  analyzer='word', 
  stop_words=None, 
  token_pattern='(?u)\b\w\w+\b', 
  ngram_range=(1, 1), 
  max_df=1.0, 
  min_df=1, 
  max_features=None, 
  vocabulary=None, 
  binary=False, 
  dtype=<class 'numpy.float64'>, 
  norm='l2', 
  use_idf=True, 
  smooth_idf=True, 
  sublinear_tf=False)

常用的参数有:

常用的方法有:

在上面三个方法中:

fit_transformtransform 的用法

下图表达的很清晰明了:

所以一般的使用步骤是:

# x 为 DictVectorizer,DictVectorizer 等类的对象
# 用于特征提取
x = XXX()

train_features = x.fit_transform(train_datas)
test_features = x.transform(test_datas)

2.3,一个例子

比如我们有如下3 个文档(docs 的每个元素表示一个文档):

docs = [ 
    'I am a student.',
    'I live in Beijing.',
    'I love China.', 
]

我们用 TfidfVectorizer 类来计算TF-IDF 值:

from sklearn.feature_extraction.text import TfidfVectorizer
t = TfidfVectorizer() # 使用默认参数

fit_transform() 方法拟合模型,反回矩阵:

t_matrix = t.fit_transform(docs)

get_feature_names() 方法获取所有不重复的特征词:

>>> t.get_feature_names()
['am', 'beijing', 'china', 'in', 'live', 'love', 'student']

不知道你有没有发现,这些特征词中不包含ia ?你能解释一下是为什么吗?

vocabulary_ 属性获取特征词与ID 的对应关系:

>>> t.vocabulary_
{'am': 0, 'student': 6, 'live': 4, 'in': 3, 'beijing': 1, 'love': 5, 'china': 2}

矩阵对象toarray() 方法输出 TF-IDF 值:

>>> t_matrix.toarray()
array([
  [0.70710678, 0.        , 0.        , 0.        , 0.        , 0.        , 0.70710678],
  [0.        , 0.57735027, 0.        , 0.57735027, 0.57735027, 0.        , 0.        ],
  [0.        , 0.        , 0.70710678, 0.        , 0.        , 0.70710678, 0.        ]
])

3,sklearn 朴素贝叶斯的实现

sklearn 库中的 naive_bayes 模块实现了 5 种朴素贝叶斯算法:

  1. naive_bayes.BernoulliNB 类:伯努利朴素贝叶斯的实现。
    • 适用于离散型数据,适合特征变量是布尔变量,符合 0/1 分布,在文档分类中特征是单词是否出现
    • 该算法以文件为粒度,如果该单词在某文件中出现了即为 1,否则为 0。
  2. naive_bayes.CategoricalNB 类:分类朴素贝叶斯的实现。
  3. naive_bayes.GaussianNB 类:高斯朴素贝叶斯的实现。
    • 适用于特征变量是连续型数据,符合高斯分布。比如说人的身高,物体的长度等,这种自然界物体
  4. naive_bayes.MultinomialNB 类:多项式朴素贝叶斯的实现。
    • 适用于特征变量是离散型数据,符合多项分布。在文档分类中特征变量体现在一个单词出现的次数,或者是单词的 TF-IDF 值等。
  5. naive_bayes.ComplementNB 类:补充朴素贝叶斯的实现。
    • 是多项式朴素贝叶斯算法的一种改进。

每个类名中的NB 后缀是 Naive Bayes 的缩写,即表示朴素贝叶斯

各个类的原型如下:

BernoulliNB(*, alpha=1.0, binarize=0.0, fit_prior=True, class_prior=None)
CategoricalNB(*, alpha=1.0, fit_prior=True, class_prior=None)
GaussianNB(*, priors=None, var_smoothing=1e-09)
MultinomialNB(*, alpha=1.0, fit_prior=True, class_prior=None)
ComplementNB(*, alpha=1.0, fit_prior=True, class_prior=None, norm=False)

构造方法中的alpha 的含义为平滑参数

4,构建模型

我准备了一个实战案例,目录结构如下:

naive_bayes\
    ├── stop_word\
    │   └── stopword.txt
    ├── test_data\
    │   ├── test_economy.txt
    │   ├── test_fun.txt
    │   ├── test_health.txt
    │   └── test_sport.txt
    ├── text_classification.py
    └── train_data\
        ├── train_economy.txt
        ├── train_fun.txt
        ├── train_health.txt
        └── train_sport.txt

其中:

这些数据是一些新闻数据,每条数据包含了新闻类型新闻标题,类型有以下四种:

我们的目的是训练一个模型,该模型的输入是新闻标题,模型的输出是新闻类型,也就是想通过新闻标题来判断新闻类型。

来看下数据的样子,每类数据抽取了一条:

财经---11月20日晚间影响市场重要政策消息速递
娱乐---2020金鸡港澳台影展曝片单 修复版《蝶变》等将映
健康---全面解析耳聋耳鸣,让你不再迷茫它的危害
体育---中国军团1人已进32强!赵心童4-1晋级,丁俊晖颜丙涛将出战

可以看到,每条数据以--- 符号分隔,前边是新闻类型,后边是新闻标题。

下面来看下代码:

import os
import sys
import jieba
import warnings
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn import metrics

warnings.filterwarnings('ignore')

if sys.version.startswith('2.'):
    reload(sys)
    sys.setdefaultencoding('utf-8')


def load_file(file_path):
    with open(file_path) as f:
        lines = f.readlines()

    titles = []
    labels = []

    for line in lines:
        line = line.encode('unicode-escape').decode('unicode-escape')
        line = line.strip().rstrip('\n')

        _lines = line.split('---')
        if len(_lines) != 2:
            continue

        label, title = _lines
        words = jieba.cut(title)

        s = ''
        for w in words:
            s += w + ' '

        s = s.strip()

        titles.append(s)
        labels.append(label)

    return titles, labels


def load_data(_dir):
    file_list = os.listdir(_dir)

    titles_list = []
    labels_list = []

    for file_name in file_list:
        file_path = _dir + '/' + file_name

        titles, labels = load_file(file_path)

        titles_list += titles
        labels_list += labels

    return titles_list, labels_list


def load_stopwords(file_path):
    with open(file_path) as f:
        lines = f.readlines()

    words = []
    for line in lines:
        line = line.encode('unicode-escape').decode('unicode-escape')
        line = line.strip('\n')
        words.append(line)

    return words


if __name__ == '__main__':
    # 加载停用词
    stop_words = load_stopwords('stop_word/stopword.txt')

    # 加载训练数据
    train_datas, train_labels = load_data('train_data')

    # 加载测试数据
    test_datas, test_labels = load_data('test_data')

    # 计算单词权重
    tf = TfidfVectorizer(stop_words = stop_words, max_df = 0.5)
    train_features = tf.fit_transform(train_datas)
    test_features = tf.transform(test_datas) 

    # 多项式贝叶斯分类器
    clf = MultinomialNB(alpha = 0.001).fit(train_features, train_labels)

    # 预测数据
    predicted_labels = clf.predict(test_features)

    # 计算准确率
    score = metrics.accuracy_score(test_labels, predicted_labels)
    print score

说明:

5,如何存储模型

实际应用中,训练一个模型需要大量的数据,也就会花费很多时间。

为了方便使用,可以将训练好的模型存储到磁盘上,在使用的时候,直接加载出来就可以使用。

可以使用 sklearn 中的 joblib 模块来存储和加载模型:

在上边的例子用,我们需要存储两个对象,分别是:

存储代码如下:

from sklearn.externals import joblib
>>> joblib.dump(clf, 'nb.pkl') 
['nb.pkl']
>>> joblib.dump(tf, 'tf.pkl') 
['tf.pkl']

使用模型代码如下:

import jieba
import warnings
from sklearn.externals import joblib

warnings.filterwarnings('ignore')

MODEL = None
TF = None

def load_model(model_path, tf_path):
    global MODEL 
    global TF

    MODEL = joblib.load(model_path)
    TF = joblib.load(tf_path)

def nb_predict(title):
    assert MODEL != None and TF != None
    
    words = jieba.cut(title)
    s = ' '.join(words)

    test_features = TF.transform([s]) 
    predicted_labels = MODEL.predict(test_features)

    return predicted_labels[0]

if __name__ == '__main__':
    # 加载模型
    load_model('nb.pkl', 'tf.pkl')

    # 测试
    print nb_predict('东莞市场采购贸易联网信息平台参加部委首批联合验收')
    print nb_predict('留在中超了!踢进生死战决胜一球,武汉卓尔保级成功')
    print nb_predict('陈思诚全新系列电影《外太空的莫扎特》首曝海报 黄渤、荣梓杉演父子')
    print nb_predict('红薯的好处 常吃这种食物能够帮你减肥')

其中:

6,总结

本篇文章介绍了如何利用朴素贝叶斯处理文本分类问题:

(本节完。)


推荐阅读:

决策树算法-理论篇-如何计算信息纯度
决策树算法-实战篇-鸢尾花及波士顿房价预测
朴素贝叶斯分类-理论篇-如何通过概率解决分类问题

上一篇 下一篇

猜你喜欢

热点阅读