BP網絡實現手寫數字識別代碼解讀
1.添加偏置
#添加偏置
temp=np.ones([X.shape[0],X.shape[1]+1])
temp[:,0:-1]=X
X=temp
np.ones()函數
numpy.ones()函數的功能是返回一個全都是1的N維數組,其中shape(用來指定返回數組的大小)、dtype(數組元素的類型)、order(是否以內存中的C或Fortran連續(行或列)順序存儲多維數據)。后兩個參數都是可選的,一般只需設定第一個參數。
shape[]的功能是: 0查詢行數,1查詢列數
temp=np.ones([X.shape[0],X.shape[1]+1])
#在這里的作用就是先產生一個和X一樣多行但是多一列的全部是1的數組
temp[:,0:-1]=X
#用X數組的值覆蓋整個新數組的所有行,第一列到最后一列,最后一列依舊全是1
X=temp
#再把整個增加一列偏置的數組給數據集X,得到帶有偏置的數據集
舉一個小例子:
import numpy as np
from sklearn.datasets import load_digits
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
X=np.array([[1,2,3],
[1,2,3],
[1,2,3]])#使用python中的數組使一定要先numpy.array
print('原來的數組:\n',X)
temp=np.ones([X.shape[0],X.shape[1]+1])
temp[:,0:-1]=X
X=temp
print('添加偏置后的數組:\n',X)
運行結果:
2.隨機選取一個數據並將其轉換為2維
i =np.random.randint(X.shape[0])#隨機選取一個數據
x=[X[i]]
x=np.atleast_2d(x)#轉為2維數據
函數解析:
numpy.random.randint(low, high=None, size=None, dtype='l')
用於產生隨機數,隨機數組
low: int
生成的數值最低要大於等於low。
(hign = None時,生成的數值要在[0, low)區間內)
high: int (可選)
如果使用這個值,則生成的數值在[low, high)區間。
size: int or tuple of ints(可選)
輸出隨機數的尺寸,比如size = (m * n* k)則輸出同規模即m * n* k個隨機數。默認是None的,僅僅返回滿足要求的單一隨機數。
dtype: dtype(可選):
想要輸出的格式。如int64、int等等
import numpy as np
from sklearn.datasets import load_digits
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
i=np.random.randint(low=5,high=10,size=5)#產生一個在(5,10)之間的長度為5的一位數組
print('產生一個在(5,10)之間的長度為5的一位數組:\n',i)
j=np.random.randint(low=5,high=10,size=(2,3))#產生一個在(5,10)之間的2行3列的數組
print('產生一個在(5,10)之間的2行3列的數組:\n',j)
運行截圖:
i =np.random.randint(X.shape[0])#隨機選取一個數據
#X.shape[0]是這個數組的行數,我們在所有行中挑選出一行,i就是這一行的行數
x=[X[i]]#用x保存行數
x=np.atleast_2d(x)#轉為2維數據
#np.atleast_Xd轉換為X維的數組
BP算法部分
L1=sigomid(np.dot(x,self.V))#隱藏層的輸出
L2=sigomid(np.dot(L1,self.W))#輸出層的輸出
L2_delta=(y[i]-L2)*dsigomid(L2)
L1_delta=L2_delta.dot(self.W.T)*dsigomid(L1)
self.W+=lr*L1.T.dot(L2_delta)
self.V+=lr*x.T.dot(L1_delta)
下面的部分可能會涉及到我昨天的博客記錄到的BP算法推導
函數 numpy.dot()
dot是用來計算矩陣乘法的,dot(x,y) 中 x是 mn的矩陣,y是nm的矩陣
配上圖片公式可能會更有利於理解,因為這是在循環中,我就把第一輪的數學公式和代碼匹配
L1=sigomid(np.dot(x,self.V))#隱藏層的輸出
L2=sigomid(np.dot(L1,self.W))#輸出層的輸出
#根據delta學習規則求偏導數, (traget-真實值)X sigomid函數的導數 X 前面隱藏層∑和權值的偏導數
L2_delta=(y[i]-L2)*dsigomid(L2)
L1_delta=L2_delta.dot(self.W.T)*dsigomid(L1)
第一行代碼是:
第二行代碼:
權重w1和權重w7的計算過程是有重復的故在算出W7 偏導后的數值依然可以參與計算到W1的delta,代碼算法和公式推導並不是一一對應的,跟着感覺走是了。我們推導的時候,是先算出 △W7再 算△W1,並沒有發現他們的偏導中是存在一定關系的,L2_delta.dot(self.W.T)就是把∑求和 net和out的關系。而dsigomid(L1)這又是求偏導的關系,算出來就是 關於輸入層和隱藏層之間的w的delta。
#delta 算出來了就用來改變原來權重的數值,使之不斷更新
self.W+=lr*L1.T.dot(L2_delta)
self.V+=lr*x.T.dot(L1_delta)
這里就很好理解了,只要能理解一輪做出的訓練,就基本上理解了BP反向傳播思想。
反饋訓練狀況:
#每訓練1000次預測一次准確率
if n%1000==0:
predictions=[]
for j in range(X_test.shape[0]):
o=self.predict(X_test[j])
predictions.append(np.argmax(o))#獲取預測結果
accuracy=np.mean(np.equal(predictions,y_test))
print('epoch:',n,'accuracy:',accuracy)
函數解讀:
numpy.argmax(array,axis=n)#返回n維的最大索引
numpy.mean()#對所有元素求平均值,可以算出精確度
numpy.equal()#判斷兩個數組是否相同
def predict(self,x):
temp=np.ones(x.shape[0]+1)
temp[0:-1]=x
x=temp
x=np.atleast_2d(x)#轉化為2維數據
L1=sigomid(np.dot(x,self.V))#隱藏層輸出
L2=sigomid(np.dot(L1,self.W))#輸出層輸出
return L2
用test測試集的數據來正向計算出輸出層的數值,把這些數值放在一起和y_test做對比,反饋出當前模型的精確度。
BP神經網絡識別手寫數字
可能有的朋友很好奇數據集從哪里來的下面給大家展示一下:
用灰度的二維(8X8)數組來寫數字
下面是BP識別代碼
import numpy as np
from sklearn.datasets import load_digits
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
def sigomid(x):
return 1/(1+np.exp(-x))
def dsigomid(x):
return x*(1-x)
class NeuralNetwork:
def __init__(self,layers):#(64,100,10)
#權值的初始化,范圍-1到1
self.V=np.random.random((layers[0]+1,layers[1]+1))*2-1
self.W=np.random.random((layers[1]+1,layers[2]))*2-1
def train(self,X,y,lr=0.11,epochs=10000):
#添加偏置
temp=np.ones([X.shape[0],X.shape[1]+1])
temp[:,0:-1]=X
X=temp
for n in range(epochs+1):
i =np.random.randint(X.shape[0])#隨機選取一個數據
x=[X[i]]
x=np.atleast_2d(x)#轉為2維數據
L1=sigomid(np.dot(x,self.V))#隱藏層的輸出
L2=sigomid(np.dot(L1,self.W))#輸出層的輸出
L2_delta=(y[i]-L2)*dsigomid(L2)
L1_delta=L2_delta.dot(self.W.T)*dsigomid(L1)
self.W+=lr*L1.T.dot(L2_delta)
self.V+=lr*x.T.dot(L1_delta)
#每訓練1000次預測一次准確率
if n%1000==0:
predictions=[]
for j in range(X_test.shape[0]):
o=self.predict(X_test[j])
predictions.append(np.argmax(o))#獲取預測結果
accuracy=np.mean(np.equal(predictions,y_test))
print('epoch:',n,'accuracy:',accuracy)
def predict(self,x):
temp=np.ones(x.shape[0]+1)
temp[0:-1]=x
x=temp
x=np.atleast_2d(x)#轉化為2維數據
L1=sigomid(np.dot(x,self.V))#隱藏層輸出
L2=sigomid(np.dot(L1,self.W))#輸出層輸出
return L2
digits=load_digits()#載入數據
X=digits.data#數據
y=digits.target#標簽
#輸入數據歸一化
X-=X.min()
X/=X.max()
nm=NeuralNetwork([64,100,10])#創建網絡輸入層是64,隱藏層是100,輸出層是10
X_train,X_test,y_train,y_test=train_test_split(X,y)#這里不寫其他參數默認的是1/4是測試數據,其他為訓練
labels_train=LabelBinarizer().fit_transform(y_train)#標簽二值化
labels_test=LabelBinarizer().fit_transform(y_test)#標簽二值化
print('start~~')
nm.train(X_train,labels_train,epochs=20000)
print('end')
精確度情況:
百分之95左右的精確度,可以基本上完成手寫數字的預測。
用sklearn庫的神經網絡實現識別
from sklearn.neural_network import MLPClassifier
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import classification_report,confusion_matrix
digits = load_digits()#載入數據
x_data = digits.data #數據
y_data = digits.target #標簽
# 標准化
scaler = StandardScaler()
x_data = scaler.fit_transform(x_data)
x_train,x_test,y_train,y_test = train_test_split(x_data,y_data) #分割數據1/4為測試數據,3/4為訓練數據
mlp = MLPClassifier(hidden_layer_sizes=(100,50) ,max_iter=500)
mlp.fit(x_train,y_train )
predictions = mlp.predict(x_test)
print(classification_report(y_test, predictions))
print(confusion_matrix(y_test,predictions))
引入混淆矩陣概念
就像是一個坐標系,左邊是真實值對應的標簽,上面是目標值對應的標簽,每一個坐標都代當前真實值被預測成當前目標值的個數
我們運行以上代碼,打印出對應的混淆矩陣可以具體觀察預測情況
拿第2行舉例,有50個1被預測成1,1個1被預測成8,1個1被預測成9
以上就是我個人通過看網課AI--MOOC
還有嗶哩嗶哩上的一些課程BP算法推導
這三天的博客第一天 對着教學視頻敲代碼,運行成功,獲取一些數據處理,語法上的知識。
第二天:
自己看教學視頻,在草稿紙上演算,跟着視頻做,搞清楚變量關系,有了一點思路,慢慢理解了BP到底是怎么反向傳播的。
第三天:
把代碼和算法公式結合起來,解決了一些語法的問題,也解開了手寫數字神秘的面紗