Logistic迴歸算法只能用於預測結果只有兩種狀況(即要麼0,要麼1)的實例。而咱們須要的函數則是能接收全部輸入特徵,最後預測出類別。且函數能穩定在某兩個值之間,且可以平均分配,這裏就引入了數學上的一個函數,即Sigmoid函數
。python
Sigmoid函數計算公式以下:git
Sigmoid函數圖像以下(來源:百度百科-Sigmoid函數):算法
Sigmoid函數特色描述:編程
當z值爲0,Sigmoid函數值爲0.5.隨着z的不斷增大,對應的Sigmoid值將逼近1;而隨着z的減少,Sigmoid值將逼近與0。若是橫座標刻度足夠大,那麼縱觀Sigmoid函數,它看起來很像一個階躍函數。數組
而Sigmoid函數中的z值是須要通過下列計算的,爲了實現Logistic迴歸分類器,咱們能夠在每個特徵上乘以一個迴歸係數,而後把全部的結果值相加,而這個相加的總和就是z值
了。再將z值代入到Sigmoid函數中,能夠獲得一個0到1之間的數值,咱們將任何大於0.5的數值歸爲1類,小於0.5的數值被歸爲0類。因此,Logistic迴歸能夠被看作是一種機率估算。網絡
在上面說明了z值的計算方法後,咱們用公式來更直觀地描述z值計算:app
用向量的寫法,上述公式能夠寫成,表示將這兩個數值向量對應元素相乘起來而後所有加起來便是z值。其中
向量x
是分類器的輸入數據,即特徵值數據;向量w
是咱們須要尋找的最佳係數,尋找最佳係數的目的是爲了讓分類器的結果儘量的精確。dom
通過上面分析,Sigmoid函數公式最終形式能夠寫成下面這種形式:機器學習
上面咱們提到z值計算中,w的值是迴歸係數,而回歸係數決定了預測結果的準確性,爲了獲取最優迴歸係數,咱們須要使用最優化方法。最優化方法這裏學習和使用兩種:梯度上升算法和隨機梯度上升算法(是對梯度上升算法的改進,使計算複雜度下降)。
算法思想:要找到某函數的最大值,最好的方法是沿着該函數的梯度方向探尋。
算法迭代公式:
其中:
w
是咱們要求的最佳係數,由於這個公式要迭代屢次,且不斷變大直到獲得一個最佳係數值,因此每次要在w的基礎上加上某個方向的步長。α
是步長,即每次的移動量。def loadDataSet(): """ 讀取測試文件中的數據,拆分獲得的每一行數據並存入相應的矩陣中,最後返回。 :return: dataMat, labelMat dataMat: 特徵矩陣 labelMat: 類型矩陣 """ # 初始化特徵列表和類型列表 dataMat = [] labelMat = [] # 打開測試數據,默認爲讀方式 fr = open("data/testSet.txt") # 經過readLines()方法能夠得到文件中的全部行信息 for line in fr.readlines(): # 將一行中大的信息先經過strip()方法去掉首尾空格 # 再經過split()方法進行分割,默認分割方式是空格分割 # 將分割好後的數據存入列表lineArr lineArr = line.strip().split() # 取出列表lineArr中的數據經過append方法插入到dataMat列表表中 # 這裏爲了方便計算,將第一列的值都設置爲1.0 dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) # 再取出文件中的最後一列的值做爲類型值存入labelMat列表中 labelMat.append(int(lineArr[2])) """ 列表中的append方法小結: 從上面兩個列表添加元素的語句能夠看出: 當咱們須要添加一行數據時,須要加[],表示將插入一行數據。 當咱們將數據直接插入到某一列的後面時,則不須要加[]。 且須要注意的是:若是一次要添加好幾個元素時,必須有[],不然會報錯。 """ return dataMat, labelMat def sigmoid(inX): """ sigmoid函數:α(z) = 1 / (1 + e^(-z)) :param inX: 函數中的參數z :return: 返回函數計算結果 """ return 1.0 / (1 + exp(-inX)) def gradAscent(dataMatIn, classLabels): """ 梯度上升算法 :param dataMatIn: 特徵值數組 :param classLabels: 類型值數組 :return: 返回最佳迴歸係數 """ # 經過numpy模塊中的mat方法能夠將列表轉化爲矩陣 dataMatrix = mat(dataMatIn) # transpose()方法是矩陣中的轉置 labelMatrix = mat(classLabels).transpose() # 經過numpy中大的shape方法能夠得到矩陣的行數和列數信息 # 當矩陣是一維矩陣時,返回的是一個數的值 # 當矩陣是二維矩陣時,返回的是一個(1*2)的元組,元組第一個元素表示行數,第二個元素表示列數 m, n = shape(dataMatrix) # m = 100; n = 3 alpha = 0.001 # 步長 maxCycles = 500 # 迭代次數 # ones()屬於numpy模塊,函數功能是生成一個全部元素都爲1的數組 # 這裏是生成一個「n行1列」的數組,數組中每個元素值都是1 weights = ones((n, 1)) # 循環迭代maxCycles次,尋找最佳參數 for k in range(maxCycles): # dataMatrix * weights 是矩陣乘法 # 矩陣相乘時注意第一個矩陣的列數要和第二個矩陣的行數相同 # (m × n) * ( n × 1) = (m × 1) 括號中表示幾行幾列 # (100 × 3) * (3 × 1) = (100 × 1) # 最後獲得一個100行1列的矩陣 # 該矩陣運算結果爲爲sigmoid函數(α(z) = 1 / (1 + e^(-z)))中的z # z的公式爲:z = w0x0 + w1x1 + w2x2 + ... + wnxn h = sigmoid(dataMatrix * weights) # 計算真實類別與預測類別的差值 error = labelMatrix - h # 按差值error的方向調整迴歸係數 # 0.01 * (3 × m) * (m × 1) # 表示每個列上的一個偏差狀況,最後獲得x1,x2,xn的係數偏移量 # 矩陣乘法,最後獲得一個更新後的迴歸係數 # 梯度上升算法公式:w:=w+α▽w f(w) # 其中α是步長,▽w f(w)是上升方向 weights = weights + alpha * dataMatrix.transpose() * error return array(weights)
輸出:
if __name__ == '__main__': dataMats, classMats = loadDataSet() dataArr = array(dataMats) weights = array(gradAscent(dataMats, classMats)) print(weights) # 輸出最佳係數w的各個值
[[ 4.12414349]
[ 0.48007329]
[-0.6168482 ]]
小結:
梯度上升算法核心函數是gradAscent(dataMatIn, classLabels)
函數,在該函數中不斷迭代使得參數w不斷優化,使得最終返回的參數最優。
上述的sigmoid(inX)
函數就是咱們使用的Sigmoid函數公式,寫成函數是由於下面咱們會頻繁的使用到這個函數,因此將該公式單獨封裝成一個函數。
loadDataSet()
函數做用就是將咱們收集到的數據讀取出來,而且將讀取出來的數據格式化存儲到相應的數組中,最後返回供外界使用和分析。
這裏小結一下列表中的append()
方法和extend()
方法:
if __name__ == '__main__': li = [] appendLi = [1, 2, 3] extendLi = [3, 4, 5] li.append(appendLi) print(li) li.extend(extendLi) print(li)
輸出:
[[1, 2, 3]]
[[1, 2, 3], 3, 4, 5]
能夠看到append()方法是將appendLi這個列表加入到li列表的新的一行,而extend()方法則是將extendLi列表中的數值取出來一個個接在li列表後面。
這裏涉及到的numpy模塊中的新函數(所謂新,是相對於我來講滴):
mat()
:將數組或則列表轉化爲矩陣transpose()
方法是矩陣的轉置。line.strip().split()
用法:能夠看到這裏是對字符串的切割,字符串line先經過strip()方法去掉了首尾的空格,在經過split()方法進行默認空格切割。兩種方法一鼓作氣,不用分紅兩不寫。
def plotBestFit(dataArr, labelMat, weights): """ 畫出決策邊界 :param dataArr: 特徵值數組 :param labelMat: 類型數組 :param weights: 最佳迴歸係數 :return: """ # 經過shape函數得到dataArr的行列數,其中[0]即行數 n = shape(dataArr)[0] # xCord1和yCord1是類型爲1的點的x和y座標值 xCord1 = [] yCord1 = [] # xCord2和yCord1是類型爲0的點的x和y座標值 xCord2 = [] yCord2 = [] # 特徵數組的每一行和類型數組的沒每一列一一對應 for i in range(n): # 當類型爲1時, # 將特徵數組中的指定行的1和2兩個下標下的值分別做爲x軸和y軸的值 if int(labelMat[i]) == 1: xCord1.append(dataArr[i, 1]) yCord1.append(dataArr[i, 2]) # 當類型爲0時, # 將特徵數組中的指定行的1和2兩個下標下的值分別做爲x軸和y軸的值 else: xCord2.append(dataArr[i, 1]) yCord2.append(dataArr[i, 2]) # figure()操做時建立或者調用畫板 # 使用時遵循就近原則,全部畫圖操做是在最近一次調用的畫圖板上實現。 fig = plt.figure() # 將fig分紅1×1的網格,在第一個格子中加載ax圖 # 參數111表示「1×1網格中的第1個表格」 # 若是參數是211則表示「2行1列的表格的中的第一個表格」 # 第幾個表格的計算順序爲從左到右,從上到下 ax = fig.add_subplot(111) # 設置散點圖參數 # 前兩個參數xCord1,yCord1表示散點對應的x和y座標值 # s=30表示散點大小爲30 # c='red'表示散點顏色爲紅色 # marker='s'表示散點的形狀,這裏是正方形 ax.scatter(xCord1, yCord1, s=30, c='red', marker='s') # 同上說明 ax.scatter(xCord2, yCord2, s=30, c='green') # 生成一個[-3.0, 3.0]範圍中間隔每0.1取一個值 x = arange(-3.0, 3.0, 0.1) # y相對於x的函數 """ 這裏的y是怎麼獲得的呢? 從dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) 可得:w0*x0+w1*x1+w2*x2 = z x0最開始就設置爲1,x2就是咱們畫圖的y值,x1就是咱們畫圖的x值。 因此:w0 + w1*x + w2*y = 0 → y = (-w0 - w1 * x) / w2 """ y = (-weights[0] - weights[1] * x) / weights[2] # 畫線 ax.plot(x, y) # 設置x軸和y軸的名稱 plt.xlabel('x1') plt.ylabel('x2') # 展現圖像 plt.show()
輸出:
if __name__ == '__main__': dataMats, classMats = loadDataSet() dataArr = array(dataMats) weights = array(gradAscent(dataMats, classMats)) plotBestFit(dataArr, classMats, weights) # 調用該函數話決策邊界
輸出圖以下:
小結:
觀察上面的圖示,能夠看出這個分類結果至關不錯,只錯分了三四個點。
畫圖方法小結:(箭頭表示賦值)
劃分畫板→ax:fig.add_subplot(111)方法,其中111表示1*1的畫板中的第1個畫板。
設置座標名稱:plt.xlabel('x1')和plt.ylabel('x2'),這裏設置x軸名稱爲x1,y軸名稱爲x2。
可是儘管該例子很小且數據集很小,求最佳係數時須要大量的計算(300次乘法)。對於幾百個左右的數據集合還能夠,可是若是是10億個樣本和成千上萬的特徵,那麼這個計算方法的複雜度過高了,甚至可能出不來最佳係數。因此下面引入隨機梯度上升算法。
def stocGradAscent0(dataMatrix, classLabels): """ 隨機梯度上升算法 :param dataMatrix: 特徵值矩陣 :param classLabels: 類型數組 :return: 最佳係數weights """ # m = 100,n = 3 m, n = shape(dataMatrix) alpha = 0.01 weights = ones(n) print(weights) # 循環迭代m次,即100次 for i in range(m): # (1 × 3) * (1 × 3) = (1 × 3) # 數組相乘,兩個數組的每一個元素對應相乘 # 最後求和 # z = w0x0 + w1x1 + wnxn # 在將z代入sigmoid函數進行計算 h = sigmoid(sum(dataMatrix[i] * weights)) # 計算實際結果和測試結果之間的偏差,按照差值調整迴歸係數 error = classLabels[i] - h # 經過梯度上升算法更新weights weights = weights + alpha * error * dataMatrix[i] return weights
使用上面的plotBestFit(dataArr, labelMat, weights)
函數分析該算法。
輸出:
if __name__ == '__main__': dataMats, classMats = loadDataSet() dataArr = array(dataMats) weights = array(stocGradAscent0(dataArr, classMats)) plotBestFit(dataArr, classMats, weights)
小結:
改進後的隨機梯度上升算法代碼以下:
def stocGradAscent1(dataMatrix, classLabels, numIter=150): """ 改進後的隨機梯度上升算法 :param dataMatrix: 特徵值矩陣 :param classLabels: 類型數組 :param numIter: 迭代次數,設置默認值爲150 :return: 最佳係數weights """ m, n = shape(dataMatrix) # 建立與列數相同矩陣,全部元素都爲1 weights = ones(n) # 隨機梯度,循環默認次數爲150次,觀察是否收斂 for j in range(numIter): # 產生列表爲[0, 1, 2 ... m-1] dataIndex = list(range(m)) for i in range(m): # i和j不斷增大,致使alpha不斷減少,單上衣不爲0, # alpha會隨着迭代不斷的減少,但永遠不會減少到0,由於後面還有一個常數項0.01 alpha = 4 / (1.0 + j + i) + 0.01 # 產生一個隨機在0-len()之間的值 # random.uniform(x, y)方法將隨機生成一個實數,他在[x, y]範圍內,x<=y。 randIndex = int(random.uniform(0, len(dataIndex))) # sum(dataMatrix[randIndex]*weights)是爲了求z值 # z = w0x0 + w1x1 + ... + wnxn h = sigmoid(sum(dataMatrix[randIndex] * weights)) # 計算實際結果和測試結果之間的偏差,按照差值調整迴歸係數 error = classLabels[randIndex] - h # 經過梯度上升算法更新weights weights = weights + alpha * error * dataMatrix[randIndex] # 刪除掉這次更新中用到的特徵數據 del(dataIndex[randIndex]) return weights
輸出:
if __name__ == '__main__': dataMats, classMats = loadDataSet() dataArr = array(dataMats) weights = array(stocGradAscent1(dataArr, classMats)) plotBestFit(dataArr, classMats, weights)
小結:
這裏咱們將使用Logistic迴歸來預測患有疝氣病症的馬的存活問題。這裏的數據包含299個訓練樣本和67個測試樣本。其中咱們經過21中特徵值(每一個特徵表明什麼咱們能夠不用關心)來進行預測患有疝病的馬的存活率。1表示存活,0表示死亡。
訓練樣本horseColicTraining.txt
展現:
測試樣本horseColicTest.txt
展現:
測試算法:用Logistic迴歸進行分類
def classifyVector(inX, weights): """ 使用梯度上升算法獲取到的最優係數來計算測試樣本中對應的Sigmoid值。 其中Sigmoid值大於0.5返回1,小於0.5返回0. :param inX: 特徵數組 :param weights: 最優係數 :return: 返回分類結果,即1或0 """ prob = sigmoid(sum(inX * weights)) if prob > 0.5: return 1.0 else: return 0.0 def colicTest(): """ 測試迴歸係數算法的用於計算疝氣病症預測病馬的死亡率的錯誤率。 這裏運用的隨機梯度算法來獲取最佳係數w1,w2,...,wn :return: 返回這次測試的錯誤率 """ # 以默認只讀方式打開訓練數據樣本和測試數據樣本 frTrain = open('data/horseColicTraining.txt') frTest = open('data/horseColicTest.txt') trainingSet = [] trainingLabels = [] # 讀取訓練數據樣本的每一行 for line in frTrain.readlines(): # 去掉首尾空格,並按tab空格數來切割字符串,並將切割後的值存入列表 currLine = line.strip().split('\t') lineArr = [] # 將21個特徵值依次加入到lineArr列表彙總 for i in range(21): lineArr.append(float(currLine[i])) # 再將lineArr列表加入到二維列表trainingSet列表中 trainingSet.append(lineArr) # 將類型值依次接到trainingLabels這個列表的末尾行 trainingLabels.append(float(currLine[21])) # 使用上面寫的改進的隨機梯度算法求得最佳係數,用於下面分類器使用區分類型 trainWeights = stocGradAscent1(array(trainingSet), trainingLabels, 300) errorCount = 0 numTestVec = 0.0 # 讀取測試數據的每一行 for line in frTest.readlines(): # 測試數據數加1 numTestVec += 1.0 # 去掉首尾空格,並以tab空格數切割字符串,並將切割後的值存入列表 currLine = line.strip().split('\t') lineArr = [] # 將21個特徵值依次加入到特徵列表lineArr中 for i in range(21): lineArr.append(float(currLine[i])) # 經過上面計算獲得的最佳係數,使用分類器計算lineArr這些特徵下的所屬的類型 if int(classifyVector(array(lineArr), trainWeights)) != int(currLine[21]): # 若是分類器獲得結果和真實結果不符,則錯誤次數加1 errorCount += 1 # 經過遍歷得到的全部測試數據量和錯誤次數求得最終的錯誤率 errorRate = float(errorCount) / numTestVec # 輸出錯誤率 print("測試結果的錯誤率爲:{:.2%}".format(errorRate)) # 返回錯誤率,用於計算n次錯誤率的平均值 return errorRate def multiTest(): """ 屢次測試算法的錯誤率取平均值,以獲得一個比較有說服力的結果。 :return: """ numTests = 10 errorSum = 0.0 # 經過10次的算法測試,並得到10次錯誤率的總和 for k in range(numTests): errorSum += colicTest() # 經過錯誤率總和/10能夠求得10次平均錯誤率並輸出 print("10次算法測試後平均錯誤率爲:{:.2%}".format(errorSum/float(numTests)))
輸出:
if __name__ == '__main__': multiTest()
測試結果的錯誤率爲:29.85%
測試結果的錯誤率爲:32.84%
測試結果的錯誤率爲:35.82%
測試結果的錯誤率爲:40.30%
測試結果的錯誤率爲:34.33%
測試結果的錯誤率爲:46.27%
測試結果的錯誤率爲:32.84%
測試結果的錯誤率爲:37.31%
測試結果的錯誤率爲:29.85%
測試結果的錯誤率爲:50.75%
10次算法測試後平均錯誤率爲:37.01%
小結:
classifyVector(inX, weights)
函數,是以最佳迴歸係數和特徵向量做爲輸入來計算最終對應的Sigmoid值。若是Sigmoid值大於0.5,函數返回1,不然返回0。注意:這個函數後面其餘實例也會常常用到,由於這個函數至關於一個分類器,能夠獲取到最終的預測結果。colicTest()
函數,是用於打開測試集和訓練測試集,並對數據進行格式化處理的函數,函數最終放回測試的錯誤率。multiTest()
函數,其功能是調用10次colicTest()函數並求結果的平均值。爲了使最終錯誤率更有說服力。從kNN算法裏面的小例子獲得啓發,使用打鬥數和接吻數這兩個特徵最終預測獲得的電影類型只有兩種:愛情片和動做片。因此符合Logistic迴歸算法的使用標準。
特徵說明:打鬥數、接吻數
類型說明:1表示動做片;0表示愛情片
自制數據代碼展現:
# !/usr/bin/python3 # -*- coding: utf-8 -*- # @Time : 2020/4/26 17:21 # @Author : zjw # @FileName: generateData.py # @Software: PyCharm # @Blog :https://www.cnblogs.com/vanishzeng/ from numpy import * def generateSomeData(fileName, num): trainFile = open(fileName, "w") for i in range(num): fightCount = float(int(random.uniform(0, 101))) kissCount = float(int(random.uniform(0, 101))) if fightCount > kissCount: label = 1 # 表示動做片 else: label = 0 # 表示愛情片 trainFile.write(str(fightCount) + "\t" + str(kissCount) + "\t" + str(float(label)) + "\n") trainFile.close() if __name__ == '__main__': generateSomeData("data/movieTraining.txt", 200) generateSomeData("data/movieTest.txt", 100)
自制數據思路:經過隨機生成打鬥數和接吻數(數量在100之內),判斷當打鬥數大於接吻數時爲動做片,記爲1;當接吻數大於打鬥數時爲愛情片,記爲0。並將數據寫入相應的txt文件中。這裏生成了200個訓練樣本和100個測試樣本。最後,還通過對生成的樣本數據,進行手動修改幾個類別,以達到更加實際現實的樣本數據。
樣本展現:
movieTraining.txt訓練樣本展現:
movieTest.txt測試樣本展現:
測試算法:使用Logistic迴歸預測電影類別(關鍵代碼展現)
def classifyVector(inX, weights): """ 使用梯度上升算法獲取到的最優係數來計算測試樣本中對應的Sigmoid值。 其中Sigmoid值大於0.5返回1,小於0.5返回0. :param inX: 特徵數組 :param weights: 最優係數 :return: 返回分類結果,即1或0 """ prob = sigmoid(sum(inX * weights)) if prob > 0.5: return 1.0 else: return 0.0 def movieTest(): """ 使用Logistic迴歸算法測試判斷電影類別的錯誤率。 :return: 錯誤率 """ trainFile = open("data/movieTraining.txt") testFile = open("data/movieTest.txt") trainSet = [] trainLabels = [] for line in trainFile.readlines(): lineArr = line.strip().split('\t') trainSet.append([float(lineArr[0]), float(lineArr[1])]) trainLabels.append(float(lineArr[2])) trainWeights = stocGradAscent1(array(trainSet), trainLabels, 500) errorCount = 0 allTestCount = 0 for line in testFile.readlines(): allTestCount += 1 lineArr = line.strip().split('\t') eigenvalue = [float(lineArr[0]), float(lineArr[1])] if classifyVector(eigenvalue, trainWeights) != float(lineArr[2]): errorCount += 1 errorRate = float(errorCount)/float(allTestCount) print("錯誤率爲:{:.2%}".format(errorRate)) return errorRate def multiTest(): """ 屢次測試算法的錯誤率取平均值,以獲得一個比較有說服力的結果。 :return: """ numTests = 10 errorSum = 0.0 # 經過10次的算法測試,並得到10次錯誤率的總和 for k in range(numTests): errorSum += movieTest() # 經過錯誤率總和/10能夠求得10次平均錯誤率並輸出 print("10次算法測試後平均錯誤率爲:{:.2%}".format(errorSum/float(numTests)))
輸出:
if __name__ == '__main__': multiTest()
錯誤率爲:11.00%
錯誤率爲:6.00%
錯誤率爲:5.00%
錯誤率爲:6.00%
錯誤率爲:5.00%
錯誤率爲:10.00%
錯誤率爲:4.00%
錯誤率爲:7.00%
錯誤率爲:14.00%
錯誤率爲:11.00%
10次算法測試後平均錯誤率爲:7.90%
小結:
在這個例子中咱們使用到一批心臟檢查樣本數據(數據來源於網絡:Statlog (Heart) Data Set),這裏我將獲取到的270個樣本數據分紅兩部分,一部分做爲訓練樣本(heartTraining.txt)有200個樣本數據,一部分做爲測試樣本(heartTest.txt)有70個樣本數據。數據各列的特徵(有13個特徵)和是否爲心臟病以下圖所示:
注:這裏中間有些特徵沒有標明,但這並不影響咱們對數據的操做。
這裏爲了使數據便於後面的操做,我將數據集中最後一列數據進行了修改,原來是1表示否,2表示是;修改後1表示是,0表示否。
重構數據集的代碼以下:
# !/usr/bin/python3 # -*- coding: utf-8 -*- # @Time : 2020/4/29 9:54 # @Author : zjw # @FileName: modifyDataFile.py # @Software: PyCharm # @Blog :https://www.cnblogs.com/vanishzeng/ def modifyData(fileName): file = open(fileName, 'r') allStr = [] for line in file.readlines(): arr = line.strip().split(' ') if arr[13] == '1': arr[13] = '0' else: arr[13] = '1' s = '' for i in range(14): s += arr[i] + '\t' s += '\n' allStr.append(s) file.close() newFile = open(fileName, 'w') newFile.writelines(allStr) newFile.close() if __name__ == '__main__': modifyData('data/heartTest.txt') modifyData('data/heartTraining.txt')
重構思路:將文本文件中的數據都讀取出來,而後進行切割,將數據中的最後一列中的值經過if判別進行替換。並將修改好後的列表轉化爲一個個字符串,存入到allStr這個總列表中。最後再以寫的方式打開文件,經過writelines()方法一次性將allStr列表寫入文件中。
重構後的文件數據集:
訓練樣本(heartTraining.txt):
測試樣本(heartTest.txt):
測試算法:使用Logistic迴歸預測是否爲心臟病(關鍵代碼展現)
def classifyVector(inX, weights): """ 使用梯度上升算法獲取到的最優係數來計算測試樣本中對應的Sigmoid值。 其中Sigmoid值大於0.5返回1,小於0.5返回0. :param inX: 特徵數組 :param weights: 最優係數 :return: 返回分類結果,即1或0 """ prob = sigmoid(sum(inX * weights)) if prob > 0.5: return 1.0 else: return 0.0 def heartTest(): """ 使用Logistic迴歸算法診斷心臟病的錯誤率。 :return:錯誤率 """ trainFile = open("data/heartTraining.txt") testFile = open("data/heartTest.txt") trainSet = [] trainLabels = [] for line in trainFile.readlines(): lineArr = line.strip().split(' ') temArr = [] for i in range(13): temArr.append(float(lineArr[i])) trainSet.append(temArr) trainLabels.append(float(lineArr[13])) trainWeights = stocGradAscent1(array(trainSet), trainLabels, 100) errorCount = 0 allTestCount = 0 for line in testFile.readlines(): allTestCount += 1 lineArr = line.strip().split(' ') eigenvalue = [] for i in range(13): eigenvalue.append(float(lineArr[i])) if classifyVector(eigenvalue, trainWeights) != float(lineArr[13]): errorCount += 1 errorRate = float(errorCount) / float(allTestCount) print("錯誤率爲:{:.2%}".format(errorRate)) return errorRate def multiTest(): """ 屢次測試算法的錯誤率取平均值,以獲得一個比較有說服力的結果。 :return: """ numTests = 10 errorSum = 0.0 # 經過10次的算法測試,並得到10次錯誤率的總和 for k in range(numTests): errorSum += heartTest() # 經過錯誤率總和/10能夠求得10次平均錯誤率並輸出 print("10次算法測試後平均錯誤率爲:{:.2%}".format(errorSum/float(numTests)))
輸出:
if __name__ == '__main__': multiTest()
錯誤率爲:18.57%
錯誤率爲:17.14%
錯誤率爲:15.71%
錯誤率爲:31.43%
錯誤率爲:24.29%
錯誤率爲:12.86%
錯誤率爲:21.43%
錯誤率爲:12.86%
錯誤率爲:32.86%
錯誤率爲:22.86%
10次算法測試後平均錯誤率爲:21.00%
小結:
改進函數展現:
def dataTest(trainFileName, testFileName, numOfFeatures): """ 函數功能:測試迴歸算法預測數據樣本的錯誤率。 函數僞代碼: 1. 讀取訓練樣本中的數據,進行格式化處理; 2. 將格式化處理後的數據傳入隨機梯度上升算法函數中,獲取到最佳參數。 3. 再讀取測試樣本中的數據,進行格式化處理後,調用分類器函數(傳入樣本特徵和最佳參數),能夠預測出最終特徵。與測試數據中的實際特徵進行比較,計算出錯誤次數。 4. 最終經過錯誤次數/測試樣本總數**求出錯誤率。 :param trainFileName: 訓練樣本的文件路徑/文件名 :param testFileName: 測試樣本的文件路徑/文件名 :param numOfFeatures: 樣本所包含的特徵數量 :return: """ trainFile = open(trainFileName) testFile = open(testFileName) trainSet = [] trainLabels = [] for line in trainFile.readlines(): lineArr = line.strip().split('\t') temArr = [] for i in range(numOfFeatures): temArr.append(float(lineArr[i])) trainSet.append(temArr) trainLabels.append(float(lineArr[numOfFeatures])) trainWeights = stocGradAscent1(array(trainSet), trainLabels, 200) errorCount = 0 allTestCount = 0 for line in testFile.readlines(): allTestCount += 1 lineArr = line.strip().split('\t') eigenvalue = [] for i in range(numOfFeatures): eigenvalue.append(float(lineArr[i])) if classifyVector(eigenvalue, trainWeights) != float(lineArr[numOfFeatures]): errorCount += 1 errorRate = float(errorCount) / float(allTestCount) print("錯誤率爲:{:.2%}".format(errorRate)) return errorRate def multiTest1(trainFileName, testFileName, numOfFeatures): """ 屢次測試算法的錯誤率取平均值,以獲得一個比較有說服力的結果。 :return: """ numTests = 10 errorSum = 0.0 # 經過10次的算法測試,並得到10次錯誤率的總和 for k in range(numTests): errorSum += dataTest(trainFileName, testFileName, numOfFeatures) # 經過錯誤率總和/10能夠求得10次平均錯誤率並輸出 print("10次算法測試後平均錯誤率爲:{:.2%}".format(errorSum/float(numTests)))
輸出:
if __name__ == '__main__': print("示例1:示例1:從疝氣病症預測病馬的死亡率") multiTest1('data/horseColicTraining.txt', 'data/horseColicTest.txt', 21) print("\n示例2:從打鬥數和接吻數預測電影類型") multiTest1('data/movieTraining.txt', 'data/movieTest.txt', 2) print("\n示例3:從心臟檢查樣本幫助診斷心臟病") multiTest1('data/heartTraining.txt', 'data/heartTest.txt', 13)
示例1:從疝氣病症預測病馬的死亡率
錯誤率爲:32.84%
錯誤率爲:29.85%
錯誤率爲:29.85%
錯誤率爲:37.31%
錯誤率爲:29.85%
錯誤率爲:34.33%
錯誤率爲:31.34%
錯誤率爲:29.85%
錯誤率爲:29.85%
錯誤率爲:31.34%
10次算法測試後平均錯誤率爲:31.64%示例2:從打鬥數和接吻數預測電影類型
錯誤率爲:14.00%
錯誤率爲:6.00%
錯誤率爲:14.00%
錯誤率爲:3.00%
錯誤率爲:6.00%
錯誤率爲:11.00%
錯誤率爲:5.00%
錯誤率爲:11.00%
錯誤率爲:5.00%
錯誤率爲:5.00%
10次算法測試後平均錯誤率爲:8.00%示例3:從心臟檢查樣本幫助診斷心臟病
錯誤率爲:20.00%
錯誤率爲:22.86%
錯誤率爲:17.14%
錯誤率爲:22.86%
錯誤率爲:18.57%
錯誤率爲:18.57%
錯誤率爲:35.71%
錯誤率爲:22.86%
錯誤率爲:35.71%
錯誤率爲:18.57%
10次算法測試後平均錯誤率爲:23.29%
小結:
dataTest(trainFileName, testFileName, numOfFeatures)
這個函數,增長了三個參數的目的是,原來三個函數中的不一樣的地方就是不一樣的文件
和不一樣的特徵數量
,因此以參數的形式傳遞進來,即便有所不一樣,可是以函數內參數形式調用便可實現相同的功能。multiTest1(trainFileName, testFileName, numOfFeatures)
這個函數,也是增長了和上面一樣的三個參數,主要是由於在這個函數中要調用dataTest()這個函數,因此須要須要經過multiTest1這個函數間接幫忙傳遞參數。Logistic迴歸算法的目的是尋找一個非線性函數Sigmoid的最佳擬合參數,求解過程可使用最優化算法來完成。
最優化算法中,最經常使用的是梯度上升算法。梯度上升算法能夠簡化爲效率比較高的隨機梯度上升算法。
改進後的隨機梯度上升算法的效果和梯度上升算法效果至關,可是佔用更少的計算資源且效率更高。
Sigmoid函數:
函數封裝很重要,能夠解決代碼冗餘問題,此外也能夠提升開發效率,沒必要每次一有新數據,就要從新寫新的函數來知足要求。好的代碼封裝,後續只要調用相應的函數就能夠完成指定的目標。
numpy相關函數:
版權聲明:歡迎轉載=>請標註信息來源於 Vanish丶博客園