概要
本文是用Python程式語言來進行機器學習小實驗的第一篇。主要內容如下:
讀入資料並清洗資料
探索理解輸入資料的特點
分析如何為學習演算法呈現資料
選擇正確的模型和學習演算法
評估程式表現的準確性
讀入資料 Reading the data
當讀入資料時,你將面臨處理無效或丟失資料的問題,好的處理方式相比於精確的科學來說,更像是一種藝術。因為這部分處理適當可以適用於更多的機器學習演算法並因此提高成功的機率。
用NumPy有效地咀嚼資料,用SciPy智慧地吸收資料
Python是一個高度最佳化的解釋性語言,在處理數值繁重的演算法方面要比C等語言慢很多,那為什麼依然有很多科學家和公司在計算密集的領域將賭注下在Python上呢?因為Python可以很容易地將數值計算任務分配給C或Fortran這些底層擴充套件。其中NumPy和SciPy就是其中代表。NumPy提供了很多有效的資料結構,比如array,而SciPy提供了很多演算法來處理這些arrays。無論是矩陣操作、線性代數、最最佳化問題、聚類,甚至快速傅立葉變換,該工具箱都可以滿足需求。
讀入資料操作
這裡我們以網頁點選資料為例,第一維屬性是小時,第二維資料是點選個數。
import scipy as sp data = sp.genfromtxt('web_traffic.tsv', delimiter='\t')
預處理和清洗資料
當你準備好了你的資料結構用於儲存處理資料後,你可能需要更多的資料來確保預測活動,或者擁有了很多資料,你需要去思考如何更好的進行資料取樣。在將原始資料(rawdata)進行訓練之前,對資料進行提煉可以起到很好的作用,有時,一個用提煉的資料的簡單的演算法要比使用原始資料的高階演算法的表現效果要好。這個工作流程被稱作特徵工程(feature engineering)。Creative and intelligent that you are, you will immediately see the results。
由於資料集中可能還有無效數值(nan),我們可以事先看一下無效值的個數:
hours = data[:,0] hits = data[:,1] sp.sum(sp.isnan(hits))
用下面的方法將其過濾掉:
#cleaning the data hours = hours[~sp.isnan(hits)] hits = hits[~sp.isnan(hits)]
為了將資料給出一個直觀的認識,用Matplotlib的pyplot包來將資料呈現出來。
import matplotlib.pyplot as plt plt.scatter(hours,hits) plt.title("Web traffic over the last month") plt.xlabel("Time") plt.ylabel("Hits/hour") plt.xticks([w*7*24 for w in range(10)], ['week %i'%w for w in range(10)]) plt.autoscale(tight=True) plt.grid() plt.show()
其顯示效果如下:
選擇合適的學習演算法
選擇一個好的學習演算法並不是從你的工具箱中的三四個演算法中挑選這麼簡單,實際上有更多的演算法你可能沒有見過。所以這是一個權衡不同的效能和功能需求的深思熟慮的過程,比如執行速度和準確率的權衡,,可擴充套件性和易用性的平衡。
現在,我們已經對資料有了一個直觀的認識,我們接下來要做的是找到一個真實的模型,並且能推斷未來的資料走勢。
用逼近誤差(approximation error)來選擇模型
在很多模型中選擇一個正確的模型,我們需要用逼近誤差來衡量模型預測效能,並用來選擇模型。這裡,我們用預測值和真實值差值的平方來定義度量誤差:
def error(f, x, y): return sp.sum((f(x)-y)**2)
其中f表示預測函式。
用簡單直線來擬合資料
我們現在假設該資料的隱含模型是一條直線,那麼我們還如何去擬合這些資料來使得逼近誤差最小呢?SciPy的polyfit()函式可以解決這個問題,給出x和y軸的資料,還有引數order(直線的order是1),該函式給出最小化逼近誤差的模型的引數。
fp1, residuals, rank, sv, rcond = sp.polyfit(hours, hits, 1, full=True)
fp1是polyfit函式返回模型引數,對於直線來說,它是直線的斜率和截距。
如果polyfit的引數full為True的話,將得到擬合過程中更多有用的資訊,這裡只有residuals是我們感興趣的,它正是該擬合直線的逼近誤差。
然後將該線在圖中畫出來:
#fit straight line model fp1, residuals, rank, sv, rcond = sp.polyfit(hours, hits, 1, full=True) fStraight = sp.poly1d(fp1) #draw fitting straight line fx = sp.linspace(0,hours[-1], 1000) # generate X-values for plotting plt.plot(fx, fStraight(fx), linewidth=4) plt.legend(["d=%i" % fStraight.order], loc="upper left")
用更高階的曲線來擬合資料
用直線的擬合是不是很好呢?用直線擬合的誤差是317,389,767.34,這說明我們的預測結果是好還是壞呢?我們不妨用更高階的曲線來擬合資料,看是不是能得到更好的效果。
其逼近誤差為:
Error of straight line: 317389767.34 Error of Curve2 line: 179983507.878 Error of Curve3 line: 139350144.032 Error of Curve10 line: 121942326.364 Error of Curve50 line: 109504587.153
這裡我們進一步看一下實驗結果,看看我們的預測曲線是不是很好的擬合資料了呢?尤其是看一下多項式的階數從10到50的過程中,模型與資料貼合太緊,這樣模型不但是去擬合資料背後的模型,還去擬合了噪聲資料,導致曲線震盪劇烈,這種現象叫做 過擬合 。
小結
從上面的小實驗中,我們可以看出,如果是直線擬合的話就太簡單了,但多項式的階數從10到50的擬合又太過了,那麼是不是2、3階的多項式就是最好的答案呢?但我們同時發現,如果我們以它們作為預測的話,那它們又會無限制增長下去。所以,我們最後反省一下,看來我們還是沒有真正地理解資料。
衡量效能指標
作為一個ML的初學者,在衡量學習器效能方面會遇到很多問題或錯誤。如果是拿你的訓練資料來進行測試的話,這可能是一個很簡單的問題;而當你遇到的不平衡的訓練資料時,資料就決定了預測的成功與否。
回看資料
我們再仔細分析一下資料,看一下再week3到week4之間,好像是有一個明顯的拐點,所以我們把week3.5之後的資料分離出來,訓練一條新的曲線。
inflection = 3.5*7*24 #the time of week3.5 is an inflection time1 = hours[:inflection] value1 = hits[:inflection] time2 = hours[inflection:] value2 = hits[inflection:] fStraight1p = sp.polyfit(time1,value1,1) fStraight1 = sp.poly1d(fStraight1p) fStraight2p = sp.polyfit(time2,value2,1) fStraight2 = sp.poly1d(fStraight2p)
顯然,這兩條直線更好的描述了資料的特徵,雖然其逼近誤差還是比那些高階多項式曲線的誤差要大,但是這種方式的擬合可以更好的獲取資料的發展趨勢。相對於高階多項式曲線的過擬合現象,對於低階的曲線,由於沒有很好的描述資料,而導致欠擬合的情形。所以為了更好的描述資料特徵,使用2階曲線來擬合資料,來避免過擬合和欠擬合現象的發生。
訓練與測試
我們訓練得到了一個模型,這裡就是我們擬合的兩個曲線。為了驗證我們訓練的模型是否準確,我們可以在最初訓練時將一部分訓練資料拿出來,當做測試資料來使用,而不僅僅透過逼近誤差來判別模型好壞。
總結
這一小節作為機器學習小實驗的引入,主要傳遞兩點意思:
1、要訓練一個學習器,必須理解和提煉資料,將注意力從演算法轉移到資料上
2、學習如何進行機器學習實驗,不要混淆訓練和測試資料