日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

python 底层实现_用Python从底层实现一个多层感知机

發(fā)布時間:2025/4/16 python 90 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python 底层实现_用Python从底层实现一个多层感知机 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在上一篇文章中,我們從數(shù)學(xué)理論對多層感知機(jī)的反向傳播進(jìn)行了推導(dǎo)。南柯一夢寧沉淪:神經(jīng)網(wǎng)絡(luò)中反向傳播算法數(shù)學(xué)推導(dǎo)?zhuanlan.zhihu.com

這一篇文章中我們將基于上一篇文章最后給出的算法使用Python語言來實(shí)現(xiàn)一個多層感知機(jī)。

完整代碼以及代碼的使用方法,可以光顧我的GithubProfessorHuang/Python_LeNet_UnderlyingImplementation?github.com

MNIST數(shù)據(jù)準(zhǔn)備

要進(jìn)行訓(xùn)練,我們第一步需要先準(zhǔn)備好訓(xùn)練數(shù)據(jù),在這里我們使用經(jīng)典的MNIST數(shù)據(jù)集。MNIST數(shù)據(jù)集的獲取有多種方式,存儲的格式也各不相同。在這里,我建議直接在MNIST的官網(wǎng),即Yann Lecun的個人網(wǎng)站上獲取。http://yann.lecun.com/exdb/mnist/index.html

我們可以在網(wǎng)站上下載得到四個壓縮包,分別是訓(xùn)練圖片,訓(xùn)練標(biāo)簽,測試圖片和測試標(biāo)簽,解壓之后獲得四個文件的存儲格式為idx。我們需要使用Python的struct庫中unpack函數(shù)進(jìn)行解析。然后使用numpy庫將數(shù)據(jù)轉(zhuǎn)換成numpy數(shù)組的形式便于我們之后處理。

圖片數(shù)據(jù)解析為一個三維數(shù)組(也可稱為張量),格式為圖片數(shù)量×784×1.需要提前將圖片數(shù)據(jù)從0-255的整數(shù)轉(zhuǎn)換成0-1的浮點(diǎn)數(shù)。標(biāo)簽數(shù)據(jù)解析為一維數(shù)據(jù),只存儲了標(biāo)簽值的一個標(biāo)量。我們需要將它也轉(zhuǎn)換成一個三維數(shù)組,格式為標(biāo)簽數(shù)量×10×1,每個標(biāo)簽以one-hot格式存儲,即一個10維列向量,正確標(biāo)簽為下標(biāo)的數(shù)為1,其它的為0.

import numpy as np

from struct import unpack

def read_image(path):

with open(path, 'rb') as f:

magic, num, rows, cols = unpack('>4I',f.read(16))

img = np.fromfile(f, dtype=np.uint8).reshape(num, 784, 1) # 在這里可以調(diào)整圖片讀入格式

return img

def read_label(path):

with open(path, 'rb') as f:

magic, num = unpack('>2I',f.read(8))

label = np.fromfile(f, dtype=np.uint8)

return label

def normalize_image(image):

img = image.astype(np.float32)/255.0

return img

def one_hot_label(label):

lab = np.zeros((label.size, 10))

for i, row in enumerate(lab):

row[label[i]] = 1

return lab

# 加載數(shù)據(jù)集以及數(shù)據(jù)預(yù)處理

def dataset_loader():

train_image = read_image(r'C:\Users\95410\Downloads\數(shù)據(jù)集\MNIST\train-images.idx3-ubyte')

train_label = read_label(r'C:\Users\95410\Downloads\數(shù)據(jù)集\MNIST\train-labels.idx1-ubyte')

test_image = read_image(r'C:\Users\95410\Downloads\數(shù)據(jù)集\MNIST\t10k-images.idx3-ubyte')

test_label = read_label(r'C:\Users\95410\Downloads\數(shù)據(jù)集\MNIST\t10k-labels.idx1-ubyte')

train_image = normalize_image(train_image)

train_label = one_hot_label(train_label)

train_label = train_label.reshape(train_label.shape[0],train_label.shape[1],1)

test_image = normalize_image(test_image)

test_label = one_hot_label(test_label)

test_label = test_label.reshape(test_label.shape[0],test_label.shape[1],1)

return train_image, train_label, test_image, test_label

train_image, train_label, test_image, test_label = dataset_loader()

train_image維度為60000×784×1,train_label維度為60000×10×1

test_image維度為10000×784×1,test_label維度為10000×10×1

編寫神經(jīng)網(wǎng)絡(luò)類

初始化神經(jīng)網(wǎng)絡(luò)

我們定義一個神經(jīng)網(wǎng)絡(luò)類NetWork,將涉及到的函數(shù)與神經(jīng)網(wǎng)絡(luò)各層參數(shù)封裝在里面。

class NetWork(object):

def __init__(self, sizes):

'''初始化神經(jīng)網(wǎng)絡(luò),給每層的權(quán)重和偏置賦初值權(quán)重為一個列表,列表中每個值是一個二維n×m的numpy數(shù)組偏置為一個列表,列表中每個值是一個二維n×1的numpy數(shù)組'''

self.num_layers = len(sizes)

self.sizes = sizes

self.weights = [np.random.randn(n,m) for m,n in zip(sizes[:-1], sizes[1:])] # 一定得用rnadn而不是random

self.biases = [np.random.randn(n,1) for n in sizes[1:]]

在初始化一個神經(jīng)網(wǎng)絡(luò)時,我們只需要以列表的形式提供神經(jīng)網(wǎng)絡(luò)各層的大小,神經(jīng)網(wǎng)絡(luò)各層的權(quán)重矩陣以及偏置項(xiàng)便會隨機(jī)初始化并以二維numpy數(shù)組的進(jìn)行存儲,并以列表的形式按順序存放在self.weights以及self.biases中,可以讓該神經(jīng)網(wǎng)絡(luò)中其它方法使用與更新。

注意權(quán)重矩陣的初始化使用np.random.randn提供標(biāo)準(zhǔn)正態(tài)分布的隨機(jī)數(shù),它有正也有負(fù)。而我之前不小心使用了np.random.random提供的是0-1的浮點(diǎn)數(shù),結(jié)果導(dǎo)致神經(jīng)網(wǎng)絡(luò)無法訓(xùn)練。

神經(jīng)網(wǎng)絡(luò)前向傳播

神經(jīng)網(wǎng)絡(luò)前向傳播很簡單,取出權(quán)重矩陣和偏置項(xiàng),通過矩陣乘法運(yùn)算和矩陣相加運(yùn)算,再經(jīng)過激活函數(shù)即可根據(jù)前一層的輸出得到當(dāng)前層的輸出,遞歸運(yùn)算下去即可得到神經(jīng)網(wǎng)絡(luò)最終的輸出。

def feed_forward(self, x):

'''完成前向傳播過程,由輸入值計算神經(jīng)網(wǎng)絡(luò)最終的輸出值輸入為一個列向量,輸出也為一個列向量'''

value = x

for i in range(len(self.weights)):

value = self.sigmoid(np.dot(self.weights[i], value) + self.biases[i])

y = value

return y

矩陣的乘法用np.dot函數(shù)實(shí)現(xiàn)。我們使用的是sigmoid函數(shù)

作為激活函數(shù)。

def sigmoid(self, z):

'''sigmoid激活函數(shù)'''

a = 1.0 / (1.0 + np.exp(-z))

return a

神經(jīng)網(wǎng)絡(luò)反向傳播

根據(jù)輸入列向量x,前向傳播出各層激活前的輸出

和激活后的輸出

為了之后計算delta誤差以及損失函數(shù)對權(quán)重矩陣的導(dǎo)數(shù)。

將最后一層輸出

與標(biāo)簽列向量y代入到損失函數(shù)對

的導(dǎo)數(shù),求得最后一層的delta誤差

利用公式

可以依次求出每層的delta誤差,Hadmard積直接用*符號即可,表示逐元素相乘。

每求出一層的delta誤差,便可以很快的帶入公式,該層的偏置的導(dǎo)數(shù)與delta誤差相等,該層權(quán)重矩陣的導(dǎo)數(shù)等于該層delta誤差右乘上上一層激活后輸出的轉(zhuǎn)置。

def backprop(self, x, y):

'''計算通過單幅圖像求得的每層權(quán)重和偏置的導(dǎo)數(shù)'''

delta_nabla_b = [np.zeros(b.shape) for b in self.biases]

delta_nabla_w = [np.zeros(w.shape) for w in self.weights]

# 前向傳播,計算各層的激活前的輸出值以及激活之后的輸出值,為下一步反向傳播計算作準(zhǔn)備

activations = [x]

zs = []

for b, w in zip(self.biases, self.weights):

z = np.dot(w, activations[-1]) + b

zs.append(z)

activation = self.sigmoid(z)

activations.append(activation)

# 先求最后一層的delta誤差以及b和W的導(dǎo)數(shù)

cost = activations[-1] - y

delta = cost * self.sigmoid_prime(zs[-1])

delta_nabla_b[-1] = delta

delta_nabla_w[-1] = np.dot(delta, activations[-2].transpose())

# 將delta誤差反向傳播以及各層b和W的導(dǎo)數(shù),一直計算到第二層

for l in range(2, self.num_layers):

delta = np.dot(self.weights[-l+1].transpose(), delta) * self.sigmoid_prime(zs[-l])

delta_nabla_b[-l] = delta

delta_nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())

return delta_nabla_b, delta_nabla_w

backprop方法只是根據(jù)一副圖像以及對應(yīng)的標(biāo)簽求得神經(jīng)網(wǎng)絡(luò)參數(shù)的導(dǎo)數(shù),而我們使用隨機(jī)梯度下降法,需要使用一個batch的數(shù)據(jù)來更新數(shù)據(jù)。sigmoid_prime是對sigmoid函數(shù)的一階導(dǎo)數(shù):

def sigmoid_prime(self, z):

'''sigmoid函數(shù)的一階導(dǎo)數(shù)'''

return self.sigmoid(z) * (1 - self.sigmoid(z))

我們使用方法update_mini _batch來調(diào)用backprop方法實(shí)現(xiàn)對一個batch的數(shù)據(jù)進(jìn)行更新

def update_mini_batch(self, mini_batch_image, mini_batch_label, eta, mini_batch_size):

'''通過一個batch的數(shù)據(jù)對神經(jīng)網(wǎng)絡(luò)參數(shù)進(jìn)行更新需要對當(dāng)前batch中每張圖片調(diào)用backprop函數(shù)將誤差反向傳播求每張圖片對應(yīng)的權(quán)重梯度以及偏置梯度,最后進(jìn)行平均使用梯度下降法更新參數(shù)'''

nabla_b = [np.zeros(b.shape) for b in self.biases]

nabla_w = [np.zeros(w.shape) for w in self.weights]

for x,y in zip(mini_batch_image, mini_batch_label):

delta_nabla_b, delta_nabla_w = self.backprop(x, y)

nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]

nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]

self.weights = [w-(eta/mini_batch_size)*nw for w, nw in zip(self.weights, nabla_w)]

self.biases = [b-(eta/mini_batch_size)*nb for b, nb in zip(self.biases, nabla_b)]

update_mini_batch方法一次接收一個batch的訓(xùn)練圖片和對應(yīng)的訓(xùn)練標(biāo)簽,根據(jù)該batch數(shù)據(jù)求得的參數(shù)的平均導(dǎo)數(shù),使用梯度下降法對神經(jīng)網(wǎng)絡(luò)中各層權(quán)重矩陣與偏置進(jìn)行更新。

我們需要使用整個60000張訓(xùn)練數(shù)據(jù)來對神經(jīng)網(wǎng)絡(luò)進(jìn)行訓(xùn)練,因此我們需要一個更高層的函數(shù)SGD,接收訓(xùn)練數(shù)據(jù),并將訓(xùn)練數(shù)據(jù)分成一個個batch,再調(diào)用update_mini_batch方法對參數(shù)進(jìn)行更新。

def SGD(self, train_image, train_label, epochs, mini_batch_size, eta):

'''Stochastic gradiend descent隨機(jī)梯度下降法,將訓(xùn)練數(shù)據(jù)分多個batch一次使用一個mini_batch_size的數(shù)據(jù),調(diào)用update_mini_batch函數(shù)更新參數(shù)'''

for j in range(epochs):

mini_batches_image = [train_image[k:k+mini_batch_size] for k in range(0, len(train_image), mini_batch_size)]

mini_batches_label = [train_label[k:k+mini_batch_size] for k in range(0, len(train_label), mini_batch_size)]

for mini_batch_image, mini_batch_label in zip(mini_batches_image, mini_batches_label):

self.update_mini_batch(mini_batch_image, mini_batch_label, eta, mini_batch_size)

print("Epoch{0}: accuracy is{1}/{2}".format(j+1, self.evaluate(test_image, test_label), len(test_image)))

SGD接收的參數(shù)中,epochs代表訓(xùn)練輪數(shù),將60000張數(shù)據(jù)全部訓(xùn)練一遍稱為一個epoch。mini_batch_size表示batch大小,即一次使用多少張圖片對參數(shù)進(jìn)行更新。eta表示學(xué)習(xí)率。

驗(yàn)證神經(jīng)網(wǎng)絡(luò)準(zhǔn)確率

我們在SGD方法中可以看見evaluate方法,它在每訓(xùn)練完一個epoch數(shù)據(jù)后,使用10000張測試數(shù)據(jù)來驗(yàn)證我們神經(jīng)網(wǎng)絡(luò)的準(zhǔn)確率。

def evaluate(self, images, labels):

result = 0

for img, lab in zip(images, labels):

predict_label = self.feed_forward(img)

if np.argmax(predict_label) == np.argmax(lab):

result += 1

return result

驗(yàn)證的方法很簡單,依次從驗(yàn)證數(shù)據(jù)集中取出圖片,經(jīng)過神經(jīng)網(wǎng)絡(luò)前向傳播,看最終預(yù)測值與圖片的標(biāo)簽是否一致即可。evaluate方法返回10000張圖片中預(yù)測正確的數(shù)量。

測試我們的神經(jīng)網(wǎng)絡(luò)

我們使用兩行代碼即可對我們的神經(jīng)網(wǎng)絡(luò)定義以及訓(xùn)練

# 訓(xùn)練神經(jīng)網(wǎng)絡(luò)

net_trained = NetWork([784, 30, 10])

net_trained.SGD(train_image, train_label, 30, 10, 3)

我們設(shè)置一個三層的神經(jīng)網(wǎng)絡(luò),唯一的一個隱藏層只有30個神經(jīng)元,可以加快我們的訓(xùn)練速度。我們調(diào)用SGD方法,訓(xùn)練30個epoch,batch大小為10,學(xué)習(xí)率為3.這些參數(shù)都是我們可以調(diào)整的,但相應(yīng)地會取得不同的訓(xùn)練效果,不合適的參數(shù)有時候會導(dǎo)致訓(xùn)練無法正確進(jìn)行。

完成30個epoch的訓(xùn)練,在我的電腦上大概只需要3分鐘即可,而我們神經(jīng)網(wǎng)絡(luò)對MNIST驗(yàn)證集的預(yù)測正確率已經(jīng)可以達(dá)到94.85%。bingo!

參考:

[1]劉建平Pinard:深度神經(jīng)網(wǎng)絡(luò)(DNN)反向傳播算法(BP)深度神經(jīng)網(wǎng)絡(luò)(DNN)反向傳播算法(BP) - 劉建平Pinard - 博客園?www.cnblogs.com

[2] Neural Networks and Deep Learning by By Michael NielsenNeural networks and deep learning?neuralnetworksanddeeplearning.com

[3] 孤獨(dú)暗星: MNIST手寫數(shù)字?jǐn)?shù)據(jù)集的讀取,基于python3https://blog.csdn.net/weixin_40522523/article/details/82823812?blog.csdn.net

《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結(jié)

以上是生活随笔為你收集整理的python 底层实现_用Python从底层实现一个多层感知机的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。