在本系列文章中提到過用Python開始機器學習(3:資料擬合與廣義線性迴歸)中提到過迴歸演算法來進行數值預測。羅吉斯迴歸演算法本質還是迴歸,只是其引入了邏輯函數來協助其分類。實踐發現,羅吉斯迴歸在文本分類領域表現的也很優秀。現在讓我們來一探究竟。 1、邏輯函數
假設資料集有n個獨立的特徵,x1到xn為樣本的n個特徵。常規的迴歸演算法的目標是擬合出一個多項式函數,使得預測值與真實值的誤差最小:
而我們希望這樣的f(x)能夠具有很好的邏輯判斷性質,最好是能夠直接表達具有特徵x的樣本被分到某類的機率。比如f(x)>0.5的時候能夠表示x被分為正類,f(x)<0.5表示分為反類。而且我們希望f(x)總在[0, 1]之間。有這樣的函數嗎。
sigmoid函數就出現了。這個函數的定義如下:
先直觀的瞭解一下,sigmoid函數的映像如下所示(來自http://computing.dcu.ie/~humphrys/Notes/Neural/sigmoid.html):
sigmoid函數具有我們需要的一切優美特性,其定義域在全體實數,範圍在[0, 1]之間,並且在0點值為0.5。
那麼,如何將f(x)轉變為sigmoid函數呢。令p(x)=1為具有特徵x的樣本被分到類別1的機率,則p(x)/[1-p(x)]被定義為讓步比(odds ratio)。引入對數:
上式很容易就能把p(x)解出來得到下式:
現在,我們得到了需要的sigmoid函數。接下來只需要和往常的線性迴歸一樣,擬合出該式中n個參數c即可。 2、測試資料
測試資料我們仍然選擇康奈爾大學網站的2M影評資料集。
在這個資料集上我們已經測試過KNN分類演算法、樸素貝葉斯分類演算法。現在我們看看羅輯迴歸分類演算法在處理此類情感分類問題效果如何。
同樣的,我們直接讀入儲存好的movie_data.npy和movie_target.npy以節省時間。 3、代碼與分析
羅吉斯迴歸的代碼如下:
# -*- coding: utf-8 -*-from matplotlib import pyplotimport scipy as spimport numpy as npfrom matplotlib import pylabfrom sklearn.datasets import load_filesfrom sklearn.cross_validation import train_test_splitfrom sklearn.feature_extraction.text import CountVectorizerfrom sklearn.feature_extraction.text import TfidfVectorizerfrom sklearn.naive_bayes import MultinomialNBfrom sklearn.metrics import precision_recall_curve, roc_curve, aucfrom sklearn.metrics import classification_reportfrom sklearn.linear_model import LogisticRegressionimport timestart_time = time.time()#繪製R/P曲線def plot_pr(auc_score, precision, recall, label=None): pylab.figure(num=None, figsize=(6, 5)) pylab.xlim([0.0, 1.0]) pylab.ylim([0.0, 1.0]) pylab.xlabel('Recall') pylab.ylabel('Precision') pylab.title('P/R (AUC=%0.2f) / %s' % (auc_score, label)) pylab.fill_between(recall, precision, alpha=0.5) pylab.grid(True, linestyle='-', color='0.75') pylab.plot(recall, precision, lw=1) pylab.show()#讀取movie_data = sp.load('movie_data.npy')movie_target = sp.load('movie_target.npy')x = movie_datay = movie_target#BOOL型特徵下的向量空間模型,注意,測試樣本調用的是transform介面count_vec = TfidfVectorizer(binary = False, decode_error = 'ignore',\ stop_words = 'english')average = 0testNum = 10for i in range(0, testNum): #載入資料集,切分資料集80%訓練,20%測試 x_train, x_test, y_train, y_test\ = train_test_split(movie_data, movie_target, test_size = 0.2) x_train = count_vec.fit_transform(x_train) x_test = count_vec.transform(x_test) #訓練LR分類器 clf = LogisticRegression() clf.fit(x_train, y_train) y_pred = clf.predict(x_test) p = np.mean(y_pred == y_test) print(p) average += p #準確率與召回率answer = clf.predict_proba(x_test)[:,1]precision, recall, thresholds = precision_recall_curve(y_test, answer) report = answer > 0.5print(classification_report(y_test, report, target_names = ['neg', 'pos']))print("average precision:", average/testNum)print("time spent:", time.time() - start_time)plot_pr(0.5, precision, recall, "pos")代碼運行結果如下:
0.8
0.817857142857
0.775
0.825
0.807142857143
0.789285714286
0.839285714286
0.846428571429
0.764285714286
0.771428571429
precision recall f1-score support
neg 0.74 0.80 0.77 132
pos 0.81 0.74 0.77 148
avg / total 0.77 0.77 0.77 280
average precision: 0.803571428571
time spent: 9.651551961898804
首先注意我們連續測試了10組測試樣本,最後統計出準確率的平均值。另外一種好的測試方法是K折交叉檢驗(K-Fold)。這樣都能更加準確的評估分類器的效能,考察分類器對噪音的敏感性。
其次我們注意看最後的圖,這張圖就是使用precision_recall_curve繪製出來的P/R曲線(precition/Recall)。結合P/R圖,我們能對羅吉斯迴歸有更進一步的理解。
前文我們說過,通常我們使用0.5來做劃分兩類的依據。而結合P/R分析,閾值的選取是可以更加靈活和優秀的。
在上圖可以看到,如果選擇的閾值過低,那麼更多的測試樣本都將分為1類。因此召回率能夠得到提升,顯然準確率犧牲相應準確率。
比如本例中,或許我會選擇0.42作為劃分值——因為該點的準確率和召回率都很高。
最後給一些比較好的資源:
浙大某女學霸的部落格。記錄的斯坦福Andrew老師主講的LR公開課筆記:http://blog.csdn.net/abcjennifer/article/details/7716281
一個總結LR還不錯的部落格:http://xiamaogeng.blog.163.com/blog/static/1670023742013231197530/
Sigmoid函數詳解:http://computing.dcu.ie/~humphrys/Notes/Neural/sigmoid.html