概要
本文是用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')
預處理和清洗數據
當你準備好了你的數據結構用于存儲處理數據后,你可能需要更多的數據來確保預測活動,或者擁有了很多數據,你需要去思考如何更好的進行數據采樣。
在將原始數據(raw data)進行訓練之前,對數據進行提煉可以起到很好的作用,有時,一個用提煉的數據的簡單的算法要比使用原始數據的高級算法的表現效果要好。這個工作流程被稱作特征工程(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,這說明我們的預測結果是好還是壞呢?
我們不妨用更高階的曲線來擬合數據,看是不是能得到更好的效果。
fCurve3p = sp.polyfit(hours, hits, 3)
fCurve3 = sp.poly1d(fCurve3p)
print "Error of Curve3 line:",error(fCurve3,hours,hits)
fCurve10p = sp.polyfit(hours, hits, 10)
fCurve10 = sp.poly1d(fCurve10p)
print "Error of Curve10 line:",error(fCurve10,hours,hits)
fCurve50p = sp.polyfit(hours, hits, 50)
fCurve50 = sp.poly1d(fCurve50p)
print "Error of Curve50 line:",error(fCurve50,hours,hits)
其逼近誤差為:
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、學習如何進行機器學習實驗,不要混淆訓練和測試數據
未來,我將加快速度,學習并實踐。
參考文獻
Building Machine Learning Systems with Python. Richert,W . Coelho,L P
轉載請注明作者Jason Ding及其出處
Github主頁(http://jasonding1354.github.io/)
CSDN博客(http://blog.csdn.net/jasonding1354)
簡書主頁(http://www.lxweimin.com/users/2bd9b48f6ea8/latest_articles)