自己编写深度学习框架ANNbox【高仿tensorflow】__04卷积神经网络编写:实现AlexNet

文章列表
1.自己编写深度学习框架ANNbox【高仿tensorflow】__01实现全连接.
2.自己编写深度学习框架ANNbox【高仿tensorflow】__02实现不同的优化方法.
3.自己编写深度学习框架ANNbox【高仿tensorflow】__03文本情感分析.
4.自己编写深度学习框架ANNbox【高仿tensorflow】__04卷积神经网络编写:实现AlexNet.
5.自己编写深度学习框架ANNbox【高仿tensorflow】__05循环神经网络编写:实现RNN(01).

自己编写神经网络库ANNbox【高仿tensorflow】__04卷积神经网络编写:实现AlexNet

在前面几节的基础上,要实现卷积神经网络,主要工作就只剩下conv2d类、bias_add类、max_pool类、mean_pool类及droupout类,下面我们来依次实现。本文中所有公式推导请详参深度学习基础模型算法原理及编程实现–03.卷积神经网络,全文同】。【明天就要到单位报道了,这两天急急忙忙把conv2d和bias_add类实现了,max_pool类、mean_pool类及droupout类会在后面补上,不过我用只有卷积层和全连接层的简化版AlexNet实现了MNIST数据分类,效果还不错,只要conv2d类实现了,max_pool类、mean_pool类的实现就比较简单了】

4.1 conv2d类

首先是线性加权输入类,主要实现下式的运算操作【[公式中参数详细的说明请参考深度学习基础模型算法原理及编程实现–03.卷积神经网络][],全文同】

4.1.1 对于padding=’same’的模式,有

这里写图片描述

4.1.2 对于padding=’ valid’的模式,有

这里写图片描述

4.1.3 对应的编程实现如下:

4.1.3.1 主函数

class conv2d(Node):
    '''二维卷积类,实现对矩阵的卷积操作'''
    def __init__(self,X,conv_weights,strides=[1,1,1,1],padding='SAME',name=[]):
        Node.__init__(self, inbound_nodes=[X,conv_weights], name=name)
        self.strides=strides
        self.padding=padding

    def forward(self):
        self.kF_KH_for_zerospadding=self.inbound_nodes[1].value.shape[0:2]
        self.imagesize=self.inbound_nodes[0].value.shape
        image=self.inbound_nodes[0].value
        kernal=self.inbound_nodes[1].value
        if(self.padding=='SAME'):
            image_after_zerospadding = bs.zerospadding(image,kernal_size=[kernal.shape[0],kernal.shape[1]],mode=0)
        if(self.padding=='VALID'):
            image_after_zerospadding = bs.zerospadding(image,kernal_size=[kernal.shape[0],kernal.shape[1]],mode=2)

        conv_origin_size_label_one_zero=np.zeros((image.shape[0],image_after_zerospadding.shape[1]-kernal.shape[0]+1,image_after_zerospadding.shape[2]-kernal.shape[1]+1,kernal.shape[-1]))
        conv_out_in_origial_size=np.zeros_like(conv_origin_size_label_one_zero)
        conv_out=np.zeros((image.shape[0],int((image_after_zerospadding.shape[1]-kernal.shape[0])/self.strides[1])+1,int((image_after_zerospadding.shape[2]-kernal.shape[1])/self.strides[2])+1,kernal.shape[-1]))
        ind_i=0
        kernel_hsize=conv_origin_size_label_one_zero.shape[1]
        kernel_wsize=conv_origin_size_label_one_zero.shape[2]
        for i in range(0,kernel_hsize,self.strides[1]):
            i=kernel_hsize-1-i
            ind_i=int(i/self.strides[1])
            for j in range(0,kernel_wsize,self.strides[2]):
                j=kernel_wsize-1-j
                ind_j=int(j/self.strides[2])
                for patch in range(image.shape[0]):
                    for channel in range(kernal.shape[-1]):
                        conv_out[patch,ind_i,ind_j,channel]=np.sum(image_after_zerospadding[patch,i:i+kernal.shape[0],j:j+kernal.shape[1],:]*kernal[:,:,:,channel])
                        conv_origin_size_label_one_zero[patch,i,j,channel]=1
                        conv_out_in_origial_size[patch,i,j,channel]=conv_out[patch,ind_i,ind_j,channel]
        self.value=conv_out
        self.conv_origin_size_label_one_zero=conv_origin_size_label_one_zero
        self.conv_out_in_origial_size=conv_out_in_origial_size
        self.image_after_zerospadding=image_after_zerospadding

    def backward(self):
        rw.SAFE_FWRITE(IsWriteDebug, fout_Debug_miniflow,"后向计算:\n")
        self.gradients = {n: np.zeros_like(n.value) for n in self.inbound_nodes}
        kernal=self.inbound_nodes[1].value
        weight_gradient=np.zeros_like(kernal).transpose(2,0,1,3)
        image_after_zerospadding_transpose=self.image_after_zerospadding.transpose(3,1,2,0)
        conv_origin_size_label_one_zero_transpoes=self.conv_origin_size_label_one_zero.transpose(1,2,0,3)
        for n in self.outbound_nodes:
            grad_cost = n.gradients[self] if(self in n.gradients.keys()) else np.ones_like(self.value)
            grad_cost_transpose_to_original_size=np.zeros_like(self.conv_origin_size_label_one_zero)
            kind=0
            for ki in range(grad_cost_transpose_to_original_size.shape[1]):
                kjnd=0
                for kj in range(grad_cost_transpose_to_original_size.shape[2]):
                    if(self.conv_origin_size_label_one_zero[0,ki,kj,0]!=0):
                        grad_cost_transpose_to_original_size[:,ki,kj,:]=grad_cost[:,kind,kjnd,:]
                        kjnd+=1
                        if(kjnd%grad_cost.shape[1]==0):
                            kjnd=0
                            kind+=1
            grad_cost_transpose=grad_cost_transpose_to_original_size.transpose(1,2,0,3)

            if(self.padding=='SAME'):
                delta_padding=bs.zerospadding(self.conv_origin_size_label_one_zero*grad_cost_transpose_to_original_size,kernal_size=[kernal.shape[0],kernal.shape[1]],mode=1)
            if(self.padding=='VALID'):
                delta_padding=bs.zerospadding(self.conv_origin_size_label_one_zero*grad_cost_transpose_to_original_size,kernal_size=[kernal.shape[0],kernal.shape[1]],mode=3)

            image_gradient=np.zeros_like(self.inbound_nodes[0].value)
            kernal180=bs.mat_rotat90(kernal,2)
            kernal180_transpose=kernal180.transpose(0,1,3,2)
            for i in range(image_gradient.shape[1]):
                for j in range(image_gradient.shape[2]):
                    for batchsize in range(delta_padding.shape[0]):
                        for dk in range(kernal180_transpose.shape[-1]):
                            image_gradient[batchsize,i,j,dk]=np.sum(delta_padding[batchsize,i:i+kernal180_transpose.shape[0],j:j+kernal180_transpose.shape[1],:]*kernal180_transpose[:,:,:,dk])

            self.gradients[self.inbound_nodes[0]] += image_gradient

            for i in range(weight_gradient.shape[1]):
                for j in range(weight_gradient.shape[2]):
                    for dk in range(image_after_zerospadding_transpose.shape[0]):
                        for dk_1 in range(conv_origin_size_label_one_zero_transpoes.shape[-1]):
                            weight_gradient[dk,i,j,dk_1]=np.sum(\
                                           image_after_zerospadding_transpose[dk,i:i+conv_origin_size_label_one_zero_transpoes.shape[0],j:j+conv_origin_size_label_one_zero_transpoes.shape[1],:]*\
                                           (conv_origin_size_label_one_zero_transpoes[:,:,:,dk_1]*grad_cost_transpose[:,:,:,dk_1]))
                            '''上式中最后一个参数conv_origin_size_label_one_zero 还需要乘上 梯度项'''
            self.gradients[self.inbound_nodes[1]] += weight_gradient.transpose(1,2,0,3)

4.1.3.2 辅助函数

## 矩阵旋转
def mat_rotat90(filter_, i): #将矩阵input_array旋转i次
    temp = np.zeros_like(filter_)
    for ind1 in range(filter_.shape[-1]):
        for ind2 in range(filter_.shape[-2]):
            temp2 = filter_[:,:,ind2,ind1].tolist()
            for _ in range(i):
                temp2 = list(map(list,zip(*temp2[::-1])))
            temp[:,:,ind2,ind1] = np.array(temp2)
    return temp

#补0
def zerospadding(input_,kernal_size=[0,0],mode=1):
    if(mode==0):
        output_size_h=input_.shape[1]+kernal_size[0]-1
        output_size_w=input_.shape[2]+kernal_size[1]-1
        err_height=kernal_size[0]-1
        zero_before_height=int(err_height/2)
        zero_after_height=err_height-zero_before_height
        err_width=kernal_size[1]-1
        zero_before_width=int(err_width/2)
        zero_after_width=err_width-zero_before_width
        output_=np.zeros((input_.shape[0],output_size_h,output_size_w,input_.shape[3]))
        if(zero_after_height!=0 and zero_after_width!=0):
            output_[:,zero_before_height:-zero_after_height,zero_before_width:-zero_after_width,:] = input_
        elif(zero_after_height==0 and zero_after_width!=0):
            output_[:,zero_before_height:,zero_before_width:-zero_after_width,:] = input_
        elif(zero_after_height!=0 and zero_after_width==0):
            output_[:,zero_before_height:-zero_after_height,zero_before_width:,:] = input_
        elif(zero_after_height==0 and zero_after_width==0):
            output_[:,zero_before_height:,zero_before_width:,:] = input_
        return output_
    elif(mode==1):
        output_size_h=input_.shape[1]+kernal_size[0]-1
        output_size_w=input_.shape[2]+kernal_size[1]-1
        err_height=kernal_size[0]-1
        zero_after_height=int(err_height/2)
        zero_before_height=err_height-zero_after_height
        err_width=kernal_size[1]-1
        zero_after_width=int(err_width/2)
        zero_before_width=err_width-zero_after_width
        output_=np.zeros((input_.shape[0],output_size_h,output_size_w,input_.shape[3]))
        if(zero_after_height!=0 and zero_after_width!=0):
            output_[:,zero_before_height:-zero_after_height,zero_before_width:-zero_after_width,:] = input_
        elif(zero_after_height==0 and zero_after_width!=0):
            output_[:,zero_before_height:,zero_before_width:-zero_after_width,:] = input_
        elif(zero_after_height!=0 and zero_after_width==0):
            output_[:,zero_before_height:-zero_after_height,zero_before_width:,:] = input_
        elif(zero_after_height==0 and zero_after_width==0):
            output_[:,zero_before_height:,zero_before_width:,:] = input_
        return output_
    elif(mode==2):
        output_size_h=input_.shape[0]
        output_size_w=input_.shape[1]
        output_=input_
        return output_
    elif(mode==3):
        output_size_h=input_.shape[1]+2*(kernal_size[0]-1)
        output_size_w=input_.shape[2]+2*(kernal_size[1]-1)
        zero_before_height=kernal_size[0]-1
        zero_after_height=kernal_size[0]-1
        zero_before_width=kernal_size[1]-1
        zero_after_width=kernal_size[1]-1
        output_=np.zeros((input_.shape[0],output_size_h,output_size_w,input_.shape[3]))
        if(zero_after_height!=0 and zero_after_width!=0):
            output_[:,zero_before_height:-zero_after_height,zero_before_width:-zero_after_width,:] = input_
        elif(zero_after_height==0 and zero_after_width!=0):
            output_[:,zero_before_height:,zero_before_width:-zero_after_width,:] = input_
        elif(zero_after_height!=0 and zero_after_width==0):
            output_[:,zero_before_height:-zero_after_height,zero_before_width:,:] = input_
        elif(zero_after_height==0 and zero_after_width==0):
            output_[:,zero_before_height:,zero_before_width:,:] = input_
        return output_

4.2 bias_add类

这里写图片描述

class bias_add(Node):
    '''卷积输出与偏执项相加'''
    def __init__(self, conv, bias, name=[]):
        Node.__init__(self, inbound_nodes=[conv, bias], name=name)

    def forward(self):
#        self.value=np.zeros_like(self.inbound_nodes[0].value)
#        for i in range(self.inbound_nodes[0].value.shape[-1]):
#            self.value[:,:,:,i]=self.inbound_nodes[0].value[:,:,:,i]+self.inbound_nodes[1].value[i]
        conv = self.inbound_nodes[0].value
        bias = self.inbound_nodes[1].value
        self.value = conv + bias

    def backward(self):
        self.gradients = {n: np.zeros_like(n.value) for n in self.inbound_nodes}
        for n in self.outbound_nodes:
            grad_cost = n.gradients[self]
            self.gradients[self.inbound_nodes[0]] += grad_cost  #axis=0表示对列求和
            self.gradients[self.inbound_nodes[1]] += np.sum(np.sum(np.sum(grad_cost,0),0),0)   

4.3 max_pool类

4.4 mean_pool类

4.5 droupout类

4.6 编程实现

4.6.1 验证算例

这里写图片描述

4.6.3 代码文件

https://pan.baidu.com/s/1QIE0a6NdJOEKO7_pKtNTOQ#list/path=%2F

猜你喜欢

转载自blog.csdn.net/drilistbox/article/details/80962242