深度学习——第一个ANN网络

深度学习——第一个ANN网络

深度学习学的第一个网络,参考B站视频

https://www.bilibili.com/video/BV1m4411x7KU/?spm_id_from=333.999.0.0&vd_source=b1c9346178fc41766e00c3d88901f1cf

网络概述

​ 人工神经网络(Artificial Neural Network,简称ANN )中,每一层都可以用 output = A(W·input+b) 来表示,其中input代表输入,output代表输出,A,W,b是本层网络的参数,A:激活函数,将线性变换转化为非线性变换,W:权重,每一个输入的像素的重要程度,b:偏置值。

​ 本网络的只有一层神经网络,即一层网络层。

​ 输入层网络为:x=A0(input+b),其中A0 :tanh ,b的初始值为 0;

​ 网络层为:output=A1(W·x+b),其中A1:softmax,W的初始值分布为平均分布,b的初始值为0。

训练数据

​ 训练的数据为MNIST,见附录,此数据分为4个文件:2个数据文件:60000个训练数据和10000个测试数据,2个标签文件:与数据对应的标签文件。

​ 设置文件的读入路径:

#引入包来处理路径
from pathlib import Path
dataset_path=Path('./MNIST')
train_img_path=dataset_path/'train-images.idx3-ubyte'
train_lab_path=dataset_path/'train-labels.idx1-ubyte'
test_img_path=dataset_path/'t10k-images.idx3-ubyte'
test_lab_path=dataset_path/'t10k-labels.idx1-ubyte'

​ 在python中,使用pathlib模块来处理文件和文件夹,其中,Path对象用来操作目录与文件。数据文件与模型文件放到同一包内即可。

​ 具体读入操作如下:

#读取的图片相关信息
#import struct
#train_f=open(train_img_path,'rb')
##struct.unpack(format, buffer),format代表构建的格式,>代表存储方向 4个 i:整数
#struct.unpack('>4i',train_f.read(16))
#输出:(2051, 60000, 28, 28)
#可以不用struct去读,用numpy中的fromfile函数去读就可以了
#np.fromfile(train_f,dtype=np.uint8).reshape(-1,28*28)
import struct
#训练集个数,验证集个数,测试集个数
train_num=50000
valid_num=10000
test_num=10000
with open(train_img_path,'rb') as f:
struct.unpack('>4i',f.read(16))
tmp_img=np.fromfile(f,dtype=np.uint8).reshape(-1,28*28)/255
train_img=tmp_img[:train_num]
valid_img=tmp_img[train_num:]
with open(test_img_path,'rb') as f:
struct.unpack('>4i',f.read(16))
test_img=np.fromfile(f,dtype=np.uint8).reshape(-1,28*28)/255
with open(train_lab_path,'rb') as f:
struct.unpack('>2i',f.read(8))
tmp_lab=np.fromfile(f,dtype=np.uint8)
train_lab=tmp_lab[:train_num]
valid_lab=tmp_lab[train_num:]
with open(test_lab_path,'rb') as f:
struct.unpack('>2i',f.read(8))
test_lab=np.fromfile(f,dtype=np.uint8)

​ 因为测试数据test非常珍贵,不能每次都用它来评估模型,所以将训练数据分为train 和 valid。

显示图片

#画图
import matplotlib.pyplot as plt
#im = np.reshape(train_im,(28,28))
#plt.imshow(im,cmap='gray')
#定义一个图片初始化显示函数
def show_train(index):
plt.imshow(train_img[index].reshape(28,28),cmap='gray')
print('label:{}'.format(train_lab[index]))
def show_valid(index):
plt.imshow(valid_img[index].reshape(28,28),cmap='gray')
print('label:{}'.format(valid_lab[index]))
def show_test(index):
plt.imshow(test_img[index].reshape(28,28),cmap='gray')
print('label:{}'.format(test_lab[index]))

构建神经网络

​ 对神经网络结构进行初始化,具体如下:

#第一个深度学习网络ANN 人工神经网络
#输入层28*28
#激活函数A=tanh A'=softmax 非线性变换
#模型参数b0 784*1 W 784*10 b1 10*1
import numpy as np
import math
#两个激活函数
def tanh(x):
return np.tanh(x)
def softmax(x):
exp=np.exp(x-np.max(x))
return exp/exp.sum()
#模型的输入输出以及相关参数
#input为28*28 output为10*1
dimensions=[28*28,10];
#激活函数:tanh softmax 归一化
activation=[tanh,softmax];
#模型参数 第0层,第1层
distribution=[
{'b':[0,0]},
{'b':[0,0],'w':[-math.sqrt(6/(dimensions[0]+dimensions[1])),math.sqrt(6/(dimensions[0]+dimensions[1]))]},
]
#初始化参数
def init_parameters_b(layer):
dist=distribution[layer]['b']
return np.random.rand(dimensions[layer])*(dist[1]-dist[0])+dist[0]
def init_parameters_w(layer):
dist=distribution[layer]['w']
return np.random.rand(dimensions[layer-1],dimensions[layer])*(dist[1]-dist[0])+dist[0]
#自动调用面对参数进行初始化
def init_parameters():
parameter=[]
for i in range(len(distribution)):
layer_parameter={}
for j in distribution[i].keys():
if j=='b':
layer_parameter['b']=init_parameters_b(i)
continue
if j=='w':
layer_parameter['w']=init_parameters_w(i)
continue
parameter.append(layer_parameter)
return parameter
#模型最初的参数
parameters=init_parameters()

模型实现预测:

#模型实现
#预测输入为图片和模型参数
def predict(img,parameters):
l0_in=img+parameters[0]['b']
l0_out=activation[0](l0_in)
#dot代表内积
l1_in=np.dot(l0_out,parameters[1]['w'])+parameters[1]['b']
l1_out=activation[1](l1_in)
return l1_out

反向传播

首先需要定义损失函数

#损失函数Loss function 参数影响损失函数,寻找参数使损失函数值最小
#梯度下降 需要先求梯度
#导数性质: 加,数乘,乘,除
#导数链式法则:洋葱一层一层求导
#多元函数的导数:偏导*内层导数的和
#损失函数定义为L=(y0-ypre0)^2+(y1-ypre1)^2+......+(y9-ypre9)^2
#ypre就是模型的output
#代入之后,L是关于W,b1,b2的一个函数,然后对W,b1,b2,分别求偏导就可以得到其梯度方向
#激活函数的导数,需要用导数定义证明导数的正确性
def d_softmax(data):
sm=softmax(data)
return np.diag(sm)-np.outer(sm,sm)
#def d_tanh(data):
# return np.diag(1/(np.cosh(data))**2)
#优化:为了减少计算量
def d_tanh(data):
return 1/(np.cosh(data))**2
differential={softmax:d_softmax,tanh:d_tanh}
#把label转换为标准向量形式
onehot=np.identity(dimensions[-1])
#定义损失函数
def sqr_loss(img,lab,parameters):
y_pred=predict(img,parameters)
y=onehot[lab]
diff=y-y_pred
return np.dot(diff,diff)

根据损失函数对参数求梯度

#求参数的梯度
def grad_parameters(img,lab,parameters):
l0_in=img+parameters[0]['b']
l0_out=activation[0](l0_in)
#dot代表内积
l1_in=np.dot(l0_out,parameters[1]['w'])+parameters[1]['b']
l1_out=activation[1](l1_in)
diff=onehot[lab]-l1_out
act1=np.dot(differential[activation[1]](l1_in),diff)
grad_b1=-2*act1
grad_w1=-2*np.outer(l0_out,act1)
grad_b0=-2*differential[activation[0]](l0_in)*np.dot(parameters[1]['w'],act1)
return {'w1':grad_w1,'b1':grad_b1,'b0':grad_b0}

训练模型

梯度计算方法和参数更新方法

#训练神经网络
#划分atch进行训练,防止训练时间过长
batch_size=100
#返回批训练的梯度
def train_batch(current_batch,parameters):
grad_accu=grad_parameters(train_img[current_batch*batch_size+0],train_lab[current_batch*batch_size+0],parameters)
for img_i in range(1,batch_size):
grad_tmp=grad_parameters(train_img[current_batch*batch_size+img_i],train_lab[current_batch*batch_size+img_i],parameters)
for key in grad_accu.keys():
grad_accu[key]+=grad_tmp[key]
for key in grad_accu.keys():
grad_accu[key]/=batch_size
return grad_accu
#对参数进行更新
import copy
def combine_parameters(parameters,grad,learn_rate):
parameter_tmp=copy.deepcopy(parameters)
parameter_tmp[0]['b']-=learn_rate*grad['b0']
parameter_tmp[1]['b']-=learn_rate*grad['b1']
parameter_tmp[1]['w']-=learn_rate*grad['w1']
return parameter_tmp

损失和精确度方法

#判断模型精确度
#验证集损失
def valid_loss(parameters):
loss_accu=0;
for img_i in range(valid_num):
loss_accu+=sqr_loss(valid_img[img_i],valid_lab[img_i],parameters)
return loss_accu/(valid_num/10000)
#是否精确
def valid_accuracy(parameters):
correct=[predict(valid_img[img_i],parameters).argmax()==valid_lab[img_i] for img_i in range(valid_num)]
return correct.count(True)/len(correct)
#训练集损失
def train_loss(parameters):
loss_accu=0;
for img_i in range(train_num):
loss_accu+=sqr_loss(train_img[img_i],train_lab[img_i],parameters)
return loss_accu/(train_num/10000)
#是否精确
def train_accuracy(parameters):
correct=[predict(train_img[img_i],parameters).argmax()==train_lab[img_i] for img_i in range(train_num)]
return correct.count(True)/len(correct)

模型的训练过程

#进度条 有更新
from tqdm.notebook import tqdm
#定义一些变量对训练过程进行表示
current_epoch=0
train_loss_list=[]
valid_loss_list=[]
train_accu_list=[]
valid_accu_list=[]
#对所有train_img进行训练
epoch_num=10
learn_rate=0.1
for epoch in tqdm(range(epoch_num)):
for i in range(train_num//batch_size):
grad_tmp=train_batch(i,parameters)
parameters=combine_parameters(parameters,grad_tmp,learn_rate)
current_epoch+=1
train_loss_list.append(train_loss(parameters))
train_accu_list.append(train_accuracy(parameters))
valid_loss_list.append(valid_loss(parameters))
valid_accu_list.append(valid_accuracy(parameters))

模型评估

画图显示损失与精确度

#画图显示损失和准确度
lower=0
plt.plot(valid_loss_list[lower:],color='black',label='validation loss')
plt.plot(train_loss_list[lower:],color='red',label='train loss')
plt.show()
plt.plot(valid_accu_list[lower:],color='black',label='validation accuracy')
plt.plot(train_accu_list[lower:],color='red',label='train accuracy')
plt.show()

关于学习率过大的问题

#解决学习率过大的问题
rand_batch=np.random.randint(train_num//batch_size)
grad_lr=train_batch(rand_batch,parameters)
#存学习率和loss
lr_list=[]
lower=-2
upper=0
step=0.1
for lr_pow in np.linspace(lower,upper,num=20):
learn_rate=10**lr_pow
parameter_tmp=combine_parameters(parameters,grad_lr,learn_rate)
train_loss_tmp=train_loss(parameter_tmp)
lr_list.append([lr_pow,train_loss_tmp])
plt.plot(np.array(lr_list)[:,0],np.array(lr_list)[:,1],color='black',label='validation loss')
plt.show()

网路可能会出现非全局最优,停在局部最优点。
网络还有可能出现暗点

这些问题在后续的学习中慢慢解决。

posted @   林每天都要努力  阅读(119)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示