深度学习——第一个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 @ 2023-04-18 10:48  林每天都要努力  阅读(148)  评论(1)    收藏  举报