1. 程式人生 > >文字挖掘之特徵選擇(python實現)

文字挖掘之特徵選擇(python實現)

來自:http://www.cnblogs.com/wangbogong/p/3251132.html

機器學習演算法的空間、時間複雜度依賴於輸入資料的規模,維度規約(Dimensionality reduction)則是一種被用於降低輸入資料維數的方法。維度規約可以分為兩類:

  • 特徵選擇(feature selection),從原始的d維空間中,選擇為我們提供資訊最多的k個維(這k個維屬於原始空間的子集)
  • 特徵提取(feature extraction),將原始的d維空間對映到k維空間中(新的k維空間不輸入原始空間的子集)

  在文字挖掘與文字分類的有關問題中,常採用特徵選擇方法。原因是文字的特徵一般都是單詞(term),具有語義資訊,使用特徵選擇找出的k維子集,仍然是單詞作為特徵,保留了語義資訊,而特徵提取則找k維新空間,將會喪失了語義資訊。

  對於一個語料而言,我們可以統計的資訊包括文件頻率和文件類比例,所有的特徵選擇方法均依賴於這兩個統計量,目前,文字的特徵選擇方法主要有:DF, MI, IG, CHI,WLLR,WFO六種。

  為了方便描述,我們首先一些概率上的定義:

    p(t):一篇文件x包含特徵詞t的概率。

    :文件x不屬於Ci的概率。

    p(Ci|t):已知文件x的包括某個特徵詞t條件下,該文件屬於Ci的概率

    : 已知文件屬於C條件下,該文件不包括特徵詞t的概率

  類似的其他的一些概率如p(Ci), 等,有著類似的定義。

為了估計這些概率,我們需要通過統計訓練樣本的相關頻率資訊,如下表:

 其中:

   Aij包含特徵詞ti,並且類別屬於Cj的文件數量    Bij: 包含特徵詞ti,並且類別屬於不Cj的文件數量

   Cij:不包含特徵詞ti,並且類別屬於Cj的文件數量 Dij:不包含特徵詞ti,並且類別屬於不Cj的文件數量

   Aij + Bij: 包含特徵詞ti的文件數量          Cij  + Dij:不包含特徵詞ti的文件數量

   Aij + Cij:Cj類的文件數量資料             Bij + Dij:非Cj類的文件數量資料

   Aij + Bij + Cij  + Dij = N :語料中所有文件數量。

有了這些統計量,有關概率的估算就變得容易,如:

    p(ti) =     (Aij + Bij) / N;    p(Cj) = (Aij +  Cij) / N;  

    p(Cj|tj) = Aij  / (Aij + Bij)        

  ......類似的一些概率計算可以依照上表計算。

  介紹了事情發展的前因,現在進入正題:常見的四種特徵選擇方法如何計算。

  1)DF(Document Frequency)

DF:統計特徵詞出現的文件數量,用來衡量某個特徵詞的重要性,DF的定義如下:

  DF的動機是,如果某些特徵詞在文件中經常出現,那麼這個詞就可能很重要。而對於在文件中出現很少(如僅在語料中出現1次)特徵詞,攜帶了很少的資訊量,甚至是"噪聲",這些特徵詞,對分類器學習影響也是很小。

  DF特徵選擇方法屬於無監督的學習演算法(也有將其改成有監督的演算法,但是大部分情況都作為無監督演算法使用),僅考慮了頻率因素而沒有考慮類別因素,因此,DF演算法的將會引入一些沒有意義的詞。如中文的"的"、"是", "個"等,常常具有很高的DF得分,但是,對分類並沒有多大的意義。

  2)MI(Mutual Information)

  互資訊法用於衡量特徵詞與文件類別直接的資訊量,互資訊法的定義如下:

  繼續推導MI的定義公式:

  從上面的公式上看出:如果某個特徵詞的頻率很低,那麼互資訊得分就會很大,因此互資訊法傾向"低頻"的特徵詞。相對的詞頻很高的詞,得分就會變低,如果這詞攜帶了很高的資訊量,互資訊法就會變得低效。

  3)IG(Information Gain)

  資訊增益法,通過某個特徵詞的缺失與存在的兩種情況下,語料中前後資訊的增加,衡量某個特徵詞的重要性。

資訊增益的定義如下:

  依據IG的定義,每個特徵詞tiIG得分前面一部分:計算值是一樣,可以省略。因此,IG的計算公式如下:

IG與MI存在關係:

因此,IG方式實際上就是互資訊與互資訊加權。

4)CHI(Chi-square)

CHI特徵選擇演算法利用了統計學中的"假設檢驗"的基本思想:首先假設特徵詞與類別直接是不相關的,如果利用CHI分佈計算出的檢驗值偏離閾值越大,那麼更有信心否定原假設,接受原假設的備則假設:特徵詞與類別有著很高的關聯度。CHI的定義如下:

對於一個給定的語料而言,文件的總數N以及Cj類文件的數量,非Cj類文件的數量,他們都是一個定值,因此CHI的計算公式可以簡化為:

CHI特徵選擇方法,綜合考慮文件頻率與類別比例兩個因素

5)WLLR(Weighted Log Likelihood Ration)

WLLR特徵選擇方法的定義如下:

  計算公式如下:

6)WFO(Weighted Frequency and Odds)

最後一個介紹的演算法,是由蘇大李壽山老師提出的演算法。通過以上的五種演算法的分析,李壽山老師認為,"好"的特徵應該有以下特點:

  • 好的特徵應該有較高的文件頻率
  • 好的特徵應該有較高的文件類別比例

WFO的演算法定義如下:

如果

否則:

不同的語料,一般來說文件詞頻與文件的類別比例起的作用應該是不一樣的,WFO方法可以通過調整引數,找出一個較好的特徵選擇依據。

-----------------------------------------分割線---------------------------------------------

  介紹完理論部分,就要給出程式碼了(只給出公式,不給出程式碼的都是調戲良家的行為~)。文字挖掘之文字表示一文,利用了sklearn開源工具,自然先首先sklearn工具,可惜的是sklearn文字的特徵選擇方法僅提供了CHI一種。為此在sklearn框架下,嘗試自己編寫這些特徵選擇方法的程式碼,自己動手,豐衣足食。

 筆者實現了三種特徵選擇方法:IG,MI和WLLR,看官如果對其他特徵選擇方法感興趣,可以嘗試實現一下~ 好了,啥也不說了,上程式碼,特徵選擇模組程式碼:

複製程式碼
#!/usr/bin/env python
# coding=gbk

import os
import sys

import numpy as np

def get_term_dict(doc_terms_list):
    term_set_dict = {}
    for doc_terms in doc_terms_list:
        for term in doc_terms:
            term_set_dict[term] = 1
    term_set_list = sorted(term_set_dict.keys())       #term set 排序後,按照索引做出字典
    term_set_dict = dict(zip(term_set_list, range(len(term_set_list))))
    return term_set_dict

def get_class_dict(doc_class_list):
    class_set = sorted(list(set(doc_class_list)))
    class_dict = dict(zip(class_set, range(len(class_set))))
    return  class_dict

def stats_term_df(doc_terms_list, term_dict):
    term_df_dict = {}.fromkeys(term_dict.keys(), 0)
    for term in term_set:
        for doc_terms in doc_terms_list:
            if term in doc_terms_list:
                term_df_dict[term] +=1                
    return term_df_dict

def stats_class_df(doc_class_list, class_dict):
    class_df_list = [0] * len(class_dict)
    for doc_class in doc_class_list:
        class_df_list[class_dict[doc_class]] += 1
    return class_df_list

def stats_term_class_df(doc_terms_list, doc_class_list, term_dict, class_dict):
    term_class_df_mat = np.zeros((len(term_dict), len(class_dict)), np.float32)
    for k in range(len(doc_class_list)):
        class_index = class_dict[doc_class_list[k]]
        doc_terms = doc_terms_list[k]
        for term in set(doc_terms):
            term_index = term_dict[term]
            term_class_df_mat[term_index][class_index] +=1
    return  term_class_df_mat
        
def feature_selection_mi(class_df_list, term_set, term_class_df_mat):
    A = term_class_df_mat
    B = np.array([(sum(x) - x).tolist() for x in A])
    C = np.tile(class_df_list, (A.shape[0], 1)) - A
    N = sum(class_df_list)
    class_set_size = len(class_df_list)
    
    term_score_mat = np.log(((A+1.0)*N) / ((A+C) * (A+B+class_set_size)))
    term_score_max_list = [max(x) for x in term_score_mat]
    term_score_array = np.array(term_score_max_list)
    sorted_term_score_index = term_score_array.argsort()[: : -1]
    term_set_fs = [term_set[index] for index in sorted_term_score_index]
    
    return term_set_fs

def feature_selection_ig(class_df_list, term_set, term_class_df_mat):
    A = term_class_df_mat
    B = np.array([(sum(x) - x).tolist() for x in A])
    C = np.tile(class_df_list, (A.shape[0], 1)) - A
    N = sum(class_df_list)
    D = N - A - B - C
    term_df_array = np.sum(A, axis = 1)
    class_set_size = len(class_df_list)
    
    p_t = term_df_array / N
    p_not_t = 1 - p_t
    p_c_t_mat =  (A + 1) / (A + B + class_set_size)
    p_c_not_t_mat = (C+1) / (C + D + class_set_size)
    p_c_t = np.sum(p_c_t_mat  *  np.log(p_c_t_mat), axis =1)
    p_c_not_t = np.sum(p_c_not_t_mat *  np.log(p_c_not_t_mat), axis =1)
    
    term_score_array = p_t * p_c_t + p_not_t * p_c_not_t
    sorted_term_score_index = term_score_array.argsort()[: : -1]
    term_set_fs = [term_set[index] for index in sorted_term_score_index]    
    
    return term_set_fs

def feature_selection_wllr(class_df_list, term_set, term_class_df_mat):
    A = term_class_df_mat
    B = np.array([(sum(x) - x).tolist() for x in A])
    C_Total = np.tile(class_df_list, (A.shape[0], 1))
    N = sum(class_df_list)
    C_Total_Not = N - C_Total
    term_set_size = len(term_set)
    
    p_t_c = (A + 1E-6) / (C_Total + 1E-6 * term_set_size)
    p_t_not_c = (B +  1E-6) / (C_Total_Not + 1E-6 * term_set_size)
    term_score_mat = p_t_c  * np.log(p_t_c / p_t_not_c)
    
    term_score_max_list = [max(x) for x in term_score_mat]
    term_score_array = np.array(term_score_max_list)
    sorted_term_score_index = term_score_array.argsort()[: : -1]
    term_set_fs = [term_set[index] for index in sorted_term_score_index]
    
    print term_set_fs[:10]
    return term_set_fs

def feature_selection(doc_terms_list, doc_class_list, fs_method):
    class_dict = get_class_dict(doc_class_list)
    term_dict = get_term_dict(doc_terms_list)
    class_df_list = stats_class_df(doc_class_list, class_dict)
    term_class_df_mat = stats_term_class_df(doc_terms_list, doc_class_list, term_dict, class_dict)
    term_set = [term[0] for term in sorted(term_dict.items(), key = lambda x : x[1])]
    term_set_fs = []
    
    if fs_method == 'MI':
        term_set_fs = feature_selection_mi(class_df_list, term_set, term_class_df_mat)
    elif fs_method == 'IG':
        term_set_fs = feature_selection_ig(class_df_list, term_set, term_class_df_mat)
    elif fs_method == 'WLLR':
        term_set_fs = feature_selection_wllr(class_df_list, term_set, term_class_df_mat)
        
    return term_set_fs
    
複製程式碼

    在movie語料裡面比較著三種特徵選擇方法,呼叫方法如下:

複製程式碼
#!/usr/bin/env python
# coding=gbk

import os
import sys

import numpy as np
import matplotlib.pyplot as plt

from sklearn.datasets import load_files
from sklearn.cross_validation import train_test_split
from sklearn.feature_extraction.text import  CountVectorizer
from sklearn.naive_bayes import MultinomialNB

import feature_selection
    
def text_classifly_twang(dataset_dir_name, fs_method, fs_num):
    print 'Loading dataset, 80% for training, 20% for testing...'
    movie_reviews = load_files(dataset_dir_name)  
    doc_str_list_train, doc_str_list_test, doc_class_list_train, doc_class_list_test = train_test_split(movie_reviews.data, movie_reviews.target, test_size = 0.2, random_state = 0)
    
    print 'Feature selection...'
    print 'fs method:' + fs_method, 'fs num:' + str(fs_num)
    vectorizer = CountVectorizer(binary = True)   
    word_tokenizer = vectorizer.build_tokenizer()
    doc_terms_list_train = [word_tokenizer(doc_str) for doc_str in doc_str_list_train]
    term_set_fs = feature_selection.feature_selection(doc_terms_list_train, doc_class_list_train, fs_method)[:fs_num]
    
    print 'Building VSM model...'
    term_dict = dict(zip(term_set_fs, range(len(term_set_fs))))
    vectorizer.fixed_vocabulary = True
    vectorizer.vocabulary_ = term_dict
    doc_train_vec = vectorizer.fit_transform(doc_str_list_train)
    doc_test_vec= vectorizer.transform(doc_str_list_test)
    
    clf = MultinomialNB().fit(doc_train_vec, doc_class_list_train)  #呼叫MultinomialNB分類器
    doc_test_predicted = clf.predict(doc_test_vec)
    
    acc = np.mean(doc_test_predicted == doc_class_list_test)  
    print 'Accuracy: ', acc
    
    return acc
       

if __name__ == '__main__':
    dataset_dir_name = sys.argv[1]
    fs_method_list = ['IG', 'MI', 'WLLR']
    fs_num_list = range(25000, 35000, 1000)
    acc_dict = {}
   
    for fs_method in fs_method_list:
        acc_list = []
        for fs_num in fs_num_list:
            acc = text_classifly_twang(dataset_dir_name, fs_method, fs_num)
            acc_list.append(acc)
        acc_dict[fs_method] = acc_list
        print 'fs method:', acc_dict[fs_method]
        
    for fs_method in fs_method_list:
        plt.plot(fs_num_list, acc_dict[fs_method],  '--^',  label = fs_method)
        plt.title('feature  selection')
        plt.xlabel('fs num')
        plt.ylabel('accuracy')
        plt.ylim((0.82, 0.86))
        
    plt.legend( loc='upper left', numpoints = 1)
    plt.show()
    
複製程式碼

  輸出的結果:

  從上面的圖看出:分類的效能隨著特徵選擇的數量的增加,呈現“凸”形趨勢:1)在特徵數量較少的情況下,不斷增加特徵的數量,有利於提高分類器的效能,呈現“上升”趨勢;2)隨著特徵數量的不斷增加,將會引入一些不重要的特徵,甚至是噪聲,因此,分類器的效能將會呈現“下降”的趨勢。這張“凸”形趨勢體現出了特徵選擇的重要性:選擇出重要的特徵,並降低噪聲,提高演算法的泛化能力。

引數文獻:

    1.Y. Yang and J. Pedersen. 1997. A comparative study on feature selection in text categorization.

    2.Shoushan Li, Rui Xia, Chengqing Zong and Chu-Ren Huang.2009.A Framework of Feature Selection Methods for Text Categorization

    3.老闆的課件