這會是激動人心的一章,由於咱們將首次接觸到最優化算法。仔細想一想就會發現,其實咱們平常生活中遇到過不少最優化問題,好比如何在最短期內從A點到達B點?如何投入最少工做量卻得到最大的效益?如何設計發動機使得油耗最少而功率最大?可見,最優化的做用十分強大。接下來,咱們介紹幾個最優化算法,並利用它們訓練出一個非線性函數用於分類。算法
假設如今有一些數據點,咱們用一條直線對這些點進行擬合(該線稱爲最佳擬合直線),這個擬合過程就稱做迴歸。利用Logistic迴歸進行分類的主要思想是:根據現有數據對分類邊界線創建迴歸公式,以此進行分類。這裏的「迴歸」一詞源於最佳擬合,表示要找到最佳擬合參數集,其背後的數學分析將在下一部分介紹。訓練分類器時的作法就是尋找最佳擬合參數,使用的是最優化算法。接下來介紹這個二值型輸出分類器的數學原理。數組
Logistic迴歸的通常過程app
(1)收集數據:採用任意方法收集數據。dom
(2)準備數據:因爲須要進行距離計算,所以要求數據類型爲數值型。另外,結構化數據格式則最佳。機器學習
(3)分析數據:採用任意方法對數據進行分析。編輯器
(4)訓練算法:大部分時間將用於訓練,訓練的目的是爲了找到最佳的分類迴歸係數。ide
(5)測試算法:一旦訓練步驟完成,分類將會很快。函數
(6)使用算法:首先,咱們須要輸入一些數據,並將其轉換成對應的結構化數值;接着,基於訓練好的迴歸係數就能夠對這些數值進行簡單的迴歸計算,斷定它們屬於哪一個類別;在這以後,咱們就能夠在輸出的類別上作一些其餘分析工做。學習
優勢:計算代價不高,易於理解和實現。測試
缺點:容易欠擬合,分類精度可能不高。
適用數據類型:數值型和標稱型數據。
咱們想要的函數應該是,能接受全部的輸入而後預測出類別。例如,在兩個類的狀況下,上述函數輸出0或1。或許你以前接觸過具備這種性質的函數,該函數稱爲海維塞德階躍函數(Heaviside step function),或者直接稱爲單位階躍函數。然而,海維塞德階躍函數的問題在於:該函數在跳躍點上從0瞬間跳躍到1,這個瞬間跳躍過程有時很難處理。幸虧,另外一個函數也有相似的性質,且數學上更易處理,這就是Sigmoid函數。Sigmoid函數具體的計算公式以下:
圖5-1給出了Sigmoid函數在不一樣座標尺度下的兩條曲線圖。當x爲0時,Sigmoid函數值爲0.5。隨着x的增大,對應的Sigmoid值將逼近於1;而隨着x的減少,Sigmoid值將逼近於0。若是橫座標刻度足夠大(圖5-1下圖),Sigmoid函數看起來很像一個階躍函數。
圖5-1 兩種座標尺度下的Sigmoid函數圖。上圖的橫座標爲-5到5,這時的曲線變化較爲平滑;下圖橫座標的尺度足夠大,能夠看到,在x=0點處Sigmoid函數看起來很像階躍函數
所以,爲了實現Logistic迴歸分類器,咱們能夠在每一個特徵上都乘以一個迴歸係數,而後把全部的結果值相加,將這個總和代入Sigmoid函數中,進而獲得一個範圍在0~1之間的數值。任何大於0.5的數據被分入1類,小於0.5即被納入0類。因此,Logistic迴歸也能夠被當作是一種機率估計。肯定了分類器的函數形式以後,如今的問題變成了:最佳迴歸係數是多少?如何肯定它們的大小?
Sigmoid函數的輸入記爲z,由下面公式得出: 其中 wi 爲最佳擬合參數,每一 列 數據都對應一個w擬合參數。
若是採用向量的寫法,上述公式能夠寫成z=wx,它表示將這兩個數值向量對應元素相乘而後所有加起來即獲得z值。其中的向量x是分類器的輸入數據,向量w也就是咱們要找到的最佳參數(係數),從而使得分類器儘量地精確。爲了尋找該最佳參數,須要用到最優化理論的一些知識。下面首先介紹梯度上升的最優化方法,咱們將學習到如何使用該方法求得數據集的最佳參數。接下來,展現如何繪製梯度上升法產生的決策邊界圖,該圖能將梯度上升法的分類效果可視化地呈現出來。最後咱們將學習隨機梯度上升算法,以及如何對其進行修改以得到更好的結果。
梯度上升法
咱們介紹的第一個最優化算法叫作梯度上升法。梯度上升法基於的思想是:要找到某函數的最大值,最好的方法是沿着該函數的梯度方向探尋。若是梯度記爲∇,則函數f(x,y)的梯度由下式表示:
這是機器學習中最易形成混淆的一個地方,但在數學上並不難,須要作的只是牢記這些符號的意義。這個梯度意味着要沿x的方向移動,沿y的方向移動
。其中,函數f(x,y)必需要在待計算的點上有定義而且可微。一個具體的函數例子見圖5-2。
圖5-2 梯度上升算法到達每一個點後都會從新估計移動的方向。從P0開始,計算完該點的梯度,函數就根據梯度移動到下一點P1。在P1點,梯度再次被從新計算,並沿新的梯度方向移動到P2。如此循環迭代,直到知足中止條件。迭代的過程當中,梯度算子老是保證咱們能選取到最佳的移動方向
圖5-2中的梯度上升算法沿梯度方向移動了一步。能夠看到,梯度算子老是指向函數值增加最快的方向。這裏所說的是移動方向,而未提到移動量的大小。該量值稱爲步長,記作α。用向量來表示的話,梯度上升算法的迭代公式以下:
該公式將一直被迭代執行,直至達到某個中止條件爲止,好比迭代次數達到某個指定值或算法達到某個能夠容許的偏差範圍。
圖5-3 一個簡單數據集,下面將採用梯度上升法找到Logistic迴歸分類器在此數據集上的最佳迴歸係數
圖5-3中有100個樣本點,每一個點包含兩個數值型特徵:X1和X2。在此數據集上,咱們將經過使用梯度上升法找到最佳迴歸係數,也就是擬合出Logistic迴歸模型的最佳參數。
梯度上升法的僞代碼以下:
每一個迴歸係數初始化爲1重複R次:
計算整個數據集的梯度
使用alpha×gradient更新迴歸係數的向量
返回迴歸係數
程序清單5-1 Logistic迴歸梯度上升優化算法
1 def loadDataSet(): #每行前兩個值分別是X1和X2,第三個值是數據對應的類別標籤。此外,爲了方便計算,該函數還將X0的值設爲1.0 2 dataMat = []; labelMat = [] 3 fr = open('testSet.txt') 4 for line in fr.readlines(): 5 lineArr = line.strip().split() 6 dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) 7 labelMat.append(int(lineArr[2])) 8 return dataMat,labelMat # 分類用 1 ,0 表示 9 10 def sigmoid(inX): 11 return 1.0/(1+exp(-inX)) 12 13 def gradAscent(dataMatIn, classLabels): # 梯度上升算法 變量alpha是向目標移動的步長,maxCycles是迭代次數。 14 dataMatrix = mat(dataMatIn) # 數據集轉 矩陣 15 labelMat = mat(classLabels).transpose() # 分類向量 轉置 16 m,n = shape(dataMatrix) 17 alpha = 0.001 18 maxCycles = 500 19 weights = ones((n,1)) #weights 與列的數量相同 20 for k in range(maxCycles): #heavy on matrix operations 21 h = sigmoid(dataMatrix*weights) #把 Z 代入到 Sigmoid 函數當中 22 error = (labelMat - h) #向量的減法 23 weights = weights + alpha * dataMatrix.transpose()* error # 數學公式 alpha=移動步長
24 return weights
接下來看看實際效果,在Python提示符下,敲入下面的代碼:
1 >>> import logRegres 2 >>> dataArr,labelMat=logRegres.loadDataSet() 3 >>> logRegres.gradAscent(dataArr,labelMat) 4 matrix([[4.12414349], [0.48007329], [-0.6168482]])
求出了數據集每一列的最佳擬合參數據 w 的值。
上面已經解出了一組迴歸係數,它肯定了不一樣類別數據之間的分隔線。那麼怎樣畫出該分隔線,從而使得優化的過程便於理解呢?下面將解決這個問題,打開logRegres.py並添加以下代碼。
畫出數據集和Logistic迴歸最佳擬合直線的函數
1 def plotBestFit(weights): 2 import matplotlib.pyplot as plt 3 dataMat,labelMat=loadDataSet() 4 dataArr = array(dataMat) 5 n = shape(dataArr)[0] 6 xcord1 = []; ycord1 = [] 7 xcord2 = []; ycord2 = [] 8 for i in range(n): 9 if int(labelMat[i])== 1: 10 xcord1.append(dataArr[i,1]); ycord1.append(dataArr[i,2]) 11 else: 12 xcord2.append(dataArr[i,1]); ycord2.append(dataArr[i,2]) 13 fig = plt.figure() 14 ax = fig.add_subplot(111) 15 ax.scatter(xcord1, ycord1, s=30, c='red', marker='s') 16 ax.scatter(xcord2, ycord2, s=30, c='green') 17 x = arange(-3.0, 3.0, 0.1) 18 y = (-weights[0]-weights[1]*x)/weights[2] #① 最佳擬合直線 19 ax.plot(x, y) 20 plt.xlabel('X1'); plt.ylabel('X2'); 21 plt.show()
程序清單5-2中的代碼是直接用Matplotlib畫出來的。惟一要指出的是,①處設置了sigmoid函數爲0。回憶5.2節,0是兩個分類(類別1和類別0)的分界處。所以,咱們設定0=w0x0+w1x1+w2x2,而後解出X2和X1的關係式(即分隔線的方程,注意X0=1)。
運行程序清單5-2的代碼,在Python提示符下輸入:
1 >>> from numpy import* 2 >>> reload(logRegres) 3 <module'logRegres'from'logRegres.py'> 4 >>> logRegres.plotBestFit(weights.getA())
梯度上升算法在500次迭代後獲得的Logistic迴歸最佳擬合直線
這個分類結果至關不錯,從圖上看只錯分了兩到四個點。可是,儘管例子簡單且數據集很小,這個方法卻須要大量的計算(300次乘法)。所以下一節將對該算法稍做改進,從而使它能夠用在真實數據集上。
梯度上升算法在每次更新迴歸係數時都須要遍歷整個數據集,該方法在處理100個左右的數據集時尚可,但若是有數十億樣本和成千上萬的特徵,那麼該方法的計算複雜度就過高了。一種改進方法是一次僅用一個樣本點來更新迴歸係數,該方法稱爲隨機梯度上升算法。因爲能夠在新樣本到來時對分類器進行增量式更新,於是隨機梯度上升算法是一個在線學習算法。與「在線學習」相對應,一次處理全部數據被稱做是「批處理」。
隨機梯度上升算法能夠寫成以下的僞代碼:
全部迴歸係數初始化爲1
對數據集中每一個樣本
計算該樣本的梯度
使用alpha×gradient更新迴歸係數值
返回迴歸係數值
程序清單5-3 隨機梯度上升算法
1 def stocGradAscent0(dataMatrix, classLabels): 2 m,n = shape(dataMatrix) 3 alpha = 0.01 4 weights = ones(n) #初始化 全部數爲1的向量 5 for i in range(m): 6 h = sigmoid(sum(dataMatrix[i]*weights)) # 兩向理相乘 h = 一個數值 7 error = classLabels[i] - h #error 這一個數值 8 weights = weights + alpha * error * dataMatrix[i] 9 return weights
能夠看到,隨機梯度上升算法與梯度上升算法在代碼上很類似,但也有一些區別:
第一,後者的變量h和偏差error都是向量,而前者則全是數值;
第二,前者沒有矩陣的轉換過程,全部變量的數據類型都是NumPy數組。
測試代碼:
1 >>> dataArr,labelMat=logRegres.loadDataSet() 2 >>> weights=logRegres.stocGradAscent0(array(dataArr),labelMat) 3 >>> weights 4 array([ 1.01702007, 0.85914348, -0.36579921])
5 >>>logRegres.plotBestFit(weights)
執行完畢後將獲得圖5-5所示的最佳擬合直線圖,該圖與圖5-4有一些類似之處。能夠看到,擬合出來的直線效果還不錯,但並不像圖5-4那樣完美。這裏的分類器錯分了三分之一的樣本。
圖5-5 隨機梯度上升算法在上述數據集上的執行結果,最佳擬合直線並不是最佳分類線
直接比較程序清單5-3和程序清單5-1的代碼結果是不公平的,後者的結果是在整個數據集上迭代了500次才獲得的。一個判斷優化算法優劣的可靠方法是看它是否收斂,也就是說參數是否達到了穩定值,是否還會不斷地變化?對此,咱們在程序清單5-3中隨機梯度上升算法上作了些修改,使其在整個數據集上運行200次。最終繪製的三個迴歸係數的變化狀況如圖5-6所示。
圖5-6 運行隨機梯度上升算法,在數據集的一次遍歷中迴歸係數與迭代次數的關係圖。迴歸係數通過大量迭代才能達到穩定值,而且仍然有局部的波動現象
圖5-6展現了隨機梯度上升算法在200次迭代過程當中迴歸係數的變化狀況。其中的係數2,也就是圖5-5中的X2只通過了50次迭代就達到了穩定值,但係數1和0則須要更屢次的迭代。另外值得注意的是,在大的波動中止後,還有一些小的週期性波動。不難理解,產生這種現象的緣由是存在一些不能正確分類的樣本點(數據集並不是線性可分),在每次迭代時會引起係數的劇烈改變。咱們指望算法能避免來回波動,從而收斂到某個值。另外,收斂速度也須要加快。
對於圖5-6存在的問題,能夠經過修改程序清單5-3的隨機梯度上升算法來解決,具體代碼以下。
程序清單5-4 改進的隨機梯度上升算法
1 def stocGradAscent1(dataMatrix, classLabels, numIter=150): 2 m,n = shape(dataMatrix) 3 weights = ones(n) #initialize to all ones 4 for j in range(numIter): 5 dataIndex = range(m) 6 for i in range(m): 7 alpha = 4/(1.0+j+i)+0.0001 #apha decreases with iteration, does not // ①alpha每次迭代時須要調整 8 randIndex = int(random.uniform(0,len(dataIndex)))#go to 0 because of the constant// ② 隨機選取更新 9 h = sigmoid(sum(dataMatrix[randIndex]*weights)) 10 error = classLabels[randIndex] - h 11 weights = weights + alpha * error * dataMatrix[randIndex] 12 del(dataIndex[randIndex]) 13 return weights
程序清單5-4與程序清單5-3相似,但增長了兩處代碼來進行改進。第一處改進在①處。一方面,alpha在每次迭代的時候都會調整,這會緩解圖5-6上的數據波動或者高頻波動。另外,雖然alpha會隨着迭代次數不斷減少,但永遠不會減少到0,這是由於①中還存在一個常數項。必須這樣作的緣由是爲了保證在屢次迭代以後新數據仍然具備必定的影響。若是要處理的問題是動態變化的,那麼能夠適當加大上述常數項,來確保新的值得到更大的迴歸係數。另外一點值得注意的是,在下降alpha的函數中,alpha每次減小1/(j+i),其中j是迭代次數,i是樣本點的下標。這樣當j<<max(i)時,alpha就不是嚴格降低的。避免參數的嚴格降低也常見於模擬退火算法等其餘優化算法中。
程序清單5-4第二個改進的地方在②處,這裏經過隨機選取樣原本更新迴歸係數。這種方法將減小週期性的波動(如圖5-6中的波動)。具體實現方法與第3章相似,這種方法每次隨機從列表中選出一個值,而後從列表中刪掉該值(再進行下一次迭代)。
此外,改進算法還增長了一個迭代次數做爲第3個參數。若是該參數沒有給定的話,算法將默認迭代150次。若是給定,那麼算法將按照新的參數值進行迭代。與stocGradAscent1()相似,圖5-7顯示了每次迭代時各個迴歸係數的變化狀況。
圖5-7 使用樣本隨機選擇和alpha動態減小機制的隨機梯度上升算法stocGradAscent1()所生成的係數收斂示意圖。該方法比採用固定alpha的方法收斂速度更快
比較圖5-7和圖5-6能夠看到兩點不一樣。第一點是,圖5-7中的係數沒有像圖5-6裏那樣出現週期性的波動,這歸功於stocGradAscent1()裏的樣本隨機選擇機制;第二點是,圖5-7的水平軸比圖5-6短了不少,這是因爲stocGradAscent1()能夠收斂得更快。此次咱們僅僅對數據集作了20次遍歷,而以前的方法是500次。
下面看看在同一個數據集上的分類效果
1 >>> weights=logRegres.stocGradAscent1(array(dataArr),labelMat) 2 >>> weights 3 array([ 14.38360334, 0.9962485 , -1.96508465]) 4 >>> logRegres.plotBestFit(weights)
圖5-8 使用改進的隨機梯度上升算法獲得的係數
程序運行以後應該能看到相似圖5-8的結果圖。該分隔線達到了與GradientAscent()差很少的效果,可是所使用的計算量更少。
本節將使用Logistic迴歸來預測患有疝病的馬的存活問題。這裏的數據包含368個樣本和28個特徵。我並不是育馬專家,從一些文獻中瞭解到,疝病是描述馬胃腸痛的術語。然而,這種病不必定源自馬的胃腸問題,其餘問題也可能引起馬疝病。該數據集中包含了醫院檢測馬疝病的一些指標,有的指標比較主觀,有的指標難以測量,例如馬的疼痛級別。
示例:使用Logistic迴歸估計馬疝病的死亡率
(1)收集數據:給定數據文件。
(2)準備數據:用Python解析文本文件並填充缺失值。
(3)分析數據:可視化並觀察數據。
(4)訓練算法:使用優化算法,找到最佳的係數。
(5)測試算法:爲了量化迴歸的效果,須要觀察錯誤率。根據錯誤率決定是否回退到訓練階段,經過改變迭代的次數和步長等參數來獲得更好的迴歸係數。
(6)使用算法:實現一個簡單的命令行程序來收集馬的症狀並輸出預測結果並不是難事,這能夠作爲留給讀者的一道習題。
另外須要說明的是,除了部分指標主觀和難以測量外,該數據還存在一個問題,數據集中有30%的值是缺失的。下面將首先介紹如何處理數據集中的數據缺失問題,而後再利用Logistic迴歸和隨機梯度上升算法來預測病馬的生死。
一、準備數據:處理數據中的缺失值
數據中的缺失值是個很是棘手的問題,有不少文獻都致力於解決這個問題。那麼,數據缺失究竟帶來了什麼問題?假設有100個樣本和20個特徵,這些數據都是機器收集回來的。若機器上的某個傳感器損壞致使一個特徵無效時該怎麼辦?此時是否要扔掉整個數據?這種狀況下,另外19個特徵怎麼辦?它們是否還可用?答案是確定的。由於有時候數據至關昂貴,扔掉和從新獲取都是不可取的,因此必須採用一些方法來解決這個問題。
下面給出了一些可選的作法:
•使用可用特徵的均值來填補缺失值;
•使用特殊值來填補缺失值,如-1;
•忽略有缺失值的樣本;
•使用類似樣本的均值添補缺失值;
•使用另外的機器學習算法預測缺失值。
如今,咱們對下一節要用的數據集進行預處理,使其能夠順利地使用分類算法。在預處理階段須要作兩件事:第一,全部的缺失值必須用一個實數值來替換,由於咱們使用的NumPy數據類型不容許包含缺失值。這裏選擇實數0來替換全部缺失值,剛好能適用於Logistic迴歸。這樣作的直覺在於,咱們須要的是一個在更新時不會影響係數的值。迴歸係數的更新公式以下:weights=weights+alpha*error*dataMatrix[randIndex]
若是dataMatrix的某特徵對應值爲0,那麼該特徵的係數將不作更新,即: weights=weights
另外,因爲sigmoid(0)=0.5,即它對結果的預測不具備任何傾向性,所以上述作法也不會對偏差項形成任何影響。基於上述緣由,將缺失值用0代替既能夠保留現有數據,也不須要對優化算法進行修改。此外,該數據集中的特徵取值通常不爲0,所以在某種意義上說它也知足「特殊值」這個要求。
預處理中作的第二件事是,若是在測試數據集中發現了一條數據的類別標籤已經缺失,那麼咱們的簡單作法是將該條數據丟棄。這是由於類別標籤與特徵不一樣,很難肯定採用某個合適的值來替換。採用Logistic迴歸進行分類時這種作法是合理的,而若是採用相似kNN的方法就可能不太可行。
原始的數據集通過預處理以後保存成兩個文件:horseCol-icTest.txt和horseColicTraining.txt。若是想對原始數據和預處理後的數據作個比較,能夠在http://archive.ics.uci.edu/ml/datasets/Horse+Colic瀏覽這些數據。
如今咱們有一個「乾淨」可用的數據集和一個不錯的優化算法,下面將把這些部分融合在一塊兒訓練出一個分類器,而後利用該分類器來預測病馬的生死問題。
二、測試算法:用Logistic迴歸進行分類
本章前面幾節介紹了優化算法,但目前爲止尚未在分類上作任何實際嘗試。使用Logistic迴歸方法進行分類並不須要作不少工做,所需作的只是把測試集上每一個特徵向量乘以最優化方法得來的迴歸係數,再將該乘積結果求和,最後輸入到Sigmoid函數中便可。若是對應的Sigmoid值大於0.5就預測類別標籤爲1,不然爲0。下面看看實際運行效果,打開文本編輯器並將下列代碼添加到logRegres.py文件中。
程序清單5-5 Logistic迴歸分類函數
1 def classifyVector(inX, weights): 2 prob = sigmoid(sum(inX*weights)) 3 if prob > 0.5: return 1.0 4 else: return 0.0 5 6 def colicTest(): 7 frTrain = open('horseColicTraining.txt'); frTest = open('horseColicTest.txt') 8 trainingSet = []; trainingLabels = [] 9 for line in frTrain.readlines(): 10 currLine = line.strip().split('\t') 11 lineArr =[] 12 for i in range(21): 13 lineArr.append(float(currLine[i])) 14 trainingSet.append(lineArr) 15 trainingLabels.append(float(currLine[21])) 16 trainWeights = stocGradAscent1(array(trainingSet), trainingLabels, 1000) 17 errorCount = 0; numTestVec = 0.0 18 for line in frTest.readlines(): 19 numTestVec += 1.0 20 currLine = line.strip().split('\t') 21 lineArr =[] 22 for i in range(21): 23 lineArr.append(float(currLine[i])) 24 if int(classifyVector(array(lineArr), trainWeights))!= int(currLine[21]): 25 errorCount += 1 26 errorRate = (float(errorCount)/numTestVec) 27 #print "the error rate of this test is: %f" % errorRate 28 return errorRate 29 30 def multiTest(): 31 numTests = 10; errorSum=0.0 32 for k in range(numTests): 33 errorSum += colicTest() 34 #print "after %d iterations the average error rate is: %f" % (numTests, errorSum/float(numTests)) 35
程序清單5-5的第一個函數是classifyVector(),它以迴歸係數和特徵向量做爲輸入來計算對應的Sigmoid值。若是Sigmoid值大於0.5函數返回1,不然返回0。
接下來的函數是colicTest(),是用於打開測試集和訓練集,並對數據進行格式化處理的函數。該函數首先導入訓練集,同前面同樣,數據的最後一列仍然是類別標籤。數據最初有三個類別標籤,分別表明馬的三種狀況:「仍存活」、「已經死亡」和「已經安樂死」。這裏爲了方便,將「已經死亡」和「已經安樂死」合併成「未能存活」這個標籤。數據導入以後,即可以使用函數stocGradAscent1()來計算迴歸係數向量。這裏能夠自由設定迭代的次數,例如在訓練集上使用500次迭代,實驗結果代表這比默認迭代150次的效果更好。在係數計算完成以後,導入測試集並計算分類錯誤率。總體看來,colicTest()具備徹底獨立的功能,屢次運行獲得的結果可能稍有不一樣,這是由於其中有隨機的成分在裏面。若是在stocGradAscent1()函數中迴歸係數已經徹底收斂,那麼結果纔將是肯定的。
最後一個函數是multiTest(),其功能是調用函數col-icTest()10次並求結果的平均值。下面看一下實際的運行效果,在Python提示符下輸入:
1 >>> import logRegres 2 >>> logRegres.multiTest() 3 4 RuntimeWarning: overflow encountered in exp 5 the error rate of this test is: 0.328358 6 the error rate of this test is: 0.343284 7 the error rate of this test is: 0.432836 8 the error rate of this test is: 0.402985 9 the error rate of this test is: 0.343284 10 the error rate of this test is: 0.343284 11 the error rate of this test is: 0.283582 12 the error rate of this test is: 0.313433 13 the error rate of this test is: 0.432836 14 the error rate of this test is: 0.283582 15 after 10 iterations the average error rate is: 0.350746
這邊有一個警告,是可能溢出的警告 從上面的結果能夠看到,10次迭代以後的平均錯誤率爲35%。事實上,這個結果並不差,由於有30%的數據缺失。固然,若是調整colicTest()中的迭代次數和stochGradAscent1()中的步長,平均錯誤率能夠降到20%左右。第7章中咱們還會再次使用到這個數據集。