文章列表
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