統計學習方法|感知機原理剖析及實現
代碼寫思路很清晰,膜拜下
import numpy as np
import time
def loadData(fileName):
'''
加載Mnist數據集
:param fileName:要加載的數據集路徑
:return: list形式的數據集及標記
'''
print('start to read data')
# 存放數據及標記的list
dataArr = []; labelArr = []
# 打開文件
fr = open(fileName, 'r')
# 將文件按行讀取
for line in fr.readlines():
# 對每一行數據按切割福','進行切割,返回字段列表
curLine = line.strip().split(',')
# Mnsit有0-9是個標記,由於是二分類任務,所以將>=5的作爲1,<5爲-1
if int(curLine[0]) >= 5:
labelArr.append(1)
else:
labelArr.append(-1)
#存放標記
#[int(num) for num in curLine[1:]] -> 遍歷每一行中除了以第一哥元素(標記)外將所有元素轉換成int類型
#[int(num)/255 for num in curLine[1:]] -> 將所有數據除255歸一化(非必須步驟,可以不歸一化)
dataArr.append([int(num)/255 for num in curLine[1:]])
#返回data和label
return dataArr, labelArr
def perceptron(dataArr, labelArr, iter=50):
'''
感知器訓練過程
:param dataArr:訓練集的數據 (list)
:param labelArr: 訓練集的標籤(list)
:param iter: 迭代次數,默認50
:return: 訓練好的w和b
'''
print('start to trans')
#將數據轉換成矩陣形式(在機器學習中因爲通常都是向量的運算,轉換稱矩陣形式方便運算)
#轉換後的數據中每一個樣本的向量都是橫向的
dataMat = np.mat(dataArr)
#將標籤轉換成矩陣,之後轉置(.T爲轉置)。
#轉置是因爲在運算中需要單獨取label中的某一個元素,如果是1xN的矩陣的話,無法用label[i]的方式讀取
#對於只有1xN的label可以不轉換成矩陣,直接label[i]即可,這裏轉換是爲了格式上的統一
labelMat = np.mat(labelArr).T
#獲取數據矩陣的大小,爲m*n
m, n = np.shape(dataMat)
#創建初始權重w,初始值全爲0。
#np.shape(dataMat)的返回值爲m,n -> np.shape(dataMat)[1])的值即爲n,與
#樣本長度保持一致
w = np.zeros((1, np.shape(dataMat)[1]))
#初始化偏置b爲0
b = 0
#初始化步長,也就是梯度下降過程中的n,控制梯度下降速率
h = 0.0001
#進行iter次迭代計算
for k in range(iter):
#對於每一個樣本進行梯度下降
#李航書中在2.3.1開頭部分使用的梯度下降,是全部樣本都算一遍以後,統一
#進行一次梯度下降
#在2.3.1的後半部分可以看到(例如公式2.6 2.7),求和符號沒有了,此時用
#的是隨機梯度下降,即計算一個樣本就針對該樣本進行一次梯度下降。
#兩者的差異各有千秋,但較爲常用的是隨機梯度下降。
for i in range(m):
#獲取當前樣本的向量
xi = dataMat[i]
#獲取當前樣本所對應的標籤
yi = labelMat[i]
#判斷是否是誤分類樣本
#誤分類樣本特診爲: -yi(w*xi+b)>=0,詳細可參考書中2.2.2小節
#在書的公式中寫的是>0,實際上如果=0,說明改點在超平面上,也是不正確的
if -1 * yi * (w * xi.T + b) >= 0:
#對於誤分類樣本,進行梯度下降,更新w和b
w = w + h * yi * xi
b = b + h * yi
#打印訓練進度
print('Round %d:%d training' % (k, iter))
#返回訓練完的w、b
return w, b
def test(dataArr, labelArr, w, b):
'''
測試準確率
:param dataArr:測試集
:param labelArr: 測試集標籤
:param w: 訓練獲得的權重w
:param b: 訓練獲得的偏置b
:return: 正確率
'''
print('start to test')
#將數據集轉換爲矩陣形式方便運算
dataMat = np.mat(dataArr)
#將label轉換爲矩陣並轉置,詳細信息參考上文perceptron中
#對於這部分的解說
labelMat = np.mat(labelArr).T
#獲取測試數據集矩陣的大小
m, n = np.shape(dataMat)
#錯誤樣本數計數
errorCnt = 0
#遍歷所有測試樣本
for i in range(m):
#獲得單個樣本向量
xi = dataMat[i]
#獲得該樣本標記
yi = labelMat[i]
#獲得運算結果
result = -1 * yi * (w * xi.T + b)
#如果-yi(w*xi+b)>=0,說明該樣本被誤分類,錯誤樣本數加一
if result >= 0: errorCnt += 1
#正確率 = 1 - (樣本分類錯誤數 / 樣本總數)
accruRate = 1 - (errorCnt / m)
#返回正確率
return accruRate
if __name__ == '__main__':
#獲取當前時間
#在文末同樣獲取當前時間,兩時間差即爲程序運行時間
start = time.time()
#獲取訓練集及標籤
trainData, trainLabel = loadData('../Mnist/mnist_train.csv')
#獲取測試集及標籤
testData, testLabel = loadData('../Mnist/mnist_test.csv')
#訓練獲得權重
w, b = perceptron(trainData, trainLabel, iter = 30)
#進行測試,獲得正確率
accruRate = test(testData, testLabel, w, b)
#獲取當前時間,作爲結束時間
end = time.time()
#顯示正確率
print('accuracy rate is:', accruRate)
#顯示用時時長
print('time span:', end - start)