机器学习入门学习笔记:(2.2)线性回归python程序实现
??上一篇博客中,推導了線性回歸的公式,這次試著編程來實現它。(機器學習入門學習筆記:(2.1)線性回歸理論推導 )
??我們求解線性回歸的思路有兩個:一個是直接套用上一篇博客最后推導出來的公式;另一個是使用梯度下降法來求解極值。如果數據量很大不建議采用第一個,采用后者能更有效地減小計算量。這篇博客后面的程序也采用的是后者。
??事先聲明,這篇博客是我自己的學習筆記,代碼參考了《機器學習實戰》一書,小部分代碼有所修改。代碼以及數據集我打包上傳好了,感興趣的可以下載下來看看:
線性邏輯回歸代碼
(今天才發現csdn下載居然沒有了0分資源,最少都要1分,有點坑啊)
??代碼不難,簡要介紹下,程序中有注釋。
??按照順序將代碼加入logRegres.py。
導入數據集
# 導入數據集 def loadDataSet():dataMat = []labelMat = []fr = open('testSet.txt')for line in fr.readlines():lineArr = line.strip().split()dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])labelMat.append(int(lineArr[2]))return dataMat, labelMat- 創建了列表dataMat和labelMat,分別用于存儲特征變量對應的值和標簽對應的值;
- 數據集存放在testSet.txt,從中讀取數據;
- 逐行讀取,遍歷所有行;讀取的每一行做一下分割,每行的最后一個數據是標簽,前面的都是特征向量的數據。將其分開后,分別加入列表dataMat和labelMat中。
激勵函數
# sigmoid函數 def sigmoid(inX):return 1.0 / (1 + exp(-inX))??我在以前的博客中介紹了激勵函數的概念。(機器學習入門學習筆記:(1)BP神經網絡原理推導及程序實現 )
??如果只考慮兩個類的情況下,我們的結果可以用0或1來表示,這種函數叫做階躍函數。但是由于階躍函數存在一個0到1的跳變的過程,這會導致不可導、不光滑,有時在數學上難以處理。所以我們采用sigmoid函數來代替激勵函數,以便能在數學上更易處理。
??直接給出sigmoid的公式,不做贅述了:
??
梯度上升法
??通常我們更多地接觸到的是梯度下降法,這兩種方法本質都是一樣的。由于梯度是變化最快的方向,梯度下降法就是向最快的方向減小,而梯度上升法就是增大,最后兩者分別收斂于局部最小值和局部最大值。
??什么時候用梯度下降法或者是梯度上升法呢?具體問題具體分析,如果我們用的是一個誤差函數,我們希望這個誤差盡可能小,那么就選擇梯度下降法了,很簡單的邏輯。
??首先,輸入的兩個參數dataMatIn和classLabels默認是列表類型,后面要使用numpy進行矩陣運算,所以調用numpy的mat()函數將其轉換為numpy下的矩陣數據類型;
??然后預設一些相關的參數,步長不宜太大,迭代次數設為500次;
??接著就是迭代了,不斷更新權值,使用如下公式:
??上面這個是梯度上升的公式,如果是梯度下降的公式把加號改成減號就行。
??關于算法推導這里不做贅述,我以前在其他博客中也有有關梯度下降法的介紹。梯度下降法和梯度上升法本質上是一樣的。(四旋翼姿態解算——梯度下降法理論推導 )
??算法不難,也可以自行查閱相關資料。
??只是看公式的話還是有點懵,梯度上升法的偽代碼如下:
??跟給出的代碼中對一對,就很容易搞懂了。在命令行測試一下看看:
??另外還有一點,我們看看代碼不難發現,可以很輕易地將這段程序改成梯度下降法,也就是將程序中的梯度更新代碼的幾個符號改一下,結果還是一樣的。
畫出邊界
??只是得到了參數還不夠直觀,我們接著使用matplotlib來是數據可視化。
# 畫出數據集和logistic回歸的最佳你和直線的函數 def plotBestFit(weights):import matplotlib.pyplot as pltdataMat, labelMat = loadDataSet()dataArr = array(dataMat)n = shape(dataArr)[0]xcord1 = []ycord1 = []xcord2 = []ycord2 = []for i in range(n):if int(labelMat[i]) == 1:xcord1.append(dataArr[i, 1])ycord1.append(dataArr[i, 2])else:xcord2.append(dataArr[i, 1])ycord2.append(dataArr[i, 2])fig = plt.figure()ax = fig.add_subplot(111)ax.scatter(xcord1, ycord1, s=30, c='red', marker='s')ax.scatter(xcord2, ycord2, s=30, c='green')x = arange(-3.0, 3.0, 0.1)y = (-weights[0] - weights[1] * x) / weights[2]ax.plot(x, y)plt.xlabel('X1')plt.ylabel('X2')plt.show()??調用庫,沒什么好說的。無非就是把點全部畫出來,最后又畫了一條邊界。
??權值返回來是一個numpy的矩陣類型,我們要先調用getA()方法將其轉換為數組類型。
??分類結果挺不錯的,只有很少的點被分錯了。
隨機梯度上升
??前面使用的梯度上升法也叫作批量梯度上升法(batch gradient ascent),它每一次更新回歸系數時都要遍歷整個數據集,如果數據集很大,計算量也會非常大。
那么一個改進方法就是一次僅僅使用一個樣本點來更新回歸系數,這個方法叫隨機梯度上升法。
??隨機梯度上升法的偽代碼:
??編寫代碼:
# 隨機梯度上升算法 def stocGradAscent(dataMatIn, classLabels):# 轉換為numpy矩陣數據類型dataMatrix = mat(dataMatIn)labelMat = mat(classLabels).transpose()m,n = shape(dataMatrix)alpha = 0.01weights = ones((n, 1))for i in range(m):h = sigmoid(sum(dataMatrix[i] * weights))error = labelMat[i] - hweights = weights + alpha *dataMatrix[i].transpose() * error return weights??這部分代碼大體上跟前面梯度上升法是一樣的,不做贅述。唯一的區別就是,沒有了迭代次數,這里是遍歷了整個數據集,使用每個樣本來對參數進行更新而不是每個樣本都計算一遍。
??測試一下:
??結果不太好,我們還需要修改程序。
改進算法
# 改進的隨機梯度上升算法 def stocGradAscent_optimized(dataMatIn, classLabels, numIter=150):# 轉換為numpy矩陣數據類型dataMatrix = mat(dataMatIn)labelMat = mat(classLabels).transpose()m, n = shape(dataMatrix)weights = ones((n, 1))for j in range(numIter):dataIndex = range(m)for i in range(n):alpha = 4 / (1.0 + j + i) + 0.01randIndex = int(random.uniform(0, len(dataIndex)))h = sigmoid(sum(dataMatrix[randIndex] * weights))error = classLabels[randIndex] - hweights = weights + alpha * dataMatrix[randIndex].transpose() * errordel(dataIndex[randIndex])return weights- 首先,步長alpha不再是一個常數,隨著迭代次數的增大不斷減小。步長alpha的值后面還加了一個0.01,因為不能讓它趨近于0,如果趨近于0了新數據的更新也幾乎沒有作用,乘積都是0。
- 然后,這里樣本選取不再是按照默認順序選取了。隨機選取數據集進行更新, 反復迭代多次。
試試看結果:
??效果跟批量梯度下降差不多了,但是隨機梯度下降的計算量要少很多。這里使用的默認迭代次數只有150次。
測試算法
從疝氣病癥預測病馬死亡的概率:
分析特征變量
def classifyVector(inX, weights):prob = sigmoid(sum(inX * weights))if prob > 0.5:return 1else:return 0??以回歸系數和輸入的特征變量計算對應的sigmoid函數的值,如果大于0.5,就預測為1;如果小于0.5, 就預測為0。
測試結果
def colicTest():# 打開測試集和數據集frTrain = open('horseColicTraining.txt')frTest = open('horseColicTest.txt')# 導入訓練集的特征變量和標簽trainingSet = []trainingLabels = []for line in frTrain.readlines():currentLine = line.strip().split('\t')lineArr = []for i in range(21):lineArr.append(float(currentLine[i]))trainingSet.append(lineArr)trainingLabels.append(float(currentLine[21]))# 使用優化過后的隨機梯度上升法計算權重trainWeights = stocGradAscent_optimized(trainingSet, trainingLabels, 1200)# 導入測試集,并檢驗效果errorCount = 0numTestVec = 0for line in frTest.readlines():numTestVec += 1currentLine = line.strip().split('\t')lineArr = []for i in range(21):lineArr.append(float(currentLine[i]))if int(classifyVector(array(lineArr), trainWeights)) != int(currentLine[21]):errorCount += 1errorRate = float(errorCount) / numTestVecprint('The error rate of this test is : %f'%errorRate)return errorRate??直接調用前面寫好的函數了,不多說了。
??調用colicTest()10次,比較結果:
def multiTest():numTests = 10errorSum = 0for k in range(numTests):errorSum += colicTest()print('After %d iterations the average error rate is: %f'%(numTests, errorSum/float(numTests)))總結
以上是生活随笔為你收集整理的机器学习入门学习笔记:(2.2)线性回归python程序实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 机器学习入门学习笔记:(2.1)线性回归
- 下一篇: caffe学习(七):使用py-fast