1+ #-*- coding: utf-8 -*-
2+
3+ '''
4+ - - - - - -- - - - - - - - - - - - - - - - - - - - - - -
5+ Name - - CNN - Convolution Neural Network For Photo Recognizing
6+ Goal - - Recognize Handing Writting Word Photo
7+ Detail:Total 5 layers neural network
8+ * Convolution layer
9+ * Pooling layer
10+ * Input layer layer of BP
11+ * Hiden layer of BP
12+ * Output layer of BP
13+ Author: Stephen Lee
14+ 15+ Date: 2017.9.20
16+ - - - - - -- - - - - - - - - - - - - - - - - - - - - - -
17+ '''
18+
19+ import numpy as np
20+ import matplotlib .pyplot as plt
21+
22+ class CNN ():
23+
24+ def __init__ (self ,conv1_get ,size_p1 ,bp_num1 ,bp_num2 ,bp_num3 ,rate_w = 0.2 ,rate_t = 0.2 ):
25+ '''
26+ :param conv1_get: [a,c,d],size, number, step of convolution kernel
27+ :param size_p1: pooling size
28+ :param bp_num1: units number of flatten layer
29+ :param bp_num2: units number of hidden layer
30+ :param bp_num3: units number of output layer
31+ :param rate_w: rate of weight learning
32+ :param rate_t: rate of threshold learning
33+ '''
34+ self .num_bp1 = bp_num1
35+ self .num_bp2 = bp_num2
36+ self .num_bp3 = bp_num3
37+ self .conv1 = conv1_get [:2 ]
38+ self .step_conv1 = conv1_get [2 ]
39+ self .size_pooling1 = size_p1
40+ self .rate_weight = rate_w
41+ self .rate_thre = rate_t
42+ self .w_conv1 = [np .mat (- 1 * np .random .rand (self .conv1 [0 ],self .conv1 [0 ])+ 0.5 ) for i in range (self .conv1 [1 ])]
43+ self .wkj = np .mat (- 1 * np .random .rand (self .num_bp3 , self .num_bp2 ) + 0.5 )
44+ self .vji = np .mat (- 1 * np .random .rand (self .num_bp2 , self .num_bp1 )+ 0.5 )
45+ self .thre_conv1 = - 2 * np .random .rand (self .conv1 [1 ])+ 1
46+ self .thre_bp2 = - 2 * np .random .rand (self .num_bp2 )+ 1
47+ self .thre_bp3 = - 2 * np .random .rand (self .num_bp3 )+ 1
48+
49+
50+ def save_model (self ,save_path ):
51+ #save model dict with pickle
52+ import pickle
53+ model_dic = {'num_bp1' :self .num_bp1 ,
54+ 'num_bp2' :self .num_bp2 ,
55+ 'num_bp3' :self .num_bp3 ,
56+ 'conv1' :self .conv1 ,
57+ 'step_conv1' :self .step_conv1 ,
58+ 'size_pooling1' :self .size_pooling1 ,
59+ 'rate_weight' :self .rate_weight ,
60+ 'rate_thre' :self .rate_thre ,
61+ 'w_conv1' :self .w_conv1 ,
62+ 'wkj' :self .wkj ,
63+ 'vji' :self .vji ,
64+ 'thre_conv1' :self .thre_conv1 ,
65+ 'thre_bp2' :self .thre_bp2 ,
66+ 'thre_bp3' :self .thre_bp3 }
67+ with open (save_path , 'wb' ) as f :
68+ pickle .dump (model_dic , f )
69+
70+ print ('Model saved: %s' % save_path )
71+
72+ @classmethod
73+ def ReadModel (cls ,model_path ):
74+ #read saved model
75+ import pickle
76+ with open (model_path , 'rb' ) as f :
77+ model_dic = pickle .load (f )
78+
79+ conv_get = model_dic .get ('conv1' )
80+ conv_get .append (model_dic .get ('step_conv1' ))
81+ size_p1 = model_dic .get ('size_pooling1' )
82+ bp1 = model_dic .get ('num_bp1' )
83+ bp2 = model_dic .get ('num_bp2' )
84+ bp3 = model_dic .get ('num_bp3' )
85+ r_w = model_dic .get ('rate_weight' )
86+ r_t = model_dic .get ('rate_thre' )
87+ #create model instance
88+ conv_ins = CNN (conv_get ,size_p1 ,bp1 ,bp2 ,bp3 ,r_w ,r_t )
89+ #modify model parameter
90+ conv_ins .w_conv1 = model_dic .get ('w_conv1' )
91+ conv_ins .wkj = model_dic .get ('wkj' )
92+ conv_ins .vji = model_dic .get ('vji' )
93+ conv_ins .thre_conv1 = model_dic .get ('thre_conv1' )
94+ conv_ins .thre_bp2 = model_dic .get ('thre_bp2' )
95+ conv_ins .thre_bp3 = model_dic .get ('thre_bp3' )
96+ return conv_ins
97+
98+
99+ def sig (self ,x ):
100+ return 1 / (1 + np .exp (- 1 * x ))
101+
102+ def do_round (self ,x ):
103+ return round (x , 3 )
104+
105+ def convolute (self ,data ,convs ,w_convs ,thre_convs ,conv_step ):
106+ #convolution process
107+ size_conv = convs [0 ]
108+ num_conv = convs [1 ]
109+ size_data = np .shape (data )[0 ]
110+ #get the data slice of original image data, data_focus
111+ data_focus = []
112+ for i_focus in range (0 , size_data - size_conv + 1 , conv_step ):
113+ for j_focus in range (0 , size_data - size_conv + 1 , conv_step ):
114+ focus = data [i_focus :i_focus + size_conv , j_focus :j_focus + size_conv ]
115+ data_focus .append (focus )
116+ #caculate the feature map of every single kernel, and saved as list of matrix
117+ data_featuremap = []
118+ Size_FeatureMap = int ((size_data - size_conv ) / conv_step + 1 )
119+ for i_map in range (num_conv ):
120+ featuremap = []
121+ for i_focus in range (len (data_focus )):
122+ net_focus = np .sum (np .multiply (data_focus [i_focus ], w_convs [i_map ])) - thre_convs [i_map ]
123+ featuremap .append (self .sig (net_focus ))
124+ featuremap = np .asmatrix (featuremap ).reshape (Size_FeatureMap , Size_FeatureMap )
125+ data_featuremap .append (featuremap )
126+
127+ #expanding the data slice to One dimenssion
128+ focus1_list = []
129+ for each_focus in data_focus :
130+ focus1_list .extend (self .Expand_Mat (each_focus ))
131+ focus_list = np .asarray (focus1_list )
132+ return focus_list ,data_featuremap
133+
134+ def pooling (self ,featuremaps ,size_pooling ,type = 'average_pool' ):
135+ #pooling process
136+ size_map = len (featuremaps [0 ])
137+ size_pooled = int (size_map / size_pooling )
138+ featuremap_pooled = []
139+ for i_map in range (len (featuremaps )):
140+ map = featuremaps [i_map ]
141+ map_pooled = []
142+ for i_focus in range (0 ,size_map ,size_pooling ):
143+ for j_focus in range (0 , size_map , size_pooling ):
144+ focus = map [i_focus :i_focus + size_pooling , j_focus :j_focus + size_pooling ]
145+ if type == 'average_pool' :
146+ #average pooling
147+ map_pooled .append (np .average (focus ))
148+ elif type == 'max_pooling' :
149+ #max pooling
150+ map_pooled .append (np .max (focus ))
151+ map_pooled = np .asmatrix (map_pooled ).reshape (size_pooled ,size_pooled )
152+ featuremap_pooled .append (map_pooled )
153+ return featuremap_pooled
154+
155+ def _expand (self ,datas ):
156+ #expanding three dimension data to one dimension list
157+ data_expanded = []
158+ for i in range (len (datas )):
159+ shapes = np .shape (datas [i ])
160+ data_listed = datas [i ].reshape (1 ,shapes [0 ]* shapes [1 ])
161+ data_listed = data_listed .getA ().tolist ()[0 ]
162+ data_expanded .extend (data_listed )
163+ data_expanded = np .asarray (data_expanded )
164+ return data_expanded
165+
166+ def _expand_mat (self ,data_mat ):
167+ #expanding matrix to one dimension list
168+ data_mat = np .asarray (data_mat )
169+ shapes = np .shape (data_mat )
170+ data_expanded = data_mat .reshape (1 ,shapes [0 ]* shapes [1 ])
171+ return data_expanded
172+
173+ def _calculate_gradient_from_pool (self ,out_map ,pd_pool ,num_map ,size_map ,size_pooling ):
174+ '''
175+ calcluate the gradient from the data slice of pool layer
176+ pd_pool: list of matrix
177+ out_map: the shape of data slice(size_map*size_map)
178+ return: pd_all: list of matrix, [num, size_map, size_map]
179+ '''
180+ pd_all = []
181+ i_pool = 0
182+ for i_map in range (num_map ):
183+ pd_conv1 = np .ones ((size_map , size_map ))
184+ for i in range (0 , size_map , size_pooling ):
185+ for j in range (0 , size_map , size_pooling ):
186+ pd_conv1 [i :i + size_pooling , j :j + size_pooling ] = pd_pool [i_pool ]
187+ i_pool = i_pool + 1
188+ pd_conv2 = np .multiply (pd_conv1 ,np .multiply (out_map [i_map ],(1 - out_map [i_map ])))
189+ pd_all .append (pd_conv2 )
190+ return pd_all
191+
192+ def trian (self ,patterns ,datas_train , datas_teach , n_repeat , error_accuracy ,draw_e = bool ):
193+ #model traning
194+ print ('----------------------Start Training-------------------------' )
195+ print (' - - Shape: Train_Data ' ,np .shape (datas_train ))
196+ print (' - - Shape: Teach_Data ' ,np .shape (datas_teach ))
197+ rp = 0
198+ all_mse = []
199+ mse = 10000
200+ while rp < n_repeat and mse >= error_accuracy :
201+ alle = 0
202+ print ('-------------Learning Time %d--------------' % rp )
203+ for p in range (len (datas_train )):
204+ #print('------------Learning Image: %d--------------'%p)
205+ data_train = np .asmatrix (datas_train [p ])
206+ data_teach = np .asarray (datas_teach [p ])
207+ data_focus1 ,data_conved1 = self .convolute (data_train ,self .conv1 ,self .w_conv1 ,
208+ self .thre_conv1 ,conv_step = self .step_conv1 )
209+ data_pooled1 = self .pooling (data_conved1 ,self .size_pooling1 )
210+ shape_featuremap1 = np .shape (data_conved1 )
211+ '''
212+ print(' -----original shape ', np.shape(data_train))
213+ print(' ---- after convolution ',np.shape(data_conv1))
214+ print(' -----after pooling ',np.shape(data_pooled1))
215+ '''
216+ data_bp_input = self ._expand (data_pooled1 )
217+ bp_out1 = data_bp_input
218+
219+ bp_net_j = np .dot (bp_out1 ,self .vji .T ) - self .thre_bp2
220+ bp_out2 = self .sig (bp_net_j )
221+ bp_net_k = np .dot (bp_out2 ,self .wkj .T ) - self .thre_bp3
222+ bp_out3 = self .sig (bp_net_k )
223+
224+ #--------------Model Leaning ------------------------
225+ # calcluate error and gradient---------------
226+ pd_k_all = np .multiply ((data_teach - bp_out3 ), np .multiply (bp_out3 , (1 - bp_out3 )))
227+ pd_j_all = np .multiply (np .dot (pd_k_all ,self .wkj ), np .multiply (bp_out2 , (1 - bp_out2 )))
228+ pd_i_all = np .dot (pd_j_all ,self .vji )
229+
230+ pd_conv1_pooled = pd_i_all / (self .size_pooling1 * self .size_pooling1 )
231+ pd_conv1_pooled = pd_conv1_pooled .T .getA ().tolist ()
232+ pd_conv1_all = self ._calculate_gradient_from_pool (data_conved1 ,pd_conv1_pooled ,shape_featuremap1 [0 ],
233+ shape_featuremap1 [1 ],self .size_pooling1 )
234+ #weight and threshold learning process---------
235+ #convolution layer
236+ for k_conv in range (self .conv1 [1 ]):
237+ pd_conv_list = self ._expand_mat (pd_conv1_all [k_conv ])
238+ delta_w = self .rate_weight * np .dot (pd_conv_list ,data_focus1 )
239+
240+ self .w_conv1 [k_conv ] = self .w_conv1 [k_conv ] + delta_w .reshape ((self .conv1 [0 ],self .conv1 [0 ]))
241+
242+ self .thre_conv1 [k_conv ] = self .thre_conv1 [k_conv ] - np .sum (pd_conv1_all [k_conv ]) * self .rate_thre
243+ #all connected layer
244+ self .wkj = self .wkj + pd_k_all .T * bp_out2 * self .rate_weight
245+ self .vji = self .vji + pd_j_all .T * bp_out1 * self .rate_weight
246+ self .thre_bp3 = self .thre_bp3 - pd_k_all * self .rate_thre
247+ self .thre_bp2 = self .thre_bp2 - pd_j_all * self .rate_thre
248+ # calculate the sum error of all single image
249+ errors = np .sum (abs ((data_teach - bp_out3 )))
250+ alle = alle + errors
251+ #print(' ----Teach ',data_teach)
252+ #print(' ----BP_output ',bp_out3)
253+ rp = rp + 1
254+ mse = alle / patterns
255+ all_mse .append (mse )
256+ def draw_error ():
257+ yplot = [error_accuracy for i in range (int (n_repeat * 1.2 ))]
258+ plt .plot (all_mse , '+-' )
259+ plt .plot (yplot , 'r--' )
260+ plt .xlabel ('Learning Times' )
261+ plt .ylabel ('All_mse' )
262+ plt .grid (True , alpha = 0.5 )
263+ plt .show ()
264+ print ('------------------Training Complished---------------------' )
265+ print (' - - Training epoch: ' , rp , ' - - Mse: %.6f' % mse )
266+ if draw_e :
267+ draw_error ()
268+ return mse
269+
270+ def predict (self ,datas_test ):
271+ #model predict
272+ produce_out = []
273+ print ('-------------------Start Testing-------------------------' )
274+ print (' - - Shape: Test_Data ' ,np .shape (datas_test ))
275+ for p in range (len (datas_test )):
276+ data_test = np .asmatrix (datas_test [p ])
277+ data_focus1 , data_conved1 = self .convolute (data_test , self .conv1 , self .w_conv1 ,
278+ self .thre_conv1 , conv_step = self .step_conv1 )
279+ data_pooled1 = self .pooling (data_conved1 , self .size_pooling1 )
280+ data_bp_input = self ._expand (data_pooled1 )
281+
282+ bp_out1 = data_bp_input
283+ bp_net_j = bp_out1 * self .vji .T - self .thre_bp2
284+ bp_out2 = self .sig (bp_net_j )
285+ bp_net_k = bp_out2 * self .wkj .T - self .thre_bp3
286+ bp_out3 = self .sig (bp_net_k )
287+ produce_out .extend (bp_out3 .getA ().tolist ())
288+ res = [list (map (self .do_round ,each )) for each in produce_out ]
289+ return np .asarray (res )
290+
291+ def convolution (self ,data ):
292+ #return the data of image after convoluting process so we can check it out
293+ data_test = np .asmatrix (data )
294+ data_focus1 , data_conved1 = self .convolute (data_test , self .conv1 , self .w_conv1 ,
295+ self .thre_conv1 , conv_step = self .step_conv1 )
296+ data_pooled1 = self .pooling (data_conved1 , self .size_pooling1 )
297+
298+ return data_conved1 ,data_pooled1
299+
300+
301+ if __name__ == '__main__' :
302+ pass
303+ '''
304+ I will put the example on other file
305+ '''
0 commit comments