python | sklearn ,做一个调包侠来解决新闻文本分类问题

https://zhuanlan.zhihu.com/p/30455047?utm_medium=social&utm_source=qq

本文介绍一下使用朴素贝叶斯算法来做文本分类任务。

数据集是搜狗新闻数据集“corpus_6_4000”,它包含六大类新闻,每类新闻4000篇,每篇新闻长度在几百到几千字不等。六类新闻分别是'Auto', 'Culture', 'Economy', 'Medicine', 'Military', 'Sports'。今天的任务就是使用监督学习算法(朴素贝叶斯)来实现文本自动分类问题。话不多说,让我们开干吧!

1、数据探索分析和预处理

首先看看数据集是什么样的

file_dir = './corpus_6_4000'

file_list = os.listdir(file_dir)
file_list[:5]

"""
['Auto_0.txt', 'Auto_1.txt', 'Auto_10.txt', 'Auto_100.txt', 'Auto_1000.txt']
"""

看一下文件名称,从名称可以看出文件名称就是新闻的类别(标签),因此我们需要把这些标签提取出来,让他们和文本组成一一对应的列表,如[[doc1,label1],[doc2,label2],...]形式。

stop_word = []
with open('stopwords_cn.txt','r') as f:
    for word in f.readlines():
        stop_word.append(word.strip())

data_set = []
for file in file_list:
    doc_label = []
    file_path = file_dir + '/' + file
    with open(file_path) as f:
        data = f.read()
    data = re.sub('[a-zA-Z0-9]+','',data.strip())
    data = jieba.cut(data)
    datas = [word for word in data if word not in stop_word and len(word)>1]
    doc_label.append(datas)
    label = file.split('_')[0]
    doc_label.append(label)

    data_set.append(doc_label)

stop_word为加载的停用词列表,data_set存储新闻文档,doc_label存储标签。在这里我们将文档中的数字字母都去掉了,而且分词之后,把单个的字也去掉了;因为我觉得这些字对文档的特征表达意义不大。

接下来将数据集的顺序随机打乱,并且保存下来,供之后使用。

random.shuffle(data_set)
pickle.dump(data_set,open('./data/data_set.pkl','wb'))

df = pd.DataFrame(data_set,columns=['data','label'])
df['label'] = df['label'].map(map_list)

data = df['data'].tolist()
label = df['label'].tolist()

train_data = data[:16800]
test_data = data[16800:]

train_label = label[:16800]
test_label = label[16800:]

pickle.dump(train_data,open('./data/train_data.pkl','wb'))
pickle.dump(test_data,open('./data/test_data.pkl','wb'))
pickle.dump(train_label,open('./data/train_label.pkl','wb'))
pickle.dump(test_label,open('./data/test_label.pkl','wb'))

我将24000篇新闻的80%用于训练模型,20%用于测试。

2、特征工程

老话说的好,机器学习中,特征的好坏决定了你的模型性能的上限,而算法的好坏决定你能否逼近这个上限。因此,特征工程非常重要。那么怎么对文本进行构造特征?由于计算机是不能对字符串进行计算的,因此我们需要将文本进行数字化,也就向量化,我们把每一篇新闻用一个向量表示,那么怎样用向量表示呢?

第一个思路是使用关键词词频,也就是哪些词出现的次数多,我就把他们作为关键词,然后构造向量空间模型。经过对训练数据集的统计发现:

[('中国', 43812),
('一个', 29356),
('市场', 23137),
('汽车', 22275),
('没有', 20719),
('已经', 17960),
('发展', 17408),
('进行', 16574),
('目前', 15792),
('公司', 15480),
('问题', 15072),
('表示', 14901),
('记者', 14688),
('文化', 14311),
('可能', 13462),
('工作', 13163),
('国家', 13156),
('北京', 12984),
('网易', 12710),
('认为', 11984),
('.%', 11888),
('美国', 11732),
('比赛', 11615),
('经济', 11422),
('成为', 11016),
('企业', 10861),
('方面', 10685),
('车型', 10465),
('现在', 10403),
('医院', 10308)]

我们看到有相当多高词频的词根本是对分类没有任何意义的,比如”中国“,”一个“,”目前“等,如果把词频最高的一些词作为特征去构造特征,这样数据集中将有很大的噪声,并且无法分类,我选取了词频最高的5000个词,10000个词,15000个词分别作了实验,准确率都只有16%左右。因此很明显,选用词频构造特征完全不行。

于是我们把目光转向TF-IDF,TF-IDF指词频和逆文档频率。词频计算公式如下:

公式里除以文章总词数是为了消除不同文章的长短不同所带来的影响。词频也可以如下计算:

逆文档频率计算公式如下:

IF-IDF就是将两者相乘

TF-IDF的具体意思,以及它为什么能做表示文章特征的原因,参看这篇文章

计算TF-IDF可以使用sklearn库里面的函数进行计算,但是今天,我自己动手实现了一下,也让大家能更好的理解TF-IDF。

def make_idf_vocab(train_data):
    if os.path.exists('./data/idf.pkl'):
        idf = pickle.load(open('./data/idf.pkl','rb'))
        vocab = pickle.load(open('./data/vocab.pkl','rb'))
    else:
        word_to_doc = {}
        idf = {}
        total_doc_num = float(len(train_data))

        for doc in train_data:
            for word in set(doc):
                if word not in word_to_doc.keys():
                    word_to_doc[word] = 1
                else:
                    word_to_doc[word] += 1

        for word in word_to_doc.keys():
            if word_to_doc[word] > 10:
                idf[word] = np.log(total_doc_num/(word_to_doc[word]+1))

        sort_idf = sorted(idf.items(),key=lambda x:x[1])
        vocab = [x[0] for x in sort_idf]
        pickle.dump(idf,open('./data/idf.pkl','wb'))
        pickle.dump(vocab,open('./data/vocab.pkl','wb'))
    return idf,vocab

word_to_doc字典存储每一个词在多少篇文章出现,vocab存储经过idf值从大到小排序后的词的列表。本文中我将那些只在10篇及以下的新闻中出现的那些词排除掉了。

接下来实现计算文档词频的函数。

def cal_term_freq(doc):
    term_freq = {}
    for word in doc:
        if word not in term_freq.keys():
            term_freq[word] = 1
        else:
            term_freq[word] += 1
    for word in term_freq.keys():
        term_freq[word] = term_freq[word]/float(len(doc))
    return term_freq

计算单个文档的词频,完全按照以上公式来的。

接下来实现构造文档特征的函数。

def make_doc_feature(vocab,idf,doc,topN):
    doc_feature = [0.]*topN
    vocab = vocab[:topN]
    tf = cal_term_freq(doc)
    for word in doc:
        if word in vocab:
            index = vocab.index(word)
            doc_feature[index] = tf[word]*idf[word]
    return doc_feature

topN确定构造多少维的特征向量,维数越高,包含的信息也越多,但是噪声也会越多,而且会增加计算难度。

将训练数据集矩阵转换成tfidf权重矩阵。

def make_tfidf(train_data,vocab,idf,topN):
    tfidf_data = []
    for doc in train_data:
        doc_feature = make_doc_feature(vocab,idf,doc,topN)
        tfidf_data.append(doc_feature)
    return tfidf_data

到这里,特征工程完成了!

3、训练模型

我先后使用了多项式朴素贝叶斯算法和K近邻算法来进行分类,发现朴素贝爷效果更好;这里使用多项式贝叶斯的原因是,我们的tfidf特征值是0-1之间的实数,是连续的,而伯努利贝叶斯适合离散型的特征。

train_data = pickle.load(open('./data/train_data.pkl','rb'))
train_label = pickle.load(open('./data/train_label.pkl','rb'))

idf,vocab = make_idf_vocab(train_data)
tfidf_data = make_tfidf(train_data,vocab,idf,6000)
train_x = np.array(tfidf_data[:13500])
train_y = np.array(train_label[:13500])
val_x = np.array(tfidf_data[13500:])
val_y = np.array(train_label[13500:])

我使用13500个文本作为训练集,剩下的3300个文本作为验证集。另外,我topN取6000,也就是我选6000个idf值排前的特征词来构文本造特征向量。

在验证集上准确率达到94%,非常不错,所以我就没有调参,打算直接用这个模型去测试集上试试。

另外使用KNN的结果如下,只有59%的准确率,差很多。

朴素贝叶斯算法在测试集上的结果如下:

和验证集上的结果差不多。

所以可以看到做文本分类时,采用TF-IDF作为文本的特征效果是非常不错的。另外我们也可以采用互信息作为特征。。

### 回答1: Python中有很多现成的库可以实现tf-idf算法,比如scikit-learn和gensim等。以下是使用scikit-learn库实现tf-idf算法的示例代码: ```python from sklearn.feature_extraction.text import TfidfVectorizer # 定义文本列表 corpus = [ 'This is the first document.', 'This is the second second document.', 'And the third one.', 'Is this the first document?', ] # 创建TfidfVectorizer对象 vectorizer = TfidfVectorizer() # 对文本列表进行tf-idf向量化 X = vectorizer.fit_transform(corpus) # 输出向量化结果 print(X.toarray()) ``` 输出结果为: ``` [[0. 0.46979139 0.58028582 0.46979139 0. 0. 0.38408524 0. ] [0. 0.6876236 0. 0.28108867 0. 0.53864762 0.28108867 0. ] [0.57735027 0. 0. 0. 0.57735027 0. 0. 0.57735027] [0. 0.46979139 0.58028582 0.46979139 0. 0. 0.38408524 0. ]] ``` 可以看到,每个文本被转换成了一个向量,向量的每个元素代表了该文本中每个词的tf-idf值。 ### 回答2: TF-IDF算法是信息检索领域中的一种经典算法,用于评估一个词语在单篇文档或整个文集中的重要程度。在Python中,我们可以通过调用现成的第三方包来实现TF-IDF算法。 目前,Python中广泛使用的开源自然语言处理库是nltk(Natural Language Toolkit)和gensim。在这两个库中,gensim被认为是更适合处理大规模语料库的库。因此,我们在本文中将以gensim包为例,讲解如何使用Python实现TF-IDF算法的调包方法。 1. 安装gensim包 首先,我们需要安装gensim包。你可以通过pip在终端中输入以下命令来安装: ``` pip install -U gensim ``` 2. 导入gensim库 成功安装后我们需要在Python代码中导入gensim库,以方便调用TF-IDF算法。导入方式为: ``` import gensim ``` 3. 准备语料库 在使用gensim库中的TF-IDF算法计算文本相似度之前,我们需要先准备语料库。语料库包括一组文本或单词集合,其中每个文本、文档或语料库都对应着一个向量。在gensim库中,我们可以用List对象或一个文件来表示一个语料库,其中每个元素表示语料库中的一个文档或一行文本。 4. 创建词向量模型 在得到语料库之后,我们可以使用gensim库中的TfidfModel函数来创建文本的词向量模型。代码实现如下: ``` from gensim import corpora, models corpus = ['这是第一个文档', '这是第二个文档', '这是第三个文档'] texts = [[word for word in doc.split()] for doc in corpus] # 创建词袋模型 dictionary = corpora.Dictionary(texts) # 统计词语出现的次数 corpus_bow = [dictionary.doc2bow(text) for text in texts] # 计算TF-IDF权重 tfidf = models.TfidfModel(corpus_bow) ``` 上述代码中,我们首先将原始语料库转化为一个List对象(corpus),接着将每个文档按照单词进行分割,将结果存储到List对象texts中。之后,我们使用gensim库中的corpora.Dictionary函数创建了一个词袋模型。通过将texts中的每个文档转化为其相应的单词索引形式并统计每个单词的出现次数,我们得到了一个包含所有单词的字典(dictionary)以及每篇文档相对应的稀疏向量(corpus_bow)。 最后,我们通过TfidfModel函数计算每篇文档中的每个单词的TF-IDF权重值。通过设置normalize参数为True,我们可以对每个文档中的所有单词的权重进行标准化。 5. 计算相似度 通过上述步骤,我们已经得到了每个文档的词向量模型。接下来,我们还可以使用TF-IDF算法来计算文本之间的相似度。 在gensim库中,我们可以通过使用文本的稀疏向量表示形式来计算两个文本之间的相似度。举个例子,我们可以使用TF-IDF模型中的similarities函数来计算第一个文本与后两个文本之间的相似度。具体实现代码如下: ``` # 计算稀疏向量表示形式 doc1_bow = dictionary.doc2bow(texts[0]) doc2_bow = dictionary.doc2bow(texts[1]) doc3_bow = dictionary.doc2bow(texts[2]) # 计算文本的相似度 doc1_tfidf = tfidf[doc1_bow] doc2_tfidf = tfidf[doc2_bow] doc3_tfidf = tfidf[doc3_bow] similarity_1_2 = similarities.MatrixSimilarity([doc1_tfidf, doc2_tfidf]) print(similarity_1_2) ``` 在这段代码中,我们首先将第一个文本转换为其相应的稀疏向量(doc1_bow),然后使用tfidf函数计算该文本的TF-IDF权重(doc1_tfidf)。接着,我们分别计算第一个文本和第二个文本的相似度,将它们转换为相似度矩阵形式。 需要注意的是,在大规模语料库中,计算相似度的时间可能会非常长。为了避免这种情况,我们可以使用LSI或LSA等方法来降低文本表示空间的维度,以此提高计算速度,同时保持语义相似性不变。 ### 回答3: tf-idf是一种计算文本相似度的方法,在文本处理和自然语言处理中广泛应用。Python语言是一种流行的编程语言,其强大的文本处理功能使它成为实现tf-idf算法的好选择。Python社区中提供了许多流行的库,如Scikit-learn,Gensim等,可以方便地实现tf-idf算法。 在使用Python实现tf-idf算法时,我们可以使用Scikit-learn库中的TfidfVectorizer()方法。TfidfVectorizer()方法将文本数据集转换为tf-idf权重矩阵。它可以自动完成文本的预处理、标记化、停用词移除等任务。以下是Python实现tf-idf算法的步骤: 1. 导入必要的库:首先需要导入用于文本处理和tf-idf计算的库,如numpy、pandas和Scikit-learn中的TfidfVectorizer()方法。 2. 数据预处理:对原始文本进行预处理,包括去除特殊符号、标点符号、停用词等。 3. TfidfVectorizer()参数设置:设置TfidfVectorizer()方法的参数,例如,ngram_range、max_features、tokenizer等。 4. 计算tf-idf权重矩阵:使用TfidfVectorizer()方法计算tf-idf权重矩阵。 5. 选取关键词:根据tf-idf权重矩阵选取权重高的关键词。 6. 可视化结果:将选取的关键词可视化展示,帮助理解文本的主题和内容。 实现tf-idf算法的Python代码如下: ``` import numpy as np import pandas as pd from sklearn.feature_extraction.text import TfidfVectorizer # 数据预处理 # 实例化 TfidfVectorizer tfidf_vectorizer = TfidfVectorizer(stop_words='english') # 加载数据 data = ["This is a sample document.", "Another document for practice.", "This is a sample sentence."] # 计算tf-idf权重矩阵 tfidf = tfidf_vectorizer.fit_transform(data) # 打印tf-idf矩阵 tfidf.toarray() # 选取关键词 # 获取特征名称 feature_names = tfidf_vectorizer.get_feature_names() # 定义一个函数,用于获取tf-idf权重 top n 关键词 def get_top_n_words(tfidf_vectorizer, tfidf_matrix, n): sorted_nzs = np.argsort(tfidf_matrix.data)[:-(n + 1):-1] return feature_names[tfidf_matrix.indices[sorted_nzs]] # 选取权重最高的前5个关键词 print(get_top_n_words(tfidf_vectorizer, tfidf, 5)) # 可视化结果 import matplotlib.pyplot as plt # 可视化选取的关键词 fig, ax = plt.subplots(figsize=(8, 6)) ax.bar(get_top_n_words(tfidf_vectorizer, tfidf, 5), tfidf.idf_[sorted_indices]) ax.set_ylabel('idf score') ax.set_xlabel('word') ax.set_title('Top 5 Words by TF-IDF Weight') plt.show() ``` 使用Python实现tf-idf算法,我们可以很方便地处理文本数据、计算tf-idf权重矩阵、选取关键词和可视化结果。Python的Scikit-learn库提供了许多有用的方法和函数,使我们能够更轻松地实现tf-idf算法,从而更好地分析和理解文本数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值