天天看點

NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

本人是零基礎的小白,現在從零開始學習NLP,這是學習的一些簡單的筆記,如有錯誤請指正。

編譯環境:Jupyter Notebook

Windows x64

本文資料處理主要分為兩個闆塊:

一 是資料預處理(Data Preparation)進而獲得所需要的特征(feature),如将資料層層處理(分詞、停用詞過濾、向量化),本文向量化内容由于使用sklearn庫,放置第二闆塊講解。

二 是利用模型(Modeling)解決具體的問題,本文主要采用樸素貝葉斯經典機器學習方法對文本進行分類。

基本内容

  • 基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)
    • 一、理論基礎
      • 1.1 詞頻(TF)
      • 1.2 逆向文本頻率(IDF)
      • 1.3 樸素貝葉斯(Naive Bayesian Model,NBM)
    • 二、資料預處理
      • 2.1 資料下載下傳及導入
      • 2.2 結巴分詞及停用詞過濾
        • 2.2.1 結巴分詞:
        • 2.2.2 停用詞過濾:
    • 三、模型(modeling)貝葉斯分類器
      • 3.1 文本資料向量化
        • 3.1.1 基于詞頻向量化
        • 3.1.2 基于TFIDF向量化

一、理論基礎

下面簡單回顧一下理論部分(可以直接跳過到實戰部分)

1.1 詞頻(TF)

詞頻(term frequency) 指的是某一個給定的詞語在該檔案中出現的頻率。對于在某一檔案裡的詞語 t i t_i ti​來說,它的重要性可表示為:

t f i j = n i , j ∑ k n k , j {tf}_{ij}=\frac{n_{i,j}}{\sum_kn_{k,j}} tfij​=∑k​nk,j​ni,j​​

其中, n i , j n_{i,j} ni,j​是該詞在檔案 d j d_j dj​中出現次數,而分母是檔案 d j d_j dj​中所有字詞出現的次數總和。

1.2 逆向文本頻率(IDF)

逆向檔案頻率(inverse document frequency) 是一個詞語普遍重要性的度量。某一特定詞語的idf,可以由總檔案數目除以包含該詞語之檔案的數目,再将得到的商取以10為底的對數得到,個人了解為:對詞頻向量的改進,原因在于:詞語出現的越多,并不能代表它就越重要,相反,文檔中出現的越多,其實它的重要性是降低的,是以TFIDF考慮了單詞的重要性而做的對詞頻的改進,可表示為:

t f i d f ( w ) = t f ( d , w ) × i d f ( w ) tfidf(w)=tf(d,w)\times{idf(w)} tfidf(w)=tf(d,w)×idf(w)

(1)其中 t f ( d , w ) tf(d,w) tf(d,w) 代表文檔d中w的詞頻

(2) i d f ( w ) = log ⁡ N N ( w ) idf(w)=\log\frac{N}{N(w)} idf(w)=logN(w)N​, N {N} N代表語料庫中的文檔總數, N ( w ) {N(w)} N(w)代表詞語w出現在多少個文檔中,出現在文檔的次數越多, log ⁡ \log log值越小,故稱為逆向文本頻率

1.3 樸素貝葉斯(Naive Bayesian Model,NBM)

樸素貝葉斯的中心思想,在于利用各類别在訓練樣本中的分布以及類别中各特征元素的分布,計算後驗機率,使用極大似然法判斷測試樣本所屬,一般用于簡單分類。

貝葉斯公式:

P ( B ∣ A ) = P ( A ∣ B ) P ( B ) P ( A ) P(B\mid{A})=\frac{P(A\mid{B})P(B)}{P(A)} P(B∣A)=P(A)P(A∣B)P(B)​

對應分類任務則為:

P ( 類 别 ∣ 特 征 ) = P ( 特 征 ∣ 類 别 ) P ( 類 别 ) P ( 特 征 ) P(類别\mid{特征})=\frac{P(特征\mid{類别})P(類别)}{P(特征)} P(類别∣特征)=P(特征)P(特征∣類别)P(類别)​

垃圾郵件分類(判别模型)舉例:

P ( 特 征 ∣ 類 别 ) P(特征\mid{類别}) P(特征∣類别) 相當于先驗機率,也就是我們已知的機率,比如垃圾郵件分類裡面,我們已有的資料中正常的類别郵件裡面包含“購買”一詞的機率,以及垃圾類别裡面包含“購買”一次的機率等, P ( 類 别 ) P(類别) P(類别) 就是正常或者垃圾郵件在資料集中的機率,這些機率都已知。

那麼要判斷郵件為正常還是垃圾,則要判斷:

P ( 正 常 ∣ 内 容 ) P(正常\mid内容) P(正常∣内容) 與 P ( 垃 圾 ∣ 内 容 ) P(垃圾\mid内容) P(垃圾∣内容) 的大小

P ( 正 常 ∣ 内 容 ) = P ( 内 容 ∣ 正 常 ) P ( 正 常 ) P ( 内 容 ) P(正常\mid内容)=\frac{P(内容\mid正常)P(正常)}{P(内容)} P(正常∣内容)=P(内容)P(内容∣正常)P(正常)​

P ( 垃 圾 ∣ 内 容 ) = P ( 内 容 ∣ 垃 圾 ) P ( 垃 圾 ) P ( 内 容 ) P(垃圾\mid内容)=\frac{P(内容\mid垃圾)P(垃圾)}{P(内容)} P(垃圾∣内容)=P(内容)P(内容∣垃圾)P(垃圾)​

P ( 正 常 ) P(正常) P(正常), P ( 垃 圾 ) P(垃圾) P(垃圾) 均已知, P ( 内 容 ) P(内容) P(内容)消去,剩下就是要比較 P ( 内 容 ∣ 正 常 ) P(内容\mid正常) P(内容∣正常) 和 P ( 内 容 ∣ 垃 圾 ) P(内容\mid垃圾) P(内容∣垃圾)

P ( 内 容 ∣ 正 常 ) = P ( 購 買 , 物 品 , 廣 告 , 産 品 ∣ 正 常 ) = P ( 購 買 ∣ 正 常 ) P ( 物 品 ∣ 正 常 ) P ( 廣 告 ∣ 正 常 ) P ( 産 品 ∣ 正 常 ) P(内容\mid正常)\\=P(購買,物品,廣告,産品\mid正常)\\ =P(購買\mid正常)P(物品\mid正常)P(廣告\mid正常)P(産品\mid正常) P(内容∣正常)=P(購買,物品,廣告,産品∣正常)=P(購買∣正常)P(物品∣正常)P(廣告∣正常)P(産品∣正常),而這些先驗機率前面都已算過,帶入計算作比較大小即可。

二、資料預處理

資料預處理部分可謂是耗費了大部分的時間,參考了一些部落格,但是感覺不是特别詳細,其中也遇到了不少麻煩,下面一一講解到位,非常适合小白參考。

2.1 資料下載下傳及導入

首先下載下傳搜狗實驗室的文本資料(精簡版347MB,tar.gz格式):

下載下傳連結

NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)
NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

解壓後,得到如下128個txt檔案

NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

檔案格式如下:

NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

對于特定格式的文本,我們一般采用正規表達式來提取所需要的資訊,代碼如下:

import re
import os
import pandas as pd
import pickle
import numpy as np
import jieba;
# 定義正規表達式
patternURL = re.compile(r'<url>(.*?)</url>', re.S)
patternCtt = re.compile(r'<content>(.*?)</content>', re.S)
contents_total = []
urls_total=[]
labels = []
# os.listdir()傳回檔案夾裡所有檔案名
file = os.listdir("C:/Users/84747/Desktop/建立檔案夾/SogouCS.reduced")
for i in range(len(file)):  
    file0=file[i]
    file_path = os.path.join("C:/Users/84747/Desktop/建立檔案夾/SogouCS.reduced/", file0)
# os.path.join()将路徑進行拼接,進而打開每一個txt檔案
    text = open(file_path, 'rb').read().decode("gbk", 'ignore')
    # 正則比對出url和content
    urls = patternURL.findall(text)
    contents = patternCtt.findall(text)

# 得到所有contents和urls
    urls_total=urls_total + urls
    contents_total = contents_total + contents
df=pd.DataFrame({'URL':urls_total,'content':contents_total})
#将目前處理的資料用dataframe可視化一下,友善查錯
df.head()  # 顯示dataframe的前五行
           

結果如下(有空值、内容也很亂),後面一步步處理:

NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

下面我們再将URL内容再次正則一下,提取官方的分類label:

labels=[]
for i in range(0,len(urls_total)):
    patternClass = re.compile(r'http://(.*?).sohu.com', re.S)
    labels.append(patternClass.findall(urls_total[i]))
df=pd.DataFrame({'label':labels,'URL':urls_total,'content':contents_total}).dropna()
df.head() #如果想顯示最後五行可用.tail()
           

其中傳統dataframe中dropna() 函數删空值的方法在這裡并不适用,結果如下,待會會處理,我們先把label裡面的格式調整一下,調整的原因:目前的label格式為list of list,為了友善後面篩選label來替換中文等後續操作,先脫去一層list:

NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)
type(labels)
# print(labels[0:100])
labels2 = []
for index in range(len(labels)):
    labels2.append(' '.join(labels[index]))  #将list of list轉換為list
labels2[0:100]  
df.label.unique()
           
NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)
df=pd.DataFrame({'label':labels2,'URL':urls_total,'content':contents_total})
df.tail()
           
NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

好了,到這裡label格式已經調好了,接下來需要對label進行中文替換,是以我們需要先把各類label篩選出來,總共有以下label:

NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

将所需要的label對應的内容進行篩選檢視(替換‘career’為各個label,檢視相關内容),友善人為辨識類别

代碼如下:

NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

接下來就是替換label,通過人為的觀察上述各label所對應的分類,将中文替換到下列map映射之中,最後完成label替換:

label_mapping={'sports':'體育', 'house':'房屋','it':'科技', '2008':'奧運', 'women':'女人',\
               'auto':'汽車','yule':'娛樂', 'news':'時事','learning':'教育', 'business':'财經',\
               'mil.news':'軍事', 'travel':'旅遊', 'health':'健康', 'cul':'文化', 'career':'職場'}
df['label'] = df['label'].map(label_mapping) #将label進行替換
df.head()
           
NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

回到剛剛提到的空值問題,明明有很多空值,但isnull()查閱後仍然顯示false,原因在于:pandas裡空值是指NA,包括numpy的np.nan,python的None,pandas對空值進行操作可以用isnull/notnull/isna/notna/fillna/dropna等等,但是,這些操作對空字元串均無效(此處參考連結)。

空字元串即“ ”(一個或多個空格),但在excel表格裡其實是看不出來,pandas也把它當成有值進行操作。

代碼如下:

df.content.replace(to_replace=r'^\s*$',value=np.nan,regex=True,inplace=True)
df.head()
           
NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

這樣一來,就将空值轉換成了NaN,進而再可以使用dropna()。

df2=df.dropna(axis=0, how='any') # 對任意含有NaN的行(axis=0)進行删除
df2.head()
           
NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

再将索引重新排列一下:

df3=df2.reset_index(drop=True)
df3.head()
           
NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

2.2 結巴分詞及停用詞過濾

此處我沒有用前面的資料進行處理(畢竟有42w行資料,作為新手使用小資料集練手足夠,後面可能還會發42w行的運作結果,這裡采用了前輩整理好的5000行資料進行處理),格式和我之前處理得到的基本一緻,不影響大家參考。

樣例資料導入:

import gensim
import numpy
import pandas as pd
import jieba
#python -m pip install --user gensim  (gensim包)
#pip install jieba
df_news = pd.read_table('./val.txt',names=['category','theme','URL','content'],encoding='utf-8')
print(df_news.head())
print(df_news.shape)  #資料類型
           
NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)
NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

2.2.1 結巴分詞:

分詞之前首先我們要将dataframe的格式轉換為list才能适應jieba庫,代碼如下:

content = df_news.content.values.tolist()    #将datafrmae中content轉化為list
content_S = []            #對content中内容進行分詞
for line in content:
    current_segment = jieba.lcut(line)
    if len(current_segment) > 1 and current_segment != '\r\n': #換行符
        content_S.append(current_segment)
df_content=pd.DataFrame({'content_S':content_S}) #### 将分完詞的list轉換為dataframe
df_content.head()
           
NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

2.2.2 停用詞過濾:

需要先下載下傳好一份停用詞表,網上有很多,此處提供前輩整理好的素材,很友善

topwords=pd.read_csv("stopwords.txt",index_col=False,sep="\t",quoting=3,names=['stopword'], encoding='utf-8')
stopwords.head(15)
           
NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)
def drop_stopwords(contents,stopwords):
    contents_clean = []
    all_words = []
    for line in contents:
        line_clean = []
        for word in line:
            if word in stopwords:
                continue
            line_clean.append(word)
        contents_clean.append(line_clean)
    return contents_clean,all_words
    #print (contents_clean)
        

contents = df_content.content_S.values.tolist()    #df轉換為list
stopwords = stopwords.stopword.values.tolist()     #轉換為list
contents_clean,all_words = drop_stopwords(contents,stopwords)

df_content=pd.DataFrame({'contents_clean':contents_clean})  #将分完詞的list再轉換為df
df_content.head()
           
NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

三、模型(modeling)貝葉斯分類器

df_train=pd.DataFrame({'contents_clean':contents_clean,'label':df_news['category']})
df_train.tail() #tail()展示最後幾個資料(一共是5000個資料)
           
NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)
df_train.label.unique()
#對label做映射
label_mapping = {"汽車": 1, "财經": 2, "科技": 3, "健康": 4, "體育":5, "教育": 6,"文化": 7,"軍事": 8,"娛樂": 9,"時尚": 0}
df_train['label'] = df_train['label'].map(label_mapping) #将label進行替換
df_train.head()
           
NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

将資料切分為訓練集(x_train,y_train)和測試集(x_test,y_test)

from sklearn.model_selection import train_test_split
#将資料集切分為訓練和測試集,x代表content,y代表label
x_train, x_test, y_train, y_test = train_test_split(df_train['contents_clean'].values, df_train['label'].values, random_state=1)
print(len(x_train),len(x_test),len(y_train),len(y_test))
           
NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

3.1 文本資料向量化

資料向量化之前,我們先要将類型轉換為list以适合CountVectorizer(詞頻)/TfidfVectorizer(逆向文本頻率IDF)

#将x_train(numpy.array型轉換為list類型,
#以适合CountVectorizer/TfidfVectorizer向量化操作)
words = []
for line_index in range(len(x_train)):
	words.append(' '.join(x_train[line_index]))  #numpy.array轉換為list

test_words = []
for line_index in range(len(x_test)):
	test_words.append(' '.join(x_test[line_index]))
           

3.1.1 基于詞頻向量化

導入sklearn機器學習庫中的CountVectorizer詞頻向量化函數

from sklearn.feature_extraction.text import CountVectorizer

vec = CountVectorizer(analyzer='word', max_features=4000,  lowercase = False) #建立向量
vec.fit(words)
           

導入貝葉斯

from sklearn.naive_bayes import MultinomialNB  #導入貝葉斯
classifier = MultinomialNB()
classifier.fit(vec.transform(words), y_train)
classifier.score(vec.transform(test_words), y_test)  #基于詞頻向量構造的結果
           
NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

3.1.2 基于TFIDF向量化

from sklearn.feature_extraction.text import TfidfVectorizer  #基于TF-IDF向量

vectorizer = TfidfVectorizer(analyzer='word', max_features=4000,  lowercase = False)
vectorizer.fit(words)
# 導入貝葉斯
from sklearn.naive_bayes import MultinomialNB
classifier = MultinomialNB()
classifier.fit(vectorizer.transform(words), y_train)
# 計算分類器精度
classifier.score(vectorizer.transform(test_words), y_test)
           
NLP入門實戰之——基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞分類基于詞頻和TF-IDF,利用樸素貝葉斯機器學習方法新聞文本分類(洗資料、sklearn新手練習)

相比之下,TFIDF向量化的結果會偏高一點點,當然,這裡采用的是很小的資料集(才5000行),精度很低,如果将42w的資料進行訓練,精度應該會提升不少。到此為止,整個搜狗新聞文本分類任務就完成了。

本文到這裡就全部結束了,如果有錯誤或者引用不當,還請指出,我會加以改進!歡迎大家評論留言,互相學習和進步!(前輩整理的資料集後面會上傳到csdn上,如有需要可以聯系)

參考文章:

https://blog.csdn.net/weixin_43269174/article/details/88634129

https://blog.csdn.net/sadfassd/article/details/80568321

https://www.jianshu.com/p/edad714110fb

https://blog.csdn.net/maotianyi941005/article/details/84315965

https://www.cnblogs.com/datou-swag/articles/10060532.html

繼續閱讀