|
| 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 | + Program: PYTHON |
| 15 | + Date: 2017.9.20 |
| 16 | + - - - - - -- - - - - - - - - - - - - - - - - - - - - - - |
| 17 | + ''' |
| 18 | +import numpy as np |
| 19 | +import matplotlib.pyplot as plt |
| 20 | + |
| 21 | + |
| 22 | +class CNN(): |
| 23 | + conv1 = [] |
| 24 | + w_conv1 = [] |
| 25 | + thre_conv1 = [] |
| 26 | + step_conv1 = 0 |
| 27 | + size_pooling1 = 0 |
| 28 | + num_bp1 = 0 |
| 29 | + num_bp2 = 0 |
| 30 | + num_bp3 = 0 |
| 31 | + thre_bp1 = [] |
| 32 | + thre_bp2 = [] |
| 33 | + wkj = np.mat([]) |
| 34 | + vji = np.mat([]) |
| 35 | + rate_weight = 0 |
| 36 | + rate_thre = 0 |
| 37 | + |
| 38 | + |
| 39 | + def __init__(self,conv1_get,size_p1,bp_num1,bp_num2,bp_num3,rate_w=0.2,rate_t=0.2): |
| 40 | + ''' |
| 41 | + :param conv1_get: [a,c,d],size, number, step of convolution kernel |
| 42 | + :param size_p1: pooling size |
| 43 | + :param bp_num1: units number of flatten layer |
| 44 | + :param bp_num2: units number of hidden layer |
| 45 | + :param bp_num3: units number of output layer |
| 46 | + :param rate_w: rate of weight learning |
| 47 | + :param rate_t: rate of threshold learning |
| 48 | + ''' |
| 49 | + self.num_bp1 = bp_num1 |
| 50 | + self.num_bp2 = bp_num2 |
| 51 | + self.num_bp3 = bp_num3 |
| 52 | + self.conv1 = conv1_get[:2] |
| 53 | + self.step_conv1 = conv1_get[2] |
| 54 | + self.size_pooling1 = size_p1 |
| 55 | + self.rate_weight = rate_w |
| 56 | + self.rate_thre = rate_t |
| 57 | + self.w_conv1 = [np.mat(-1*np.random.rand(self.conv1[0],self.conv1[0])+0.5) for i in range(self.conv1[1])] |
| 58 | + self.wkj = np.mat(-1 * np.random.rand(self.num_bp3, self.num_bp2) + 0.5) |
| 59 | + self.vji = np.mat(-1*np.random.rand(self.num_bp2, self.num_bp1)+0.5) |
| 60 | + self.thre_conv1 = -2*np.random.rand(self.conv1[1])+1 |
| 61 | + self.thre_bp2 = -2*np.random.rand(self.num_bp2)+1 |
| 62 | + self.thre_bp3 = -2*np.random.rand(self.num_bp3)+1 |
| 63 | + |
| 64 | + |
| 65 | + def save_model(self,save_path): |
| 66 | + #将模型保存 |
| 67 | + import pickle |
| 68 | + model_dic = {'num_bp1':self.num_bp1, |
| 69 | + 'num_bp2':self.num_bp2, |
| 70 | + 'num_bp3':self.num_bp3, |
| 71 | + 'conv1':self.conv1, |
| 72 | + 'step_conv1':self.step_conv1, |
| 73 | + 'size_pooling1':self.size_pooling1, |
| 74 | + 'rate_weight':self.rate_weight, |
| 75 | + 'rate_thre':self.rate_thre, |
| 76 | + 'w_conv1':self.w_conv1, |
| 77 | + 'wkj':self.wkj, |
| 78 | + 'vji':self.vji, |
| 79 | + 'thre_conv1':self.thre_conv1, |
| 80 | + 'thre_bp2':self.thre_bp2, |
| 81 | + 'thre_bp3':self.thre_bp3} |
| 82 | + with open(save_path, 'wb') as f: |
| 83 | + pickle.dump(model_dic, f) |
| 84 | + |
| 85 | + print('模型已经保存: %s'% save_path) |
| 86 | + |
| 87 | + |
| 88 | + def paste_model(self,save_path): |
| 89 | + #实例方法, |
| 90 | + #虽然这么写一点也不简洁。。。。 |
| 91 | + #卸载这个里面的话,只是用于修改已经存在的模型,要根据读取的数据返回实例的模型,再写一个吧 |
| 92 | + import pickle |
| 93 | + with open(save_path, 'rb') as f: |
| 94 | + model_dic = pickle.load(f) |
| 95 | + self.num_bp1 = model_dic.get('num_bp1') |
| 96 | + self.num_bp2 = model_dic.get('num_bp2') |
| 97 | + self.num_bp3 = model_dic.get('num_bp3') |
| 98 | + self.conv1 = model_dic.get('conv1') |
| 99 | + self.step_conv1 = model_dic.get('step_conv1') |
| 100 | + self.size_pooling1 = model_dic.get('size_pooling1') |
| 101 | + self.rate_weight = model_dic.get('rate_weight') |
| 102 | + self.rate_thre = model_dic.get('rate_thre') |
| 103 | + self.w_conv1 = model_dic.get('w_conv1') |
| 104 | + self.wkj = model_dic.get('wkj') |
| 105 | + self.vji = model_dic.get('vji') |
| 106 | + self.thre_conv1 = model_dic.get('thre_conv1') |
| 107 | + self.thre_bp2 = model_dic.get('thre_bp2') |
| 108 | + self.thre_bp3 = model_dic.get('thre_bp3') |
| 109 | + print('已经成功读取模型') |
| 110 | + |
| 111 | + @classmethod |
| 112 | + def ReadModel(cls,model_path): |
| 113 | + #类方法,读取保存的模型,返回一个实例。 |
| 114 | + import pickle |
| 115 | + with open(model_path, 'rb') as f: |
| 116 | + model_dic = pickle.load(f) |
| 117 | + |
| 118 | + conv_get= model_dic.get('conv1') |
| 119 | + conv_get.append(model_dic.get('step_conv1')) |
| 120 | + size_p1 = model_dic.get('size_pooling1') |
| 121 | + bp1 = model_dic.get('num_bp1') |
| 122 | + bp2 = model_dic.get('num_bp2') |
| 123 | + bp3 = model_dic.get('num_bp3') |
| 124 | + r_w = model_dic.get('rate_weight') |
| 125 | + r_t = model_dic.get('rate_thre') |
| 126 | + #创建实例 |
| 127 | + conv_ins = CNN(conv_get,size_p1,bp1,bp2,bp3,r_w,r_t) |
| 128 | + #修改实例的参数 |
| 129 | + conv_ins.w_conv1 = model_dic.get('w_conv1') |
| 130 | + conv_ins.wkj = model_dic.get('wkj') |
| 131 | + conv_ins.vji = model_dic.get('vji') |
| 132 | + conv_ins.thre_conv1 = model_dic.get('thre_conv1') |
| 133 | + conv_ins.thre_bp2 = model_dic.get('thre_bp2') |
| 134 | + conv_ins.thre_bp3 = model_dic.get('thre_bp3') |
| 135 | + return conv_ins |
| 136 | + |
| 137 | + |
| 138 | + def sig(self,x): |
| 139 | + return 1 / (1 + np.exp(-1*x)) |
| 140 | + def do_round(self,x): |
| 141 | + return round(x, 3) |
| 142 | + #卷积 |
| 143 | + def Convolute(self,data,convs,w_convs,thre_convs,conv_step): |
| 144 | + size_conv = convs[0] |
| 145 | + num_conv =convs[1] |
| 146 | + size_data = np.shape(data)[0] |
| 147 | + #得到原图像滑动的小图,data_focus |
| 148 | + data_focus = [] |
| 149 | + for i_focus in range(0, size_data - size_conv + 1, conv_step): |
| 150 | + for j_focus in range(0, size_data - size_conv + 1, conv_step): |
| 151 | + focus = data[i_focus:i_focus + size_conv, j_focus:j_focus + size_conv] |
| 152 | + data_focus.append(focus) |
| 153 | + #计算所有卷积核得到的特征图,每个特征图以矩阵形式,存储为一个列表data_featuremap |
| 154 | + data_featuremap = [] |
| 155 | + Size_FeatureMap = int((size_data - size_conv) / conv_step + 1) |
| 156 | + for i_map in range(num_conv): |
| 157 | + featuremap = [] |
| 158 | + for i_focus in range(len(data_focus)): |
| 159 | + net_focus = np.sum(np.multiply(data_focus[i_focus], w_convs[i_map])) - thre_convs[i_map] |
| 160 | + featuremap.append(self.sig(net_focus)) |
| 161 | + featuremap = np.asmatrix(featuremap).reshape(Size_FeatureMap, Size_FeatureMap) |
| 162 | + data_featuremap.append(featuremap) |
| 163 | + |
| 164 | + #将data_focus中的focus展开为一维 |
| 165 | + focus1_list = [] |
| 166 | + for each_focus in data_focus: |
| 167 | + focus1_list.extend(self.Expand_Mat(each_focus)) |
| 168 | + focus_list = np.asarray(focus1_list) |
| 169 | + return focus_list,data_featuremap |
| 170 | + |
| 171 | + # 池化 |
| 172 | + def Pooling(self,featuremaps,size_pooling): |
| 173 | + size_map = len(featuremaps[0]) |
| 174 | + size_pooled = int(size_map/size_pooling) |
| 175 | + featuremap_pooled = [] |
| 176 | + for i_map in range(len(featuremaps)): |
| 177 | + map = featuremaps[i_map] |
| 178 | + map_pooled = [] |
| 179 | + for i_focus in range(0,size_map,size_pooling): |
| 180 | + for j_focus in range(0, size_map, size_pooling): |
| 181 | + focus = map[i_focus:i_focus + size_pooling, j_focus:j_focus + size_pooling] |
| 182 | + #平均池化 |
| 183 | + map_pooled.append(np.average(focus)) |
| 184 | + #最大池化 |
| 185 | + #map_pooled.append(np.max(focus)) |
| 186 | + map_pooled = np.asmatrix(map_pooled).reshape(size_pooled,size_pooled) |
| 187 | + featuremap_pooled.append(map_pooled) |
| 188 | + return featuremap_pooled |
| 189 | + |
| 190 | + def Expand(self,datas): |
| 191 | + #将三元的数据展开为1为的list |
| 192 | + data_expanded = [] |
| 193 | + for i in range(len(datas)): |
| 194 | + shapes = np.shape(datas[i]) |
| 195 | + data_listed = datas[i].reshape(1,shapes[0]*shapes[1]) |
| 196 | + data_listed = data_listed.getA().tolist()[0] |
| 197 | + data_expanded.extend(data_listed) |
| 198 | + #连接所有数据 |
| 199 | + data_expanded = np.asarray(data_expanded) |
| 200 | + return data_expanded |
| 201 | + |
| 202 | + def Expand_Mat(self,data_mat): |
| 203 | + #用来展开矩阵为一维的list |
| 204 | + data_mat = np.asarray(data_mat) |
| 205 | + shapes = np.shape(data_mat) |
| 206 | + data_expanded = data_mat.reshape(1,shapes[0]*shapes[1]) |
| 207 | + return data_expanded |
| 208 | + |
| 209 | + def Getpd_From_Pool(self,out_map,pd_pool,num_map,size_map,size_pooling): |
| 210 | + ''' |
| 211 | + 误差反传,从pooled到前一个map, 例如将池化层6*6的误差矩阵扩大为12*12的误差矩阵 |
| 212 | + pd_pool: 是采样层的误差,list形式。。。。要改要改 |
| 213 | + out_map: 前面特征图的输出,数量*size*size的列表形式 |
| 214 | + return: pd_all:前面层所有的特征图的pd, num*size_map*size_map的列表形式 |
| 215 | + ''' |
| 216 | + pd_all = [] |
| 217 | + i_pool = 0 |
| 218 | + for i_map in range(num_map): |
| 219 | + pd_conv1 = np.ones((size_map, size_map)) |
| 220 | + for i in range(0, size_map, size_pooling): |
| 221 | + for j in range(0, size_map, size_pooling): |
| 222 | + pd_conv1[i:i + size_pooling, j:j + size_pooling] = pd_pool[i_pool] |
| 223 | + i_pool = i_pool + 1 |
| 224 | + pd_conv2 = np.multiply(pd_conv1,np.multiply(out_map[i_map],(1-out_map[i_map]))) |
| 225 | + pd_all.append(pd_conv2) |
| 226 | + return pd_all |
| 227 | + |
| 228 | + def trian(self,patterns,datas_train, datas_teach, n_repeat, error_accuracy,draw_e = bool): |
| 229 | + print('----------------------Start Training-------------------------') |
| 230 | + print(' - - Shape: Train_Data ',np.shape(datas_train)) |
| 231 | + print(' - - Shape: Teach_Data ',np.shape(datas_teach)) |
| 232 | + rp = 0 |
| 233 | + all_mse = [] |
| 234 | + mse = 10000 |
| 235 | + while rp < n_repeat and mse >= error_accuracy: |
| 236 | + alle = 0 |
| 237 | + print('-------------进行第%d次学习--------------'%rp) |
| 238 | + for p in range(len(datas_train)): |
| 239 | + #print('------------学习第%d个图像--------------'%p) |
| 240 | + data_train = np.asmatrix(datas_train[p]) |
| 241 | + data_teach = np.asarray(datas_teach[p]) |
| 242 | + data_focus1,data_conved1 = self.Convolute(data_train,self.conv1,self.w_conv1, |
| 243 | + self.thre_conv1,conv_step=self.step_conv1) |
| 244 | + data_pooled1 = self.Pooling(data_conved1,self.size_pooling1) |
| 245 | + shape_featuremap1 = np.shape(data_conved1) |
| 246 | + ''' |
| 247 | + print(' -----original shape ', np.shape(data_train)) |
| 248 | + print(' ---- after convolution ',np.shape(data_conv1)) |
| 249 | + print(' -----after pooling ',np.shape(data_pooled1)) |
| 250 | + ''' |
| 251 | + data_bp_input = self.Expand(data_pooled1) |
| 252 | + # 计算第一层输入输出 |
| 253 | + bp_out1 = data_bp_input |
| 254 | + # 计算第二层输入输出 |
| 255 | + bp_net_j = np.dot(bp_out1,self.vji.T) - self.thre_bp2 |
| 256 | + bp_out2 = self.sig(bp_net_j) |
| 257 | + # 计算第三层输入输出 |
| 258 | + bp_net_k = np.dot(bp_out2 ,self.wkj.T) - self.thre_bp3 |
| 259 | + bp_out3 = self.sig(bp_net_k) |
| 260 | + |
| 261 | + # 计算一般化误差 |
| 262 | + pd_k_all = np.multiply((data_teach - bp_out3), np.multiply(bp_out3, (1 - bp_out3))) |
| 263 | + pd_j_all = np.multiply(np.dot(pd_k_all,self.wkj), np.multiply(bp_out2, (1 - bp_out2))) |
| 264 | + pd_i_all = np.dot(pd_j_all,self.vji) |
| 265 | + |
| 266 | + pd_conv1_pooled = pd_i_all / (self.size_pooling1*self.size_pooling1) |
| 267 | + pd_conv1_pooled = pd_conv1_pooled.T.getA().tolist() |
| 268 | + pd_conv1_all = self.Getpd_From_Pool(data_conved1,pd_conv1_pooled,shape_featuremap1[0], |
| 269 | + shape_featuremap1[1],self.size_pooling1) |
| 270 | + |
| 271 | + #卷积层1的权重和阈值修正,每个卷积核的权重需要修正 12*12(map) 次 |
| 272 | + #修正量为featuremap中点的偏导值 乘以 前一层图像focus, 整个权重模板一起更新 |
| 273 | + for k_conv in range(self.conv1[1]): |
| 274 | + pd_conv_list = self.Expand_Mat(pd_conv1_all[k_conv]) |
| 275 | + delta_w = self.rate_weight * np.dot(pd_conv_list,data_focus1) |
| 276 | + |
| 277 | + self.w_conv1[k_conv] = self.w_conv1[k_conv] + delta_w.reshape((self.conv1[0],self.conv1[0])) |
| 278 | + |
| 279 | + self.thre_conv1[k_conv] = self.thre_conv1[k_conv] - np.sum(pd_conv1_all[k_conv]) * self.rate_thre |
| 280 | + # 更新kj层的权重 |
| 281 | + |
| 282 | + self.wkj = self.wkj + pd_k_all.T * bp_out2 * self.rate_weight |
| 283 | + # 更新ji层的权重 |
| 284 | + self.vji = self.vji + pd_j_all.T * bp_out1 * self.rate_weight |
| 285 | + # 更新阈值 |
| 286 | + self.thre_bp3 = self.thre_bp3 - pd_k_all * self.rate_thre |
| 287 | + self.thre_bp2 = self.thre_bp2 - pd_j_all * self.rate_thre |
| 288 | + # 计算总误差 |
| 289 | + errors = np.sum(abs((data_teach - bp_out3))) |
| 290 | + alle = alle + errors |
| 291 | + #print(' ----Teach ',data_teach) |
| 292 | + #print(' ----BP_output ',bp_out3) |
| 293 | + rp = rp + 1 |
| 294 | + mse = alle/patterns |
| 295 | + all_mse.append(mse) |
| 296 | + def draw_error(): |
| 297 | + yplot = [error_accuracy for i in range(int(n_repeat * 1.2))] |
| 298 | + plt.plot(all_mse, '+-') |
| 299 | + plt.plot(yplot, 'r--') |
| 300 | + plt.xlabel('Learning Times') |
| 301 | + plt.ylabel('All_mse') |
| 302 | + plt.grid(True, alpha=0.5) |
| 303 | + plt.show() |
| 304 | + print('------------------Training Complished---------------------') |
| 305 | + print(' - - Training epoch: ', rp, ' - - Mse: %.6f' % mse) |
| 306 | + if draw_e: |
| 307 | + draw_error() |
| 308 | + return mse |
| 309 | + |
| 310 | + def produce(self,datas_test): |
| 311 | + #对验证和测试数据集进行输出 |
| 312 | + produce_out = [] |
| 313 | + print('-------------------Start Testing-------------------------') |
| 314 | + print(' - - Shape: Test_Data ',np.shape(datas_test)) |
| 315 | + for p in range(len(datas_test)): |
| 316 | + print('--------测试第%d个图像----------' % p) |
| 317 | + data_test = np.asmatrix(datas_test[p]) |
| 318 | + data_focus1, data_conved1 = self.Convolute(data_test, self.conv1, self.w_conv1, |
| 319 | + self.thre_conv1, conv_step=self.step_conv1) |
| 320 | + data_pooled1 = self.Pooling(data_conved1, self.size_pooling1) |
| 321 | + data_bp_input = self.Expand(data_pooled1) |
| 322 | + # 计算第一层输入输出 |
| 323 | + bp_out1 = data_bp_input |
| 324 | + # 计算第二层输入输出 |
| 325 | + bp_net_j = bp_out1 * self.vji.T - self.thre_bp2 |
| 326 | + bp_out2 = self.sig(bp_net_j) |
| 327 | + # 计算第三层输入输出 |
| 328 | + bp_net_k = bp_out2 * self.wkj.T - self.thre_bp3 |
| 329 | + bp_out3 = self.sig(bp_net_k) |
| 330 | + produce_out.extend(bp_out3.getA().tolist()) |
| 331 | + res = [list(map(self.do_round,each)) for each in produce_out] |
| 332 | + return np.asarray(res) |
| 333 | + |
| 334 | + def convolution(self,data): |
| 335 | + #返回卷积和池化后的数据,用于查看图像 |
| 336 | + data_test = np.asmatrix(data) |
| 337 | + data_focus1, data_conved1 = self.Convolute(data_test, self.conv1, self.w_conv1, |
| 338 | + self.thre_conv1, conv_step=self.step_conv1) |
| 339 | + data_pooled1 = self.Pooling(data_conved1, self.size_pooling1) |
| 340 | + |
| 341 | + return data_conved1,data_pooled1 |
| 342 | + |
| 343 | + |
0 commit comments