基礎的理論知識參考:https://www.zybuluo.com/hanbingtao/note/485480
下面的代碼也是基于上面文章的實現(xiàn):
整個算法分為三個步驟:
前向計算每個神經元的輸出值ajaj(jj表示網絡的第jj個神經元,以下同);反向計算每個神經元的誤差項δjδj,δjδj在有的文獻中也叫做敏感度(sensitivity)。它實際上是EdEd網絡的損失函數(shù)對神經元netjnetj加權輸入的偏導數(shù),即δj=?Ed?netjδj=?Ed?netj;計算每個神經元連接權重wjiwji的梯度(wjiwji表示從神經元連接到神經元jj的權重),公式為?Ed?wji=aiδj?Ed?wji=aiδj,其中aiai,表示神經元ii<script type="math/tex" id="MathJax-Element-14">i</script>的輸出。
最后,根據梯度下降法則更新每個權重即可。
具體的細節(jié)參考上面的連接文章,這里只貼出代碼實現(xiàn):
import numpy
as np
class ReluActivator(object):def forward(self, weighted_input):return max(
0, weighted_input)
def backward(self, output):return 1 if output >
0 else 0class IdentityActivator(object):def forward(self, weighted_input):return weighted_input
def backward(self, output):return 1class SigmoidActivator(object):def forward(self, weighted_input):return 1.0 / (
1.0 + np.exp(-weighted_input))
def backward(self, output):return output * (
1 - output)
class TanhActivator(object):def forward(self, weighted_input):return 2.0 / (
1.0 + np.exp(-
2 * weighted_input)) -
1.0def backward(self, output):return 1 - output * output
是一些基本的激活函數(shù)的實現(xiàn)
- 下面的CNN.py文件實現(xiàn)cnn網絡主要的組件
import numpy
as np
from activators
import ReluActivator, IdentityActivator
def get_patch(input_array, i, j, filter_width,filter_height, stride):'''從輸入數(shù)組中獲取本次卷積的區(qū)域,自動適配輸入為2D和3D的情況'''start_i = i * stridestart_j = j * stride
if input_array.ndim ==
2:
return input_array[ start_i : start_i + filter_height, start_j : start_j + filter_width]
elif input_array.ndim ==
3:
return input_array[:, start_i : start_i + filter_height, start_j : start_j + filter_width]
def get_max_index(array):max_i =
0max_j =
0max_value = array[
0,
0]
for i
in range(array.shape[
0]):
for j
in range(array.shape[
1]):
if array[i,j] > max_value:max_value = array[i,j]max_i, max_j = i, j
return max_i, max_j
def conv(input_array,kernel_array,output_array,stride, bias):'''計算卷積,自動適配輸入為2D和3D的情況,是在get_patch函數(shù)中判斷的'''channel_number = input_array.ndim output_width = output_array.shape[
1]output_height = output_array.shape[
0]kernel_width = kernel_array.shape[-
1]kernel_height = kernel_array.shape[-
2]
for i
in range(output_height):
for j
in range(output_width):output_array[i][j] = (get_patch(input_array, i, j, kernel_width, kernel_height, stride) * kernel_array).sum() + bias
def padding(input_array, zp): '''為數(shù)組增加Zero padding,自動適配輸入為2D和3D的情況'''if zp ==
0:
return input_array
else:
if input_array.ndim ==
3:input_width = input_array.shape[
2]input_height = input_array.shape[
1]input_depth = input_array.shape[
0]padded_array = np.zeros((input_depth, input_height +
2 * zp,input_width +
2 * zp))padded_array[:,zp : zp + input_height,zp : zp + input_width] = input_array
return padded_array
elif input_array.ndim ==
2:input_width = input_array.shape[
1]input_height = input_array.shape[
0]padded_array = np.zeros((input_height +
2 * zp,input_width +
2 * zp))padded_array[zp : zp + input_height,zp : zp + input_width] = input_array
return padded_array
def element_wise_op(array, op):for i
in np.nditer(array,op_flags=[
'readwrite']):i[...] = op(i)
class Filter(object):def __init__(self, width, height, depth):self.weights = np.random.uniform(-
1e-4,
1e-4,(depth, height, width))self.bias =
0self.weights_grad = np.zeros(self.weights.shape)self.bias_grad =
0def __repr__(self):return 'filter weights:\n%s\nbias:\n%s' % (repr(self.weights), repr(self.bias))
def get_weights(self):return self.weights
def get_bias(self):return self.bias
def update(self, learning_rate):self.weights -= learning_rate * self.weights_gradself.bias -= learning_rate * self.bias_grad
class ConvLayer(object):def __init__(self, input_width, input_height, channel_number, filter_width, filter_height, filter_number, zero_padding, stride, activator,learning_rate):self.input_width = input_widthself.input_height = input_heightself.channel_number = channel_numberself.filter_width = filter_widthself.filter_height = filter_heightself.filter_number = filter_numberself.zero_padding = zero_paddingself.stride = strideself.activator = activatorself.learning_rate = learning_rateself.output_width = ConvLayer.calculate_output_size(self.input_width, filter_width, zero_padding,stride)self.output_height = ConvLayer.calculate_output_size(self.input_height, filter_height, zero_padding,stride)self.output_array = np.zeros((self.filter_number, self.output_height, self.output_width))self.filters = []
for i
in range(filter_number):self.filters.append(Filter(filter_width,filter_height, self.channel_number))
@staticmethoddef calculate_output_size(input_size,filter_size, zero_padding, stride):return (input_size - filter_size +
2 * zero_padding) / stride +
1def forward(self, input_array):'''計算卷積層的輸出輸出結果保存在self.output_array'''self.input_array = input_arrayself.padded_input_array = padding(input_array,self.zero_padding)
for f
in range(self.filter_number):filter = self.filters[f]conv(self.padded_input_array, filter.get_weights(), self.output_array[f],self.stride, filter.get_bias())element_wise_op(self.output_array,self.activator.forward)
def backward(self, input_array, sensitivity_array, activator):'''計算傳遞給前一層的誤差項,以及計算每個權重的梯度前一層的誤差項保存在:self.delta_array梯度保存在:Filter對象的weights_grad'''self.forward(input_array)self.bp_sensitivity_map(sensitivity_array,activator)self.bp_gradient(sensitivity_array)
def update(self):'''按照梯度下降,更新權重'''for filter
in self.filters:filter.update(self.learning_rate)
def bp_sensitivity_map(self, sensitivity_array,activator):'''計算傳遞到上一層的sensitivity mapsensitivity_array: 本層的sensitivity mapactivator: 上一層的激活函數(shù)'''expanded_array = self.expand_sensitivity_map(sensitivity_array)expanded_width = expanded_array.shape[
2]zp = (self.input_width + self.filter_width -
1 - expanded_width) /
2padded_array = padding(expanded_array, zp)
print 'padded_array:',np.shape(padded_array)self.delta_array = self.create_delta_array()
for f
in range(self.filter_number):filter = self.filters[f]flipped_weights = np.array(map(
lambda i: np.rot90(i,
2),filter.get_weights()))
print 'flipped_weights:',np.shape(flipped_weights)delta_array = self.create_delta_array()
for d
in range(delta_array.shape[
0]):conv(padded_array[f], flipped_weights[d],delta_array[d],
1,
0)self.delta_array += delta_arrayderivative_array = np.array(self.input_array)element_wise_op(derivative_array,activator.backward)self.delta_array *= derivative_array
def bp_gradient(self, sensitivity_array):expanded_array = self.expand_sensitivity_map(sensitivity_array)
for f
in range(self.filter_number):filter = self.filters[f]
for d
in range(filter.weights.shape[
0]):conv(self.padded_input_array[d],expanded_array[f],filter.weights_grad[d],
1,
0)filter.bias_grad = expanded_array[f].sum()
def expand_sensitivity_map(self, sensitivity_array):print 'sensitivity_array:\n',sensitivity_arraydepth = sensitivity_array.shape[
0]expanded_width = (self.input_width - self.filter_width +
2 * self.zero_padding +
1)expanded_height = (self.input_height - self.filter_height +
2 * self.zero_padding +
1)expand_array = np.zeros((depth, expanded_height, expanded_width))
for i
in range(self.output_height):
for j
in range(self.output_width):i_pos = i * self.stridej_pos = j * self.strideexpand_array[:,i_pos,j_pos] = sensitivity_array[:,i,j]
print 'expand_array:\n',expand_array
return expand_array
def create_delta_array(self):return np.zeros((self.channel_number,self.input_height, self.input_width))
class MaxPoolingLayer(object):def __init__(self, input_width, input_height, channel_number, filter_width, filter_height, stride):self.input_width = input_widthself.input_height = input_heightself.channel_number = channel_numberself.filter_width = filter_widthself.filter_height = filter_heightself.stride = strideself.output_width = (input_width - filter_width) / self.stride +
1self.output_height = (input_height -filter_height) / self.stride +
1self.output_array = np.zeros((self.channel_number,self.output_height, self.output_width))
def forward(self, input_array):for d
in range(self.channel_number):
for i
in range(self.output_height):
for j
in range(self.output_width):self.output_array[d,i,j] = ( get_patch(input_array[d], i, j,self.filter_width, self.filter_height, self.stride).max())
def backward(self, input_array, sensitivity_array):self.delta_array = np.zeros(input_array.shape)
for d
in range(self.channel_number):
for i
in range(self.output_height):
for j
in range(self.output_width):patch_array = get_patch(input_array[d], i, j,self.filter_width, self.filter_height, self.stride)k, l = get_max_index(patch_array)self.delta_array[d, i * self.stride + k, j * self.stride + l] = \sensitivity_array[d,i,j]
def init_test():a = np.array([[[
0,
1,
1,
0,
2],[
2,
2,
2,
2,
1],[
1,
0,
0,
2,
0],[
0,
1,
1,
0,
0],[
1,
2,
0,
0,
2]],[[
1,
0,
2,
2,
0],[
0,
0,
0,
2,
0],[
1,
2,
1,
2,
1],[
1,
0,
0,
0,
0],[
1,
2,
1,
1,
1]],[[
2,
1,
2,
0,
0],[
1,
0,
0,
1,
0],[
0,
2,
1,
0,
1],[
0,
1,
2,
2,
2],[
2,
1,
0,
0,
1]]])b = np.array([[[
0,
1,
1],[
2,
2,
2],[
1,
0,
0]],[[
1,
0,
2],[
0,
0,
0],[
1,
2,
1]]])cl = ConvLayer(
5,
5,
3,
3,
3,
2,
1,
2,IdentityActivator(),
0.001)cl.filters[
0].weights = np.array([[[-
1,
1,
0],[
0,
1,
0],[
0,
1,
1]],[[-
1,-
1,
0],[
0,
0,
0],[
0,-
1,
0]],[[
0,
0,-
1],[
0,
1,
0],[
1,-
1,-
1]]], dtype=np.float64)cl.filters[
0].bias=
1cl.filters[
1].weights = np.array([[[
1,
1,-
1],[-
1,-
1,
1],[
0,-
1,
1]],[[
0,
1,
0],[-
1,
0,-
1],[-
1,
1,
0]],[[-
1,
0,
0],[-
1,
0,
1],[-
1,
0,
0]]], dtype=np.float64)cl.filters[
1].bias=
1 return a, b, cl
def test():a, b, cl = init_test()cl.forward(a)
print 'cl.output_array:\n',cl.output_array
def test_bp():a, b, cl = init_test()cl.backward(a, b, IdentityActivator())cl.update()
print 'cl.filters[0]:\n',cl.filters[
0]
print 'cl.filters[1]:\n',cl.filters[
1]
def init_pool_test():a = np.array([[[
1,
1,
2,
4],[
5,
6,
7,
8],[
3,
2,
1,
0],[
1,
2,
3,
4]],[[
0,
1,
2,
3],[
4,
5,
6,
7],[
8,
9,
0,
1],[
3,
4,
5,
6]]], dtype=np.float64)b = np.array([[[
1,
2],[
2,
4]],[[
3,
5],[
8,
2]]], dtype=np.float64)mpl = MaxPoolingLayer(
4,
4,
2,
2,
2,
2)
return a, b, mpl
def test_pool():a, b, mpl = init_pool_test()mpl.forward(a)
print 'input array:\n%s\noutput array:\n%s' % (a,mpl.output_array)
def test_pool_bp():a, b, mpl = init_pool_test()mpl.backward(a, b)
print 'input array:\n%s\nsensitivity array:\n%s\ndelta array:\n%s' % (a, b, mpl.delta_array)
if __name__ ==
'__main__':test()test_pool()test_bp()
print '................................................'test_pool_bp()
'''a = np.arange(6).reshape(2, 3) print a for x in np.nditer(a, op_flags = ['readwrite']): x[...] = 2*x print a '''
一些基本得運行結果:
cl.output_array:
[[[ 6. 7. 5.][ 3. -1. -1.][ 2. -1. 4.]][[ 3. -4. -7.][ 2. -3. -3.][ 1. -4. -4.]]]
input array:
[[[ 1. 1. 2. 4.][ 5. 6. 7. 8.][ 3. 2. 1. 0.][ 1. 2. 3. 4.]][[ 0. 1. 2. 3.][ 4. 5. 6. 7.][ 8. 9. 0. 1.][ 3. 4. 5. 6.]]]
output array:
[[[ 6. 8.][ 3. 4.]][[ 5. 7.][ 9. 6.]]]
sensitivity_array:
[[[0 1 1][2 2 2][1 0 0]][[1 0 2][0 0 0][1 2 1]]]
expand_array:
[[[ 0. 0. 1. 0. 1.][ 0. 0. 0. 0. 0.][ 2. 0. 2. 0. 2.][ 0. 0. 0. 0. 0.][ 1. 0. 0. 0. 0.]][[ 1. 0. 0. 0. 2.][ 0. 0. 0. 0. 0.][ 0. 0. 0. 0. 0.][ 0. 0. 0. 0. 0.][ 1. 0. 2. 0. 1.]]]
padded_array: (
2L,
7L,
7L)
flipped_weights: (
3L,
3L,
3L)
flipped_weights: (
3L,
3L,
3L)
sensitivity_array:
[[[0 1 1][2 2 2][1 0 0]][[1 0 2][0 0 0][1 2 1]]]
expand_array:
[[[ 0. 0. 1. 0. 1.][ 0. 0. 0. 0. 0.][ 2. 0. 2. 0. 2.][ 0. 0. 0. 0. 0.][ 1. 0. 0. 0. 0.]][[ 1. 0. 0. 0. 2.][ 0. 0. 0. 0. 0.][ 0. 0. 0. 0. 0.][ 0. 0. 0. 0. 0.][ 1. 0. 2. 0. 1.]]]
cl.filters[
0]:
filter weights:
array(
[[[-1.008, 0.99 , -0.009],[-0.005, 0.994, -0.006],[-0.006, 0.995, 0.996]],
[[-1.004, -1.001, -0.004],[-0.01 , -0.009, -0.012],[-0.002, -1.002, -0.002]],
[[-0.002, -0.002, -1.003],[-0.005, 0.992, -0.005],[ 0.993, -1.008, -1.007]]])
bias:
0.99099999999999999
cl.filters[
1]:
filter weights:
array(
[[[ 9.98000000e-01, 9.98000000e-01, -1.00100000e+00],[ -1.00400000e+00, -1.00700000e+00, 9.97000000e-01],[ -4.00000000e-03, -1.00400000e+00, 9.98000000e-01]],
[[ 0.00000000e+00, 9.99000000e-01, 0.00000000e+00],[ -1.00900000e+00, -5.00000000e-03, -1.00400000e+00],[ -1.00400000e+00, 1.00000000e+00, 0.00000000e+00]],
[[ -1.00400000e+00, -6.00000000e-03, -5.00000000e-03],[ -1.00200000e+00, -5.00000000e-03, 9.98000000e-01],[ -1.00200000e+00, -1.00000000e-03, 0.00000000e+00]]])
bias:
0.99299999999999999
................................................
input array:
[[[ 1. 1. 2. 4.][ 5. 6. 7. 8.][ 3. 2. 1. 0.][ 1. 2. 3. 4.]][[ 0. 1. 2. 3.][ 4. 5. 6. 7.][ 8. 9. 0. 1.][ 3. 4. 5. 6.]]]
sensitivity array:
[[[ 1. 2.][ 2. 4.]][[ 3. 5.][ 8. 2.]]]
delta array:
[[[ 0. 0. 0. 0.][ 0. 1. 0. 2.][ 2. 0. 0. 0.][ 0. 0. 0. 4.]][[ 0. 0. 0. 0.][ 0. 3. 0. 5.][ 0. 8. 0. 0.][ 0. 0. 0. 2.]]]
全連接層的實現(xiàn)和上一篇文章類似,在此就不再贅述了。至此,你已經擁有了實現(xiàn)了一個簡單的卷積神經網絡所需要的基本組件,并沒有完全實現(xiàn)一個CNN網絡。
對于卷積神經網絡,現(xiàn)在有很多優(yōu)秀的開源實現(xiàn),因此我們并不需要真的自己去實現(xiàn)一個。這里貼出這些代碼能讓我們更深的理解卷積神經網絡的原理,僅供參考學習。
總結
以上是生活随笔為你收集整理的python 面向对象实现CNN(四)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。