天天看點

nltk 自己訓練模型例子

首先來看一下nltk官方文檔中給出的一個簡單明了的例子,在了解這個例子之後,再設法将同樣的模型應用到自己的資料集上。官方給出的例子是英文名中,在知道名字中最後一個字母後,判斷這個名字對應的人是男是女。

#coding=utf-8  

import random, nltk  

from nltk.corpus import names  

def gender_features(word):  

    '''''提取每個單詞的最後一個字母作為特征'''  

    return {'last_letter': word[-1]}  

# 先為原始資料打好标簽  

labeled_names = ([(name, 'male') for name in names.words('male.txt')] + [(name, 'female') for name in names.words('female.txt')])  

# 随機打亂打好标簽的資料集的順序,  

random.shuffle(labeled_names)  

# 從原始資料中提取特征(名字的最後一個字母, 參見gender_features的實作)  

featuresets = [(gender_features(name), gender) for (name, gender) in labeled_names]  

# 将特征集劃分成訓練集和測試集  

train_set, test_set = featuresets[500:], featuresets[:500]  

# 使用訓練集訓練模型(核心就是求出各種後驗機率)  

classifier = nltk.naivebayesclassifier.train(train_set)  

# 通過測試集來估計分類器的準确性  

print(nltk.classify.accuracy(classifier, test_set))  

# 如果一個人的名字的最後一個字母是‘a’,那麼這個人是男還是女  

print(classifier.classify({'last_letter': 'a'}))  

# 找出最能夠區分分類的特征值  

classifier.show_most_informative_features(5)  

以上程式的輸出如下:

0.754  

female  

most informative features  

             last_letter = u'a'           female : male   =     35.6 : 1.0  

             last_letter = u'k'             male : female =     30.7 : 1.0  

             last_letter = u'f'             male : female =     16.6 : 1.0  

             last_letter = u'p'             male : female =     12.5 : 1.0  

             last_letter = u'm'             male : female =     11.1 : 1.0  

從結果中,我們可以看到,通過訓練集訓練出的模型,在應用到測試集上時,其準确率為75%;如果一個人的名字以字母‘a’結束,那麼此分類器将其劃分為女性;最後輸出了最能區分男女的5個屬性值的資料,比如,對于字母‘a’來說,它作為女性名的最後一個字母的可能性是男性的35倍。

可以看到nltk的樸素貝葉斯實作之中,它的輸入的訓練集的輸入是類似于以下的形式:

[

({'attr1':val1, 'attr2': val2, 'attr3': val3 ... 'attrn': valn}, label1),

({'attr1':val1, 'attr2': val2, 'attr3': val3 ... 'attrn': valn}, label2),

......

]

其中,每個特征對應一個标簽,在以上的官方的例子中,特征就隻有一個,last_letter;而特征的可能值是26個字母。對應到自己的資料,對應一個使用者就不止有一個特征了,而是使用者安裝的app名稱清單,同時又由于每個使用者安裝的app可能不同,是以不同的使用者所對應的特征的長度也是可能不同的;而每個屬性(app名稱)對應的值隻有兩個:安裝或者沒安裝。

#!/usr/local/bin/python2.7  

# encoding: utf-8  

from collections import defaultdict  

import nltk  

def gender_features(appnamelist):  

    features = defaultdict(bool)  

    for appname in appnamelist:  

        features[appname] = true  

    return features  

if __name__ == '__main__':  

    raw_data = defaultdict(lambda: defaultdict(list))  

    with open('data/genderapplist.log') as f:  

        for line in f:  

            cells = line.strip().split('\t')  

            if len(cells) == 3:  

                imei, gender, appname = cells  

                gender = 'male' if gender == '男性應用' else 'female'  

                raw_data[gender][imei].append(appname)  

    labeled_applist = [(appnamelist, 'male') for appnamelist in raw_data['male'].values()] + [(appnamelist, 'female') for appnamelist in raw_data['female'].values()]  

    featuresets = [(gender_features(appnamelist), gender) for appnamelist, gender in labeled_applist]  

    train_set, test_set = featuresets[500:], featuresets[:500]  

    classifier = nltk.naivebayesclassifier.train(train_set)  

    # 在訓練生成的分類器classifier中,有兩個屬性存儲着貝葉斯分類器所需要的先驗和後驗機率:  

    # _label_probdist 儲存了标簽的分布  

    # _feature_probdist 儲存了每個appname對應的後驗分布  

    # 通過下面的代碼我們可以看到它們的值  

    print '以下是 _label_probdist的相關資訊'  

    print '1. 類型'   

    print type(classifier._label_probdist)  

    print '2. 标簽的整體分布狀況'  

    classifier._label_probdist.freqdist().tabulate()  

    print '3. 由第二步推出的标簽的機率分布'  

    print classifier._label_probdist.prob('female'), classifier._label_probdist.prob('male')  

    print '*' * 32  

    # _feature_probdist的值  

    print '以下是 _feature_probdist的相關資訊'  

    print '1. 類型'  

    print type(classifier._feature_probdist)  

    print '2. 從1的輸出中可以看到其類型為dict,我們看它的一個key和value即可'  

    print classifier._feature_probdist.items()[6302]  

    print '3. 從2中可以看到,其代表了,在标簽為female的情況下,安裝了支付寶錢包這個應用的機率分布'  

    classifier._feature_probdist.items()[6302][1].freqdist().tabulate()  

    print '4. 3的輸出,我們非常熟悉,也就是在所有4910個female使用者中,有77個安裝了支付寶錢包,沒有安裝的有4833個'  

    print '有了這個分布,我們就可以計算出p(true|female, 支付寶錢包),其意義就是,在female使用者中,支付寶錢包這個屬性為true的可能性為'  

    print classifier._feature_probdist.items()[6302][1].prob(true)  

    print '5. 然後你會發現4中輸出的p(true|female, 支付寶錢包)并不正好等于77./4910,這是因為使用eleprobdist'  

    print '也就是“期望相似性機率估計”,這種方法避免了p(true|female, 支付寶錢包)=0情況的出現,進而避免模型失效'  

    print '6. 通過在訓練集上的訓練,我們得到了以上的機率分布,然後就可以使用訓練好的模型來分類了,我們看一下安裝了蘑菇街和支付寶錢包的使用者是男還是女'  

    print classifier.classify({'蘑菇街':true, '支付寶錢包': true})  

    print '7. 讓我們看一下安傳過了蘑菇街和支付寶錢包的使用者男女的可能性'  

    print 'prob(female)', classifier.prob_classify({'蘑菇街':true, '支付寶錢包': true}).prob('female')  

    print 'prob(male)', classifier.prob_classify({'蘑菇街':true, '支付寶錢包': true}).prob('male')  

    print '8. 如果我們的輸入中,有一個全新的應用“這個應用不存在”,這裡的處理是不處理它'  

    print 'prob(female)', classifier.prob_classify({'蘑菇街':true, '支付寶錢包': true, '這個應用不存在':true}).prob('female')  

    print 'prob(male)', classifier.prob_classify({'蘑菇街':true, '支付寶錢包': true, '這個應用不存在':true}).prob('male')  

以上程式的輸出為:

以下是 _label_probdist的相關資訊  

1. 類型  

<class 'nltk.probability.eleprobdist'>  

2. 标簽的整體分布狀況  

female male   

4910 4420   

3. 由第二步推出的标簽的機率分布  

0.526256564141 0.473743435859  

********************************  

以下是 _feature_probdist的相關資訊  

<type 'dict'>  

2. 從1的輸出中可以看到其類型為dict,我們看它的一個key和value即可  

(('female', '\xe6\x94\xaf\xe4\xbb\x98\xe5\xae\x9d\xe9\x92\xb1\xe5\x8c\x85'), <eleprobdist based on 4910 samples>)  

3. 從2中可以看到,其代表了,在标簽為female的情況下,安裝了支付寶錢包這個應用的機率分布  

none true   

4833   77   

4. 3的輸出,我們非常熟悉,也就是在所有4910個female使用者中,有77個安裝了支付寶錢包,沒有安裝的有4833個  

有了這個分布,我們就可以計算出p(true|female, 支付寶錢包),其意義就是,在female使用者中,支付寶錢包這個屬性為true的可能性為  

0.0157809000204  

5. 然後你會發現4中輸出的p(true|female, 支付寶錢包)并不正好等于77./4910,這是因為使用eleprobdist  

也就是“期望相似性機率估計”,這種方法避免了p(true|female, 支付寶錢包)=0情況的出現,進而避免模型失效  

6. 通過在訓練集上的訓練,我們得到了以上的機率分布,然後就可以使用訓練好的模型來分類了,我們看一下安裝了蘑菇街和支付寶錢包的使用者是男還是女  

7. 讓我們看一下安傳過了蘑菇街和支付寶錢包的使用者男女的可能性  

prob(female) 0.994878529146  

prob(male) 0.00512147085357  

8. 如果我們的輸入中,有一個全新的應用“這個應用不存在”,這裡的處理是不處理它  

這樣通過使用nltk,相比自己實作來說有了更簡潔的代碼,并且更容易維護,希望對有需要的同學有幫助。