機器學習筆記:初識sklearn(一)
以下內容為優達學城機器學習入門的mini專案:這裡有一系列分別由Sara(label 0)與Chris(label 1)所寫的郵件,劃分資料集,使用sklearn中的整合模型進行訓練與預測。
預處理
依賴庫
import nltk
import numpy
import scipy
import time
import sys
import sklearn
from email_preprocess import preprocess
sys.path.append("../tools/")
資料處理
email_preprocess.py:
import pickle
import _pickle as cPickle
import numpy
from sklearn import model_selection
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_selection import SelectPercentile, f_classif
def preprocess(words_file = "../tools/word_data.pkl", authors_file="../tools/email_authors.pkl"):
"""
this function takes a pre-made list of email texts and
the corresponding authors and performs
a number of preprocessing steps:
-- splits into training/testing sets (10% testing)
-- vectorizes into tfidf matrix
-- selects/keeps most helpful features
after this, the feaures and labels are put into numpy arrays
which play nice with sklearn functions
4 objects are returned:
-- training/testing features
-- training/testing labels
"""
### the words (features) and authors (labels), already largely preprocessed
authors_file_handler = open(authors_file, "rb")
authors = pickle.load(authors_file_handler)
authors_file_handler.close()
words_file_handler = open(words_file, "rb")
word_data = cPickle.load(words_file_handler)
words_file_handler.close()
### 按照交叉驗證法則劃分資料集,test_size表示劃分到測試集的百分比
features_train, features_test, labels_train, labels_test = \
model_selection.train_test_split(word_data, authors, test_size=0.1, random_state=42)
### text vectorization--go from strings to lists of numbers
vectorizer = TfidfVectorizer(sublinear_tf=True, max_df=0.5, stop_words='english')
features_train_transformed = vectorizer.fit_transform(features_train)
features_test_transformed = vectorizer.transform(features_test)
### 特徵選擇。因為文字的特徵數量非常多,只選取一部分特徵
selector = SelectPercentile(f_classif, percentile=10) #選取的特徵百分比
selector.fit(features_train_transformed, labels_train)
features_train_transformed = selector.transform(features_train_transformed).toarray()
features_test_transformed = selector.transform(features_test_transformed).toarray()
print("no. of Chris training emails:", sum(labels_train))
print("no. of Sara training emails:", len(labels_train)-sum(labels_train))
return features_train_transformed, features_test_transformed, labels_train, labels_test
樸素貝葉斯
簡介
貝葉斯概率就不用介紹了。
sklearn官方文件給了樸素貝葉斯中高斯模型的示例程式碼:
import numpy as np
X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
Y = np.array([1, 1, 1, 2, 2, 2])
from sklearn.naive_bayes import GaussianNB
clf = GaussianNB()
clf.fit(X, Y)
print(clf.predict([[-0.8, -1]]))
依賴庫
from sklearn.naive_bayes import GaussianNB
使用
features_train, features_test, labels_train, labels_test = preprocess()
clf=GaussianNB()
tic=time.time()
clf.fit(features_train,labels_train)
toc=time.time()
print("training time:{}s.".format(round(toc-tic,3)))
accuracy=clf.score(features_test,labels_test) #.score()方法用於評測模型準確度
print("accuracy:{}".format(accuracy))
支援向量機
發現SVM這塊優達學城講的挺通俗的,低維到高位的對映就是通過增加組合特徵值實現的,之前在《機器學習》這本書上沒看懂,現在懂了。
簡介
sklearn官方文件給出了SVM分類器的簡單程式碼示例:
from sklearn import svm
X = [[0, 0], [1, 1]]
y = [0, 1]
clf = svm.SVC()
clf.fit(X, y)
其中SVC物件有幾個重要的引數:C=1.0,gamma='auto', kernel='rbf'
。
kernel
指定所用的核函式,sklearn中內建的核函式有’linear’、’poly’、’rbf’、’sigmoid’、’precomputed’ 或者可呼叫的自定義核函式。引數C與gamma對’rbf’核的SVM影響較大。C
引數決定在繪製超平面時把多少樣本考慮進去,越高的C值會使決策邊界更復雜甚至過擬合,而越低的C值會使得決策邊界更平滑。對線性核函式無影響。gamma
決定了單個樣本在繪製決策邊界時的影響範圍有多大,高gamma值影響範圍小,低gamma值影響範圍大。gamma引數的值可看作是樣本影響半徑的倒數。
依賴庫
from sklearn import svm
使用
features_train, features_test, labels_train, labels_test = preprocess()
clf=svm.SVC(kernel='linear')
#丟棄部分訓練資料以加快擬合
features_train=features_train[:int(len(features_train)/100)]
labels_train=labels_train[:int(len(labels_train)/100)]
tic=time.time()
clf.fit(features_train,labels_train)
toc=time.time()
print("train time:{}".format(round(toc-tic,3)))
acc=clf.score(features_test,labels_test)
print(acc)
注意在擬合時只使用到了1%的訓練資料,因為在處理文字時SVM的擬合速度要比樸素貝葉斯慢得多。實際執行中,線性SVM對完整訓練集的擬合時間為2min左右,準確率為98%;而線性SVM對1%訓練集的擬合時間在0.1s以內,準確率為88%。
rbf核函式引數C的優化
接下來將核函式換成rbf,來討論引數C的優化問題。在程式中設定四個不同的C值1.0,、10.0、1000.0、10000.0,分別檢視不同引數值模型的準確度,為了更快地看到優化效果,只使用1%的訓練集:
features_train, features_test, labels_train, labels_test = preprocess()
clf1=svm.SVC(kernel='rbf',C=1.)
clf2=svm.SVC(kernel='rbf',C=10.)
clf3=svm.SVC(kernel='rbf',C=1000.)
clf4=svm.SVC(kernel='rbf',C=10000.)
#丟棄部分訓練資料以加快擬合
features_train=features_train[:int(len(features_train)/100)]
labels_train=labels_train[:int(len(labels_train)/100)]
clf1.fit(features_train,labels_train)
clf2.fit(features_train,labels_train)
clf3.fit(features_train,labels_train)
clf4.fit(features_train,labels_train)
acc1=clf1.score(features_test,labels_test)
acc2=clf2.score(features_test,labels_test)
acc3=clf3.score(features_test,labels_test)
acc4=clf4.score(features_test,labels_test)
print(acc1,acc2,acc3,acc4)
採用四號分類器(C=10000),使用整個訓練集對clf4進行訓練,最後準確度達到了:
決策樹
簡介
決策樹是一種以線性劃分做出非線性決策的演算法,它以不同層次的限定條件來對資料進行線性劃分,然後根據不同區域做出非線性決策。
熵
決策樹中一個很重要的屬性就是節點的熵,其計算公式為:
資訊增益
很明顯決策樹需要依照不同特徵來對資料進行劃分,或者說對每一個節點進行進一步的劃分,很明顯最佳的決策就是在劃分之後子節點的資訊純度儘量高,資訊增益就是表示這一概念的量:
後一項為父節點劃分後所有子節點的加權平均資訊熵,權重
假如有如下樣本,三項二元特徵grads、bumpiness與speed limit,二元類別為speed,樣本數為4:
grads | bumpiness | speed limit | speed |
---|---|---|---|
steep | bumpy | yes | slow |
steep | smooth | yes | slow |
flat | bumpy | no | fast |
steep | smooth | no | fast |
假設決策樹先按照grads特徵對資料進行劃分,那麼有:
則根據特徵grads劃分之後的資訊增益計算過程如下。
父節點的熵為:
parent_ent=-((2/4)*math.log(2/4,2)+(2/4)*math.log(2/4,2))
顯而易見右子節點的熵為0:
r_child_ent=0
計算左子結點的熵:
l_child_ent=-((2/3)*math.log(2/3,2)+(1/3)*math.log(1/3,2))
子節點的權重:
l_child_w=3/4
r_child_w=1/4
劃分後的資訊增益為:
Gain=parent_ent-l_child_w*l_child_ent-r_child_w*r_child_ent
這樣,分別計算按照不同特徵劃分樣本後的資訊增益,選擇能得到最大資訊增益的特徵來進行劃分。此例中最佳劃分特徵為speed limit,因為劃分之後兩子節點的熵都是0,資訊增益為1,如下圖所示:
示例
sklearn官方文件給出了決策樹分類器的使用程式碼示例:
from sklearn import tree
X = [[0, 0], [1, 1]]
Y = [0, 1]
clf = tree.DecisionTreeClassifier()
clf = clf.fit(X, Y)
決策樹分類器有幾個重要的引數:
criterion=’gini’
:劃分節點的依據,可接受值'entropy'
來按照資訊增益劃分節點。min_samples_split=2
:節點的最小樣本數,當節點包含的樣本數大於這個值時就會繼續劃分下去。此值太小會導致過擬合。
依賴庫
from sklearn import tree
使用
features_train, features_test, labels_train, labels_test = preprocess()
clf=tree.DecisionTreeClassifier(criterion='entropy',min_samples_split=40)
tic=time.time()
clf.fit(features_train,labels_train)
toc=time.time()
print("training time:{}".format(round(toc-tic,3)))
acc=clf.score(features_test,labels_test)
print(acc)
不難得出,決策樹模型的擬合時長是由資料的特徵數量決定的,上述程式碼的擬合時長明顯是無法接受的,所以有必要對特徵數量進行削減。在email_preprocess.py檔案中修改以下行,只保留前1%的特徵:
selector = SelectPercentile(f_classif, percentile=1)
再次執行模型: