神经网络已经在各行各业都得到了普遍的应用,然而在工程落地以及一些验证过程中有必要自己实现一些常用的模块,比如卷积,池化,批量归一化等常规的神经网络模块。尽管现在都是各种调库的天下,自己在做相关工程发现了解和实现这些基本模块还是很有必要的,不仅加深自己的理解,更加容易工程落地。神经网络的基础这里就不过多介绍,直接上实现代码。
卷积实现:输入数据排布[N,chin,dim_inh,dim_inw],权重的排布[chout,chin,kernelh,kernelw],输出数据排布[N,chout,dim_outh,dim_outw],实现程序也给出了输入数据,权重,输出数据都为二维数据的情形。x:[N,chin * dim_inh * dim_inw],w:[chout,chin * kernelh * kernelw],result:[N,chout * dimouth * dimoutw]。实现示意图如下:
#四维实现
class predict_cnn(nn.Module):
def __init__(self, stride=1, padding=0):
super(predict_cnn, self).__init__()
self.padding = padding
self.stride = stride
def forward(self, x, w, b):
w = w.detach().numpy()
b = b.detach().numpy()
ch_im_in = w.shape[1]
ch_im_out = w.shape[0]
kernel = w.shape[2]
dim_im_in_y = x.shape[2]
dim_im_in_x = x.shape[3]
dim_im_out_y = int((dim_im_in_y - kernel + 2 * self.padding) / self.stride) + 1
dim_im_out_x = int((dim_im_in_x - kernel + 2 * self.padding) / self.stride) + 1
result = np.zeros((ch_im_out, dim_im_out_y, dim_im_out_x))
for co in range(ch_im_out):
for oy in range(dim_im_out_y):
for ox in range(dim_im_out_x):
conv_out = b[co].copy()
for m in range(kernel):
for n in range(kernel):
row = self.stride * oy + m - self.padding
col = self.stride * ox + n - self.padding
if row < 0 or row >= dim_im_in_y or col < 0 or col >= dim_im_in_x:
pass
else:
for ci in range(ch_im_in):
conv_out += x[0, ci, row, col] * w[co, ci, m, n]
result[co][oy][ox] = conv_out
return result
#二维实现
class predict_cnn_dim2(nn.Module):
def __init__(self, stride=1, padding=0):
super(predict_cnn_dim2, self).__init__()
self.padding = padding
self.stride = stride
def forward(self, x, w, b):
w = w.detach().numpy()
b = b.detach().numpy()
ch_im_in = w.shape[1]
ch_im_out = w.shape[0]
kernel = w.shape[2]
dim_im_in_y = x.shape[2]
dim_im_in_x = x.shape[3]
dim_im_out_y = int((dim_im_in_y - kernel + 2 * self.padding) / self.stride) + 1
dim_im_out_x = int((dim_im_in_x - kernel + 2 * self.padding) / self.stride) + 1
result = np.zeros((ch_im_out, dim_im_out_y * dim_im_out_x))
w = w.reshape(w.shape[0],-1)
x = x.squeeze(0)
x = x.reshape(x.shape[0],-1)
for co in range(ch_im_out):
for oy in range(dim_im_out_y):
for ox in range(dim_im_out_x):
conv_out = b[co].copy()
co_idx = oy * dim_im_out_x + ox
for m in range(kernel):
for n in range(kernel):
row = self.stride * oy + m - self.padding
col = self.stride * ox + n - self.padding
ci_idx = row * dim_im_in_x + col
if row < 0 or row >= dim_im_in_y or col < 0 or col >= dim_im_in_x:
pass
else:
for ci in range(ch_im_in):
w_idx = ci * kernel * kernel + m * kernel + n
conv_out += x[ci, ci_idx] * w[co, w_idx]
result[co,co_idx] = conv_out
return result
通道卷积:实现示意图如下
可以看到通道卷积要求输入数据的通道和输出通道数一致,权重维度:[chout,1,kernelh,kernelw],输入数据:[N,chin,dim_inh,dim_inw],输出数据:[N,chout,dim_outh,dim_outw],此时要求chin = chout,且在前向推理一般N=1。这里代码实现主要是针对前向推理,当N=1时,将输入数据的维度0,1转换一下,此时的通道卷积就等价于常规卷积,即相当于输入数据的通道chin = 1,样本N原来的通道数。具体代码实现如下:
#四维实现
class dpw_cnn(nn.Module):
def __init__(self, stride=1, padding=0):
super(dpw_cnn, self).__init__()
self.padding = padding
self.stride = stride
def forward(self, x, w, b):
w = w.detach().numpy()
b = b.detach().numpy()
ch_im_in = w.shape[1]
ch_im_out = w.shape[0]
kernel = w.shape[2]
dim_im_in_y = x.shape[2]
dim_im_in_x = x.shape[3]
dim_im_out_y = int((dim_im_in_y - kernel + 2 * self.padding) / self.stride) + 1
dim_im_out_x = int((dim_im_in_x - kernel + 2 * self.padding) / self.stride) + 1
result = np.zeros((ch_im_out, dim_im_out_y, dim_im_out_x))
for co in range(ch_im_out):
for oy in range(dim_im_out_y):
for ox in range(dim_im_out_x):
conv_out = b[co].copy()
for m in range(kernel):
for n in range(kernel):
row = self.stride * oy + m - self.padding
col = self.stride * ox + n - self.padding
if row < 0 or row >= dim_im_in_y or col < 0 or col >= dim_im_in_x:
pass
else:
for ci in range(ch_im_in):
conv_out += x[0, co, row, col] * w[co, ci, m, n]
result[co][oy][ox] = conv_out
return result
#二维实现
class dpw_cnn_dim2(nn.Module):
def __init__(self, stride=1, padding=0):
super(dpw_cnn_dim2, self).__init__()
self.padding = padding
self.stride = stride
def forward(self, x, w, b):
w = w.detach().numpy()
b = b.detach().numpy()
ch_im_in = w.shape[1]
ch_im_out = w.shape[0]
kernel = w.shape[2]
dim_im_in_y = x.shape[2]
dim_im_in_x = x.shape[3]
dim_im_out_y = int((dim_im_in_y - kernel + 2 * self.padding) / self.stride) + 1
dim_im_out_x = int((dim_im_in_x - kernel + 2 * self.padding) / self.stride) + 1
result = np.zeros((ch_im_out, dim_im_out_y * dim_im_out_x))
w = w.reshape(w.shape[0],-1)
x = x.squeeze()
x = x.reshape(x.shape[0],-1)
for co in range(ch_im_out):
for oy in range(dim_im_out_y):
for ox in range(dim_im_out_x):
conv_out = b[co].copy()
co_idx = oy * dim_im_out_x + ox
for m in range(kernel):
for n in range(kernel):
row = self.stride * oy + m - self.padding
col = self.stride * ox + n - self.padding
ci_idx = row * dim_im_in_x + col
if row < 0 or row >= dim_im_in_y or col < 0 or col >= dim_im_in_x:
pass
else:
for ci in range(ch_im_in):
w_idx = ci * kernel * kernel + m * kernel + n
conv_out += x[co, ci_idx] * w[co, w_idx]
result[co,co_idx] = conv_out
return result
全连接层:全连接最为直观,也很容易理解,实质就是两个矩阵相乘。实现过程如下:
class predict_fc(nn.Module):
def __init__(self):
super(predict_fc, self).__init__()
def forward(self, x, w, b):
w = w.detach().numpy()
b = b.detach().numpy()
# x = x.numpy()
x = np.matmul(w, x.T).squeeze() + b
return x
后续会进一步的讲解神经网络相关基础实现,这里只是做个铺垫,bn层在后续会讲解。希望可以帮助到大家,不当之处请指教,谢谢!
这里附上一个例子:python卷积,通道卷积实现