中文文本分类_特征选择算法初探

0 数据集回顾

一点更改

看过我上篇文章的同学可能还记得我们最后处理得到的训练集,是每个分类一个大txt。这样的数据其实也不是不能处理,比如你做基于词频的特征选择方法时是方便的。但一旦涉及到文档频率,那就比较尴尬,因为你把所有文档合到一起了。。统计不了。

我最终选择的教程是使用MI, IG, CHI做一个初步选择的,所以我按照他所给的函数要求重新做了数据集,是和上篇文章标准语料库的格式一样,把训练集小文档分别分词再储存的。

数据集就交待到这里。(虽然只是短短一段话,从摸索文档格式到更改却是花了我半天时间。。我太菜了)

1 特征选择_谁更重要?

1.1 预处理

OK,你发现在开始工作前还是得预处理。

这次是要把文档与标签合成一个嵌套list。这也是一个大文件,就是那个all_text,里面每一条文档是分开的,且都有自己对应的label。

看过结果后,总结其数据结构为[(‘label’, {‘term’, ‘term’, … }), ‘label’, {‘term’, ‘term’, … }),… ] 这样。

code

def preprocess(raw_text):
    """
    预处理,将文件中读出的str以空格为分隔,转化为list
    :param raw_text: read()出的数据
    :return:   处理好的list
    """
    # 以空格为分隔转成list
    word_li = raw_text.split()
    # 去除空白符
    word_li = [w.strip() for w in word_li if w.strip()]
    # 移除单字词
    word_li = [w for w in word_li if len(w) > 1]
    return word_li


def getDocuments(root_path, catoga_li):
    """
    读取原始文档集并进行预处理
    :param root_path: 文档集所在路径
    :param catoga_li: 文档类别list
    :return: 预处理后的文档列表
    """
    all_text = []
    print("start load files...")
    all_data = load_files(container_path=root_path, categories=catoga_li,
                          encoding="utf-8", decode_error="ignore")
    print("load files over.")

    for label, raw_text in zip(all_data.target, all_data.data):
        word_li = preprocess(raw_text)
        label = all_data.target_names[label]
        all_text.append((label, set(word_li)))
    return all_text


def getVocabulary(all_text):
    """
    获取文档集词汇表
    :param all_text:
    :return:
    """
    global vocabulary
    for label, word_set in all_text:
        vocabulary |= word_set


最后这个vocabulart是一个词语库,将每个不同的词都存到一个list里,用来算后面的卡方和互信息。

1.2 三种特征选择方法

原作者将MI, DF, CHI集成到一个函数里了,emmm它们的具体原理、公式什么的我一向不感冒,过几天写论文时再详细贴出来吧,今天就只debug然后改代码了。所以咱们就先只谈风月,实用主义,能用就行。

互信息(Mutual Information)

原理公式占坑,先上代码


def multual_infomation(N_10, N_11, N_00, N_01):
    """
    互信息计算
    :param N_10:
    :param N_11:
    :param N_00:
    :param N_01:
    :return: 词项t互信息值
    """
    N = N_11 + N_10 + N_01 + N_00
    I_UC = (N_11 * 1.0 / N) * log2((N_11 * N * 1.0) / ((N_11 + N_10) * (N_11 + N_01))) + \
           (N_01 * 1.0 / N) * log2((N_01 * N * 1.0) / ((N_01 + N_00) * (N_01 + N_11))) + \
           (N_10 * 1.0 / N) * log2((N_10 * N * 1.0) / ((N_10 + N_11) * (N_10 + N_00))) + \
           (N_00 * 1.0 / N) * log2((N_00 * N * 1.0) / ((N_00 + N_10) * (N_00 + N_01)))
    return I_UC

文档频率(Document Frequency)

原理公式占坑,先上代码。
额,这个简单,之前也提到过,这次直接说了吧。。原理一句话就能说清楚,基本公式也就在注释里了。


def freq_select(t_doc_cnt, doc_cnt):
    """
    频率特征计算
    :param t_doc_cnt: 类别c中含有词项t的文档数
    :param doc_cnt: 类别c中文档总数
    :return: 词项t频率特征值
    """
    return t_doc_cnt * 1.0 / doc_cnt

卡方检验(CHI)

原理公式占坑,先上代码


def chi_square(N_10, N_11, N_00, N_01):
    """
    卡方计算
    :param N_10:
    :param N_11:
    :param N_00:
    :param N_01:
    :return: 词项t卡方值
    """
    fenzi = (N_11 + N_10 + N_01 + N_00) * (N_11 * N_00 - N_10 * N_01) * (N_11 * N_00 - N_10 * N_01)
    fenmu = (N_11 + N_01) * (N_11 + N_10) * (N_10 + N_00) * (N_01 + N_00)
    if fenmu == 0:
        return 0
    return fenzi * 1.0 / fenmu

集成函数

code

def selectFeatures(documents, category_name, top_k, select_type="chi"):
    """
    特征抽取
    :param documents: 预处理后的文档集
    :param category_name: 类目名称
    :param top_k:  返回的最佳特征数量
    :param select_type: 特征选择的方法,可取值chi,mi,freq,默认为chi
    :return:  最佳特征词序列
    """
    L = []
    # 互信息和卡方特征抽取方法
    if select_type == "chi" or select_type == "mi":
        for t in vocabulary:
            N_11 = 0
            N_10 = 0
            N_01 = 0
            N_00 = 0
            N = 0
            for label, word_set in documents:
                if (t in word_set) and (category_name == label):
                    N_11 += 1
                elif (t in word_set) and (category_name != label):
                    N_10 += 1
                elif (t not in word_set) and (category_name == label):
                    N_01 += 1
                elif (t not in word_set) and (category_name != label):
                    N_00 += 1
                else:
                    print("N error")
                    exit(1)

            if N_00 == 0 or N_01 == 0 or N_10 == 0 or N_11 == 0:
                continue
            # 互信息计算
            if select_type == "mi":
                A_tc = multual_infomation(N_10, N_11, N_00, N_01)
            # 卡方计算
            else:
                A_tc = chi_square(N_10, N_11, N_00, N_01)
            L.append((t, A_tc))
    # 频率特征抽取法
    elif select_type == "freq":
        for t in vocabulary:
            # C类文档集中包含的文档总数
            doc_cnt = 0
            # C类文档集包含词项t的文档数
            t_doc_cnt = 0
            for label, word_set in documents:
                if category_name == label:
                    doc_cnt += 1
                    if t in word_set:
                        t_doc_cnt += 1
            A_tc = freq_select(t_doc_cnt, doc_cnt)
            L.append((t, A_tc))
    else:
        print("error param select_type")
    return sorted(L, key=lambda x: x[1], reverse=True)[:top_k]

打完收工。噢,还有最后一步——

1.3 主函调用

分模块记录的,所以代码结构可能有点乱,凑活看吧。

这里多说一句,上一篇文章我的训练集是每类4000条文档共32000条文档,晚上我在用MI跑数据时,近40分钟才跑完一个类。。这么算来跑完全部得五六个小时,实在遭不住,所以将训练集和测试集分别减半了。

可能是我电脑太烂,能读研的话是该换一台了。。


from sklearn.datasets import load_files

# from pyhanlp import *

train_path = "data_standard/train_splited/"
category_l = ['auto', 'education', 'finance', 'game', 'IT', 'politics', 'sports', 'yule']

#import json
#import codecs
import re
from math import log2
import time

time1 = time.time()

vocabulary = set()

'''
上文各种函数插在这里
'''

if __name__ == "__main__":
    # 读取文档集(需要根据具体类目名称修改)
    category_name_li = category_l

    # 获取文本(根目录需要根据具体类目名称修改)
    all_text = getDocuments(train_path, category_name_li)
    print("all_text len = ", len(all_text))
    f1=open("all_text.txt", 'w', encoding='utf-8')
    f1.write(str(all_text))

    # 读取词汇表
    getVocabulary(all_text)
    print("vocabulary len = ", len(vocabulary))
    f2 = open("vocabulary.txt", 'w', encoding='utf-8')
    f2.write(str(vocabulary))

    # 获取特征词表
    '''
    print("=" * 20, '\n', "  卡方特征选择  \n", "=" * 20)
    feature_select_type = "chi"
    for category_name in category_name_li:
        # 特征抽取,最后一个参数可选值 "chi"卡方,"mi"互信息,"freq"频率
        feature_li = selectFeatures(all_text, category_name, 1000, feature_select_type)
        print(category_name)
        for t, i_uc in feature_li:
            print(t, i_uc)
        print("=" * 10)
'''
    print("=" * 20, '\n', "  互信息特征选择  \n", "=" * 20)
    feature_select_type = "mi"
    for category_name in category_name_li:
        # 特征抽取,最后一个参数可选值 "chi"卡方,"mi"互信息,"freq"频率
        feature_li = selectFeatures(all_text, category_name, 1000, feature_select_type)
        print(category_name)
        f=open("result_MI/"+category_name+'.txt', 'w')
        for t, i_uc in feature_li:
            print(t, i_uc)
            f.write(t+'\t'+str(i_uc)+'\n')
        print("=" * 10)
    print("program finished")

'''
    print("=" * 20, '\n', "  频率特征选择  \n", "=" * 20)
    feature_select_type = "freq"
    for category_name in category_name_li:
        # 特征抽取,最后一个参数可选值 "chi"卡方,"mi"互信息,"freq"频率
        feature_li = selectFeatures(all_text, category_name, 1000, feature_select_type)
        print(category_name)
        for t, i_uc in feature_li:
            print(t, i_uc)
        print("=" * 10)
'''

time2 = time.time()
print(" run time is " + str(time2 - time1) + 's')

2 运行结果

MI_result

目前只跑了MI的一部分,且文档编码忘了encoding='utf-8’了。。所以编译器乱码,记事本打开是这样:
在这里插入图片描述

函数里你可以选输出top k个特征,这些特征子集可以继续用特征选择算法筛选或直接向量化去训练分类器。

3 Reference

感谢知乎用户@baiziyu的专栏文章

如果我能早些发现这个宝藏,能少走很多弯路呀。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,下面我将给出朴素贝叶斯算法进行垃圾短信分类的实现代码,代码实现过程中包括各个步骤的详细注释,帮助您更好地理解实现过程。 ``` # 导入需要的库 import pandas as pd from sklearn.feature_extraction.text import CountVectorizer from sklearn.naive_bayes import MultinomialNB from sklearn.metrics import classification_report from sklearn.model_selection import train_test_split # 1. 读取数据 data = pd.read_csv('spam.csv', encoding='latin-1') # 只保留'v1'(标签)和'v2'(短信内容)两列,分别赋值给label和text label = data['v1'] text = data['v2'] # 2. 数据初探 print('标签数目统计:\n', label.value_counts()) # 输出结果如下: # ham 4825 # spam 747 # Name: v1, dtype: int64 # 3. 数据拆分 text_train, text_test, label_train, label_test = train_test_split(text, label, test_size=0.3, random_state=42) # 4. 特征工程(文本向量化) # 使用CountVectorizer将文本向量化 vectorizer = CountVectorizer() # 对训练数据进行向量化 vectorizer.fit(text_train) text_train = vectorizer.transform(text_train) # 对测试数据进行向量化 text_test = vectorizer.transform(text_test) # 5. 模型构建 # 构建朴素贝叶斯模型 clf = MultinomialNB() # 用训练数据拟合模型 clf.fit(text_train, label_train) # 6. 模型评估及可视化 # 在测试数据上进行预测 label_pred = clf.predict(text_test) # 输出分类报告 print(classification_report(label_test, label_pred)) ``` 以上代码实现了朴素贝叶斯算法进行垃圾短信分类的全过程,其中包括数据读取、数据初探、数据拆分、特征工程、模型构建和模型评估等步骤。您可以根据自己的需要对代码进行修改和完善。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值