深度神经网络

发布时间 2023-08-08 20:59:59作者: 波霸奶绿去冰三分糖
需要解决的问题:
1、掉入局部最优解的陷阱
2、过拟合(陷入对特定模式的数据进行最优化,无法对未知输入进行正确的预测)
3、梯度消失——使用ReLU作为激励函数
4、学习时间过长
一些解决方案:
1、更换最优化算法
2、批次尺寸最优化
3、对超参数的最优化(神经网络层数、神经元个数、学习系数)
4、正则化(对权重加以限制,如对权重设置上限值/使权重产生衰减)
5、对权重初始值的最优化
(这里解释了前面为什么使用随机数,避免网络梯度都一样&&网络表现能力丧失,该书选择都用N~(0,0.01)对权重初始化)
6、提前终止(即决定epoch次数)
7、数据扩张(对样本数据进行加工产生新样本,例如GAN???)
8、Dropout(按一定概率随机消除输出层以外的神经元)
9、数据预处理:常用方法
(1)正规化
def normalize(x):
x_max = np.max(x)
x_min = np.min(x)
return (x-x_min)/(x_max-x_min)
(2)标准化(之后,学习过程会变得稳定且快速)
def standardize(x):
ave = np.average(x)
std = np.std(x)
return (x-ave)/std
(3)去相关化
(4)白化
 
例:以鸢尾花数据集为例,使用深度神经网络进行分类
前提:
中间层激励函数:ReLU
输出层激励函数:SoftMax
损失函数:交叉熵误差
最优化算法:随机梯度下降法
批次尺寸:8(小批次法)
中间层神经元数量:25 中间层数:2
n=150/2=75
每轮epoch中更新的次数75/8≈9
 
步骤:
1、获取数据集、预处理
2、实现各个网络层
3、实现神经网络
4、使用最小批次法进行学习
5、统计正确率
 
from sklearn import datasets
import numpy as np
import matplotlib.pyplot as plt

# 一、数据集导入和数据预处理
iris_data = datasets.load_iris()
input_data = iris_data.data
correct = iris_data.target

n_data = len(correct)

# 标准化处理
ave_input = np.average(input_data,axis = 0) #是矩阵
std_input = np.std(input_data,axis = 0) #是向量
input_data = (input_data-ave_input)/std_input

# 将数据转化为独热编码格式
correct_data = np.zeros((n_data,3))
for i in range(n_data):
    correct_data[i,correct[i]] = 1.0

# 分割训练集和测试集
index = np.arange(n_data)
index_train = index[index%2==0]
index_test = index[index%2 !=0]

input_train = input_data[index_train,:]
correct_train = correct_data[index_train,:]
input_test = input_data[index_test,:]
correct_test = correct_data[index_test,:]

n_train = input_train.shape[0]
n_test = input_test.shape[0]

#基础值的设置
n_in = 4
n_mid = 25
n_out = 3

wb_width = 0.1 #权重和偏置的分散度
eta = 0.01
epoch = 1000 #为了时间和性能考虑,测试时可以只设置100,但是自己跑毕设实验得原数据!!!
batch_size = 8
interval = 100

# 二、各个网络层的实现,抽象出基类,感觉更多的东西可以被抽象???
#神经层的基类
class BaseLayer:
    def __init__(self,n_upper,n):
        self.w = wb_width * np.random.randn(n_upper,n)
        self.b = wb_width * np.random.randn(n)
        
    def update(self,eta):
        self.w -= eta*self.grad_w
        self.b -= eta*self.grad_b 
        
class MiddleLayer(BaseLayer):
    def forward(self,x):
        self.x = x
        self.u = np.dot(x,self.w) + self.b   #这里的u加上了self是因为有多个中间层!输出层只有一个所以不用self来约束
        self.y = np.where(self.u <=0,0,self.u)   #使用ReLU
    
    def backward(self,grad_y):
        delta = grad_y * np.where(self.u<=0,0,1)  #ReLU 的微分
        self.grad_w = np.dot(self.x.T,delta)
        self.grad_b = np.sum(delta,axis=0)
        self.grad_x = np.dot(delta,self.w.T)
        
class OutputLayer(BaseLayer):        
    def forward(self,x):
        self.x = x
        u = np.dot(x,self.w) + self.b
        self.y = np.exp(u)/np.sum(np.exp(u),axis=1,keepdims=True)   #使用SoftMax
    
    def backward(self,t):
        delta = self.y -t  #SoftMax 的微分
        self.grad_w = np.dot(self.x.T,delta)
        self.grad_b = np.sum(delta,axis=0)
        self.grad_x = np.dot(delta,self.w.T)

#三、神经网络的实现
middle_layer_1 = MiddleLayer(n_in,n_mid)
middle_layer_2 = MiddleLayer(n_mid,n_mid)
output_layer = OutputLayer(n_mid,n_out)

#由于有多层 所以对于正向处理和反向处理这些都封装一下
def forward_propagation(x):
    middle_layer_1.forward(x)
    middle_layer_2.forward(middle_layer_1.y)
    output_layer.forward(middle_layer_2.y)
    
def backward_propagation(t):
    output_layer.backward(t)
    middle_layer_2.backward(output_layer.grad_x)
    middle_layer_1.backward(middle_layer_2.grad_x)
    
def update_wb():
    middle_layer_1.update(eta)
    middle_layer_2.update(eta)
    output_layer.update(eta)
    
#计算交叉熵误差
def get_error(t,batch_size):
    return -np.sum(t*np.log(output_layer.y+1e-7))/batch_size

#记录误差
train_error_x = []
train_error_y = []
test_error_x = []
test_error_y = []

#记录学习的过程和经过
n_batch = n_train // batch_size  #取整数部分参与计算,每一轮epoch的批次尺寸
for i in range(epoch):
    #误差的统计和测算
    forward_propagation(input_train)
    error_train = get_error(correct_train,n_train)
    forward_propagation(input_test)
    error_test = get_error(correct_test,n_test)
    #误差的记录
    test_error_x.append(i)
    test_error_y.append(error_test)
    train_error_x.append(i)
    train_error_y.append(error_train)
    #进度的显示
    if i%interval==0:
        print("Epoch:"+str(i)+"/"+str(epoch),"Error_train:"+str(error_train),"Error_test:"+str(error_test))
    
    #学习
    index_random = np.arange(n_train)  #打乱索引值 这里也是基于随机梯度下降法
    np.random.shuffle(index_random)
    for j in range(n_batch):
        #取出最小批次
        mb_index= index_random[j*batch_size:(j+1)*batch_size]
        x = input_train[mb_index,:]
        t = correct_train[mb_index,:]
        
        forward_propagation(x)
        backward_propagation(t)
        
        update_wb()
        
    plt.plot(train_error_x,train_error_y,label="Train")
    plt.plot(test_error_x,test_error_y,label="Test")
    plt.legend() #设置图例

    plt.xlabel("Epoches")
    plt.ylabel("Error")

    plt.show()

# 计算正确率
forward_propagation(input_train)
count_train = np.sum(np.argmax(output_layer.y,axis=1) == np.argmax(correct_train,axis=1))

forward_propagation(input_test)
count_test = np.sum(np.argmax(output_layer.y,axis=1) == np.argmax(correct_test,axis=1))
print("Accuracy Train:"+str(count_train/n_train*100)+"%","Accuracy Test:"+str(count_test/n_test*100)+"%")
(调整epoch)运行代码后观察图像,显然出现了过拟合的问题
 
应对过拟合:
四种尝试比较的策略
(1)随机梯度下降法
(2)AdaGrad算法
(3)随机梯度下降法+Dropout机制
(4)AdaGrad算法+Dropout机制
 
A.AdaGrad算法的实现——修改BaseLayer
class BaseLayer:
    def __init__(self,n_upper,n):
        self.w = wb_width * np.random.randn(n_upper,n)
        self.b = wb_width * np.random.randn(n)
        
        self.h_w = np.zeros((n_upper,n))+1e-8
        self.h_b = np.zeros(n)+1e-8
        
    def update(self,eta):
        self.h_w += self.grad_w*self.grad_w
        self.h_b += self.grad_b*self.grad_b
        
        self.w -= eta/np.sqrt(self.h_w)*self.grad_w
        self.b -= eta/np.sqrt(self.h_b)*self.grad_b
        
B.Dropout的实现
#封装一个Dropout层 放在每一个中间层后面
class Dropout:
    def __init__(self,dropout_ratio):
         self.dropout_ratio = dropout_ratio
    
    def forward(self,x,is_train):
        if is_train: 
            #学习时,创建与输入具有相同形状的随机数矩阵
            rand = np.random.rand(*x.shape)
            #将对应的神经元设置为有效1,无效0,保存在self.dropout矩阵中
            self.dropout = np.where(rand>self.dropout_ratio,1,0) 
            self.y = x*self.dropout #将上层网络中的神经元随机设置成无效的
        else:
            self.y = (1-self.dropout_ratio)*x #使输出下降
            
    def backward(self,grad_y):
        self.grad_x = grad_y*self.dropout #确保不会对无效的神经元进行反向传播处理
        
   ##中间层加入时这样做:
   dropout_1 = Dropout(0.5)   
   
   ##正向传播中
   dropout_1.forward(middle_layer_1,is_train) 
   middle_layer_2.forward(dropout_1.y) 
   
   ##反向传播中
   dropout_2.backward(output_layer.grad_x) 
   middle_layer_2.backward(dropout_2.grad_x)