├── Discuz ├── README.md ├── get_discuz.py ├── test.py └── train.py ├── Pytorch-Seg ├── lesson-1 │ ├── unet_model.py │ └── unet_parts.py ├── lesson-2 │ ├── best_model.pth │ ├── data │ │ ├── test │ │ │ ├── 0.png │ │ │ ├── 1.png │ │ │ ├── 10.png │ │ │ ├── 11.png │ │ │ ├── 12.png │ │ │ ├── 13.png │ │ │ ├── 14.png │ │ │ ├── 15.png │ │ │ ├── 16.png │ │ │ ├── 17.png │ │ │ ├── 18.png │ │ │ ├── 19.png │ │ │ ├── 2.png │ │ │ ├── 20.png │ │ │ ├── 21.png │ │ │ ├── 22.png │ │ │ ├── 23.png │ │ │ ├── 24.png │ │ │ ├── 25.png │ │ │ ├── 26.png │ │ │ ├── 27.png │ │ │ ├── 28.png │ │ │ ├── 29.png │ │ │ ├── 3.png │ │ │ ├── 4.png │ │ │ ├── 5.png │ │ │ ├── 6.png │ │ │ ├── 7.png │ │ │ ├── 8.png │ │ │ └── 9.png │ │ └── train │ │ │ ├── image │ │ │ ├── 0.png │ │ │ ├── 1.png │ │ │ ├── 10.png │ │ │ ├── 11.png │ │ │ ├── 12.png │ │ │ ├── 13.png │ │ │ ├── 14.png │ │ │ ├── 15.png │ │ │ ├── 16.png │ │ │ ├── 17.png │ │ │ ├── 18.png │ │ │ ├── 19.png │ │ │ ├── 2.png │ │ │ ├── 20.png │ │ │ ├── 21.png │ │ │ ├── 22.png │ │ │ ├── 23.png │ │ │ ├── 24.png │ │ │ ├── 25.png │ │ │ ├── 26.png │ │ │ ├── 27.png │ │ │ ├── 28.png │ │ │ ├── 29.png │ │ │ ├── 3.png │ │ │ ├── 4.png │ │ │ ├── 5.png │ │ │ ├── 6.png │ │ │ ├── 7.png │ │ │ ├── 8.png │ │ │ └── 9.png │ │ │ └── label │ │ │ ├── 0.png │ │ │ ├── 1.png │ │ │ ├── 10.png │ │ │ ├── 11.png │ │ │ ├── 12.png │ │ │ ├── 13.png │ │ │ ├── 14.png │ │ │ ├── 15.png │ │ │ ├── 16.png │ │ │ ├── 17.png │ │ │ ├── 18.png │ │ │ ├── 19.png │ │ │ ├── 2.png │ │ │ ├── 20.png │ │ │ ├── 21.png │ │ │ ├── 22.png │ │ │ ├── 23.png │ │ │ ├── 24.png │ │ │ ├── 25.png │ │ │ ├── 26.png │ │ │ ├── 27.png │ │ │ ├── 28.png │ │ │ ├── 29.png │ │ │ ├── 3.png │ │ │ ├── 4.png │ │ │ ├── 5.png │ │ │ ├── 6.png │ │ │ ├── 7.png │ │ │ ├── 8.png │ │ │ └── 9.png │ ├── model │ │ ├── __init__.py │ │ ├── unet_model.py │ │ └── unet_parts.py │ ├── predict.py │ ├── train.py │ └── utils │ │ ├── __pycache__ │ │ └── dataset.cpython-37.pyc │ │ └── dataset.py ├── lesson-3 │ ├── log.py │ ├── logger.py │ ├── show_loss.py │ ├── tensorboardX_test.py │ └── train_loss.txt └── lesson-4 │ ├── dataset.py │ ├── dir_label.txt │ ├── infer.py │ ├── test.txt │ ├── train.py │ ├── train.txt │ └── val.txt ├── README.md ├── Tutorial ├── README.md ├── lesson-1 │ └── perceptron.py ├── lesson-2 │ ├── linear_unit.py │ └── perceptron.py ├── lesson-3 │ ├── bp.py │ ├── fc.py │ └── mnist.py ├── lesson-4 │ ├── activators.py │ └── cnn.py ├── lesson-5 │ ├── activators.py │ ├── cnn.py │ └── rnn.py ├── lesson-6 │ ├── activators.py │ ├── cnn.py │ └── lstm.py └── lesson-7 │ ├── activators.py │ └── recursive.py ├── face └── video_mosaic.py └── mnist.py /Discuz/README.md: -------------------------------------------------------------------------------- 1 | ## 深度学习实战教程 2 | 3 | #### 说明 4 | 5 | * get_discuz.py接口已关闭,关闭说明以及验证码环境部署方法,请查看:[Tensorflow实战(二):Discuz验证码识别](https://cuijiahua.com/blog/2018/01/dl_5.html "悬停显示") -------------------------------------------------------------------------------- /Discuz/get_discuz.py: -------------------------------------------------------------------------------- 1 | #-*- coding:utf-8 -*- 2 | from urllib.request import urlretrieve 3 | import time, random, os 4 | 5 | class Discuz(): 6 | def __init__(self): 7 | # Discuz验证码生成图片地址 8 | self.url = 'http://cuijiahua.com/tutrial/discuz/index.php?label=' 9 | 10 | def random_captcha_text(self, captcha_size = 4): 11 | """ 12 | 验证码一般都无视大小写;验证码长度4个字符 13 | Parameters: 14 | captcha_size:验证码长度 15 | Returns: 16 | captcha_text:验证码字符串 17 | """ 18 | number = ['0','1','2','3','4','5','6','7','8','9'] 19 | alphabet = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'] 20 | char_set = number + alphabet 21 | captcha_text = [] 22 | for i in range(captcha_size): 23 | c = random.choice(char_set) 24 | captcha_text.append(c) 25 | captcha_text = ''.join(captcha_text) 26 | return captcha_text 27 | 28 | def download_discuz(self, nums = 5000): 29 | """ 30 | 下载验证码图片 31 | Parameters: 32 | nums:下载的验证码图片数量 33 | """ 34 | dirname = './Discuz' 35 | if dirname not in os.listdir(): 36 | os.mkdir(dirname) 37 | for i in range(nums): 38 | label = self.random_captcha_text() 39 | print('第%d张图片:%s下载' % (i + 1,label)) 40 | urlretrieve(url = self.url + label, filename = dirname + '/' + label + '.jpg') 41 | # 请至少加200ms延时,避免给我的服务器造成过多的压力,如发现影响服务器正常工作,我会关闭此功能。 42 | # 你好我也好,大家好才是真的好! 43 | time.sleep(0.2) 44 | print('恭喜图片下载完成!') 45 | 46 | if __name__ == '__main__': 47 | dz = Discuz() 48 | dz.download_discuz() 49 | -------------------------------------------------------------------------------- /Discuz/test.py: -------------------------------------------------------------------------------- 1 | #-*- coding:utf-8 -*- 2 | import tensorflow as tf 3 | import numpy as np 4 | import train 5 | 6 | def crack_captcha(captcha_image, captcha_label): 7 | """ 8 | 使用模型做预测 9 | Parameters: 10 | captcha_image:数据 11 | captcha_label:标签 12 | """ 13 | 14 | output = dz.crack_captcha_cnn() 15 | saver = tf.train.Saver() 16 | with tf.Session(config=dz.config) as sess: 17 | 18 | saver.restore(sess, tf.train.latest_checkpoint('.')) 19 | for i in range(len(captcha_label)): 20 | img = captcha_image[i].flatten() 21 | label = captcha_label[i] 22 | predict = tf.argmax(tf.reshape(output, [-1, dz.max_captcha, dz.char_set_len]), 2) 23 | text_list = sess.run(predict, feed_dict={dz.X: [img], dz.keep_prob: 1}) 24 | text = text_list[0].tolist() 25 | vector = np.zeros(dz.max_captcha*dz.char_set_len) 26 | i = 0 27 | for n in text: 28 | vector[i*dz.char_set_len + n] = 1 29 | i += 1 30 | prediction_text = dz.vec2text(vector) 31 | print("正确: {} 预测: {}".format(dz.vec2text(label), prediction_text)) 32 | 33 | if __name__ == '__main__': 34 | dz = train.Discuz() 35 | batch_x, batch_y = dz.get_next_batch(False, 5) 36 | crack_captcha(batch_x, batch_y) 37 | -------------------------------------------------------------------------------- /Discuz/train.py: -------------------------------------------------------------------------------- 1 | #-*- coding:utf-8 -*- 2 | import tensorflow as tf 3 | import matplotlib.pyplot as plt 4 | import numpy as np 5 | import os, random, cv2 6 | 7 | class Discuz(): 8 | def __init__(self): 9 | # 指定GPU 10 | os.environ["CUDA_VISIBLE_DEVICES"] = "0" 11 | self.config = tf.ConfigProto(allow_soft_placement = True) 12 | gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction = 1) 13 | self.config.gpu_options.allow_growth = True 14 | # 数据集路径 15 | self.data_path = './Discuz/' 16 | # 写到指定的磁盘路径中 17 | self.log_dir = '/home/Jack_Cui/Work/Crack_Discuz/Tb' 18 | # 数据集图片大小 19 | self.width = 30 20 | self.heigth = 100 21 | # 最大迭代次数 22 | self.max_steps = 1000000 23 | # 读取数据集 24 | self.test_imgs, self.test_labels, self.train_imgs, self.train_labels = self.get_imgs() 25 | # 训练集大小 26 | self.train_size = len(self.train_imgs) 27 | # 测试集大小 28 | self.test_size = len(self.test_imgs) 29 | # 每次获得batch_size大小的当前训练集指针 30 | self.train_ptr = 0 31 | # 每次获取batch_size大小的当前测试集指针 32 | self.test_ptr = 0 33 | # 字符字典大小:0-9 a-z A-Z _(验证码如果小于4,用_补齐) 一共63个字符 34 | self.char_set_len = 63 35 | # 验证码最长的长度为4 36 | self.max_captcha = 4 37 | # 输入数据X占位符 38 | self.X = tf.placeholder(tf.float32, [None, self.heigth*self.width]) 39 | # 输入数据Y占位符 40 | self.Y = tf.placeholder(tf.float32, [None, self.char_set_len*self.max_captcha]) 41 | # keepout占位符 42 | self.keep_prob = tf.placeholder(tf.float32) 43 | 44 | def test_show_img(self, fname, show = True): 45 | """ 46 | 读取图片,显示图片信息并显示其灰度图 47 | Parameters: 48 | fname:图片文件名 49 | show:是否展示灰度图 50 | """ 51 | # 获得标签 52 | label = fname.split('.') 53 | # 读取图片 54 | img = cv2.imread(fname) 55 | # 获取图片大小 56 | width, heigth, _ = img.shape 57 | print("图像宽:%s px" % width) 58 | print("图像高:%s px" % heigth) 59 | 60 | if show == True: 61 | # plt.imshow(img) 62 | #将fig画布分隔成1行1列,不共享x轴和y轴,fig画布的大小为(13,8) 63 | #当nrow=3,nclos=2时,代表fig画布被分为六个区域,axs[0][0]表示第一行第一列 64 | fig, axs = plt.subplots(nrows=2, ncols=1, sharex=False, sharey=False, figsize=(10,5)) 65 | axs[0].imshow(img) 66 | axs0_title_text = axs[0].set_title(u'RGB img') 67 | plt.setp(axs0_title_text, size=10) 68 | # 转换为灰度图 69 | gray = np.mean(img, axis=-1) 70 | axs[1].imshow(gray, cmap='Greys_r') 71 | axs1_title_text = axs[1].set_title(u'GRAY img') 72 | plt.setp(axs1_title_text, size=10) 73 | plt.show() 74 | 75 | def get_imgs(self, rate = 0.2): 76 | """ 77 | 获取图片,并划分训练集和测试集 78 | Parameters: 79 | rate:测试集和训练集的比例,即测试集个数/训练集个数 80 | Returns: 81 | test_imgs:测试集 82 | test_labels:测试集标签 83 | train_imgs:训练集 84 | test_labels:训练集标签 85 | """ 86 | # 读取图片 87 | imgs = os.listdir(self.data_path) 88 | # 打乱图片顺序 89 | random.shuffle(imgs) 90 | 91 | # 数据集总共个数 92 | imgs_num = len(imgs) 93 | # 按照比例求出测试集个数 94 | test_num = int(imgs_num * rate / (1 + rate)) 95 | # 测试集 96 | test_imgs = imgs[:test_num] 97 | # 根据文件名获取测试集标签 98 | test_labels = list(map(lambda x: x.split('.')[0], test_imgs)) 99 | # 训练集 100 | train_imgs = imgs[test_num:] 101 | # 根据文件名获取训练集标签 102 | train_labels = list(map(lambda x: x.split('.')[0], train_imgs)) 103 | 104 | return test_imgs, test_labels, train_imgs, train_labels 105 | 106 | def get_next_batch(self, train_flag=True, batch_size=100): 107 | """ 108 | 获得batch_size大小的数据集 109 | Parameters: 110 | batch_size:batch_size大小 111 | train_flag:是否从训练集获取数据 112 | Returns: 113 | batch_x:大小为batch_size的数据x 114 | batch_y:大小为batch_size的数据y 115 | """ 116 | # 从训练集获取数据 117 | if train_flag == True: 118 | if (batch_size + self.train_ptr) < self.train_size: 119 | trains = self.train_imgs[self.train_ptr:(self.train_ptr + batch_size)] 120 | labels = self.train_labels[self.train_ptr:(self.train_ptr + batch_size)] 121 | self.train_ptr += batch_size 122 | else: 123 | new_ptr = (self.train_ptr + batch_size) % self.train_size 124 | trains = self.train_imgs[self.train_ptr:] + self.train_imgs[:new_ptr] 125 | labels = self.train_labels[self.train_ptr:] + self.train_labels[:new_ptr] 126 | self.train_ptr = new_ptr 127 | 128 | batch_x = np.zeros([batch_size, self.heigth*self.width]) 129 | batch_y = np.zeros([batch_size, self.max_captcha*self.char_set_len]) 130 | 131 | for index, train in enumerate(trains): 132 | img = np.mean(cv2.imread(self.data_path + train), -1) 133 | # 将多维降维1维 134 | batch_x[index,:] = img.flatten() / 255 135 | for index, label in enumerate(labels): 136 | batch_y[index,:] = self.text2vec(label) 137 | 138 | # 从测试集获取数据 139 | else: 140 | if (batch_size + self.test_ptr) < self.test_size: 141 | tests = self.test_imgs[self.test_ptr:(self.test_ptr + batch_size)] 142 | labels = self.test_labels[self.test_ptr:(self.test_ptr + batch_size)] 143 | self.test_ptr += batch_size 144 | else: 145 | new_ptr = (self.test_ptr + batch_size) % self.test_size 146 | tests = self.test_imgs[self.test_ptr:] + self.test_imgs[:new_ptr] 147 | labels = self.test_labels[self.test_ptr:] + self.test_labels[:new_ptr] 148 | self.test_ptr = new_ptr 149 | 150 | batch_x = np.zeros([batch_size, self.heigth*self.width]) 151 | batch_y = np.zeros([batch_size, self.max_captcha*self.char_set_len]) 152 | 153 | for index, test in enumerate(tests): 154 | img = np.mean(cv2.imread(self.data_path + test), -1) 155 | # 将多维降维1维 156 | batch_x[index,:] = img.flatten() / 255 157 | for index, label in enumerate(labels): 158 | batch_y[index,:] = self.text2vec(label) 159 | 160 | return batch_x, batch_y 161 | 162 | def text2vec(self, text): 163 | """ 164 | 文本转向量 165 | Parameters: 166 | text:文本 167 | Returns: 168 | vector:向量 169 | """ 170 | if len(text) > 4: 171 | raise ValueError('验证码最长4个字符') 172 | 173 | vector = np.zeros(4 * self.char_set_len) 174 | def char2pos(c): 175 | if c =='_': 176 | k = 62 177 | return k 178 | k = ord(c) - 48 179 | if k > 9: 180 | k = ord(c) - 55 181 | if k > 35: 182 | k = ord(c) - 61 183 | if k > 61: 184 | raise ValueError('No Map') 185 | return k 186 | for i, c in enumerate(text): 187 | idx = i * self.char_set_len + char2pos(c) 188 | vector[idx] = 1 189 | return vector 190 | 191 | def vec2text(self, vec): 192 | """ 193 | 向量转文本 194 | Parameters: 195 | vec:向量 196 | Returns: 197 | 文本 198 | """ 199 | char_pos = vec.nonzero()[0] 200 | text = [] 201 | for i, c in enumerate(char_pos): 202 | char_at_pos = i #c/63 203 | char_idx = c % self.char_set_len 204 | if char_idx < 10: 205 | char_code = char_idx + ord('0') 206 | elif char_idx < 36: 207 | char_code = char_idx - 10 + ord('A') 208 | elif char_idx < 62: 209 | char_code = char_idx - 36 + ord('a') 210 | elif char_idx == 62: 211 | char_code = ord('_') 212 | else: 213 | raise ValueError('error') 214 | text.append(chr(char_code)) 215 | return "".join(text) 216 | 217 | def crack_captcha_cnn(self, w_alpha=0.01, b_alpha=0.1): 218 | """ 219 | 定义CNN 220 | Parameters: 221 | w_alpha:权重系数 222 | b_alpha:偏置系数 223 | Returns: 224 | out:CNN输出 225 | """ 226 | # 卷积的input: 一个Tensor。数据维度是四维[batch, in_height, in_width, in_channels] 227 | # 具体含义是[batch大小, 图像高度, 图像宽度, 图像通道数] 228 | # 因为是灰度图,所以是单通道的[?, 100, 30, 1] 229 | x = tf.reshape(self.X, shape=[-1, self.heigth, self.width, 1]) 230 | # 卷积的filter:一个Tensor。数据维度是四维[filter_height, filter_width, in_channels, out_channels] 231 | # 具体含义是[卷积核的高度, 卷积核的宽度, 图像通道数, 卷积核个数] 232 | w_c1 = tf.Variable(w_alpha*tf.random_normal([3, 3, 1, 32])) 233 | # 偏置项bias 234 | b_c1 = tf.Variable(b_alpha*tf.random_normal([32])) 235 | # conv2d卷积层输入: 236 | # strides: 一个长度是4的一维整数类型数组,每一维度对应的是 input 中每一维的对应移动步数 237 | # padding:一个字符串,取值为 SAME 或者 VALID 前者使得卷积后图像尺寸不变, 后者尺寸变化 238 | # conv2d卷积层输出: 239 | # 一个四维的Tensor, 数据维度为 [batch, out_width, out_height, in_channels * out_channels] 240 | # [?, 100, 30, 32] 241 | # 输出计算公式H0 = (H - F + 2 * P) / S + 1 242 | # 对于本卷积层而言,因为padding为SAME,所以P为1。 243 | # 其中H为图像高度,F为卷积核高度,P为边填充,S为步长 244 | # 学习参数: 245 | # 32*(3*3+1)=320 246 | # 连接个数: 247 | # (输出图像宽度*输出图像高度)(卷积核高度*卷积核宽度+1)*卷积核数量(100*30)(3*3+1)*32=100*30*320=960000个 248 | 249 | # bias_add:将偏差项bias加到value上。这个操作可以看做是tf.add的一个特例,其中bias是必须的一维。 250 | # 该API支持广播形式,因此value可以是任何维度。但是,该API又不像tf.add,可以让bias的维度和value的最后一维不同, 251 | conv1 = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(x, w_c1, strides=[1, 1, 1, 1], padding='SAME'), b_c1)) 252 | # max_pool池化层输入: 253 | # ksize:池化窗口的大小,取一个四维向量,一般是[1, height, width, 1] 254 | # 因为我们不想在batch和channels上做池化,所以这两个维度设为了1 255 | # strides:和卷积类似,窗口在每一个维度上滑动的步长,一般也是[1, stride,stride, 1] 256 | # padding:和卷积类似,可以取'VALID' 或者'SAME' 257 | # max_pool池化层输出: 258 | # 返回一个Tensor,类型不变,shape仍然是[batch, out_width, out_height, in_channels]这种形式 259 | # [?, 50, 15, 32] 260 | # 学习参数: 261 | # 2*32 262 | # 连接个数: 263 | # 15*50*32*(2*2+1)=120000 264 | conv1 = tf.nn.max_pool(conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME') 265 | # dropout层 266 | # conv1 = tf.nn.dropout(conv1, self.keep_prob) 267 | w_c2 = tf.Variable(w_alpha*tf.random_normal([3, 3, 32, 64])) 268 | b_c2 = tf.Variable(b_alpha*tf.random_normal([64])) 269 | # [?, 50, 15, 64] 270 | conv2 = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(conv1, w_c2, strides=[1, 1, 1, 1], padding='SAME'), b_c2)) 271 | # [?, 25, 8, 64] 272 | conv2 = tf.nn.max_pool(conv2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME') 273 | #conv2 = tf.nn.dropout(conv2, self.keep_prob) 274 | w_c3 = tf.Variable(w_alpha*tf.random_normal([3, 3, 64, 64])) 275 | b_c3 = tf.Variable(b_alpha*tf.random_normal([64])) 276 | # [?, 25, 8, 64] 277 | conv3 = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(conv2, w_c3, strides=[1, 1, 1, 1], padding='SAME'), b_c3)) 278 | # [?, 13, 4, 64] 279 | conv3 = tf.nn.max_pool(conv3, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME') 280 | #conv3 = tf.nn.dropout(conv3, self.keep_prob) 281 | # [3328, 1024] 282 | w_d = tf.Variable(w_alpha*tf.random_normal([4*13*64, 1024])) 283 | b_d = tf.Variable(b_alpha*tf.random_normal([1024])) 284 | # [?, 3328] 285 | dense = tf.reshape(conv3, [-1, w_d.get_shape().as_list()[0]]) 286 | # [?, 1024] 287 | dense = tf.nn.relu(tf.add(tf.matmul(dense, w_d), b_d)) 288 | dense = tf.nn.dropout(dense, self.keep_prob) 289 | # [1024, 63*4=252] 290 | w_out = tf.Variable(w_alpha*tf.random_normal([1024, self.max_captcha*self.char_set_len])) 291 | 292 | b_out = tf.Variable(b_alpha*tf.random_normal([self.max_captcha*self.char_set_len])) 293 | # [?, 252] 294 | out = tf.add(tf.matmul(dense, w_out), b_out) 295 | # out = tf.nn.softmax(out) 296 | return out 297 | 298 | def train_crack_captcha_cnn(self): 299 | """ 300 | 训练函数 301 | """ 302 | output = self.crack_captcha_cnn() 303 | 304 | # 创建损失函数 305 | # loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=output, labels=self.Y)) 306 | diff = tf.nn.sigmoid_cross_entropy_with_logits(logits=output, labels=self.Y) 307 | loss = tf.reduce_mean(diff) 308 | tf.summary.scalar('loss', loss) 309 | 310 | # 使用AdamOptimizer优化器训练模型,最小化交叉熵损失 311 | optimizer = tf.train.AdamOptimizer(learning_rate=0.001).minimize(loss) 312 | 313 | # 计算准确率 314 | y = tf.reshape(output, [-1, self.max_captcha, self.char_set_len]) 315 | y_ = tf.reshape(self.Y, [-1, self.max_captcha, self.char_set_len]) 316 | correct_pred = tf.equal(tf.argmax(y, 2), tf.argmax(y_, 2)) 317 | accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) 318 | tf.summary.scalar('accuracy', accuracy) 319 | 320 | merged = tf.summary.merge_all() 321 | saver = tf.train.Saver() 322 | with tf.Session(config=self.config) as sess: 323 | # 写到指定的磁盘路径中 324 | train_writer = tf.summary.FileWriter(self.log_dir + '/train', sess.graph) 325 | test_writer = tf.summary.FileWriter(self.log_dir + '/test') 326 | sess.run(tf.global_variables_initializer()) 327 | 328 | # 遍历self.max_steps次 329 | for i in range(self.max_steps): 330 | # 迭代500次,打乱一下数据集 331 | if i % 499 == 0: 332 | self.test_imgs, self.test_labels, self.train_imgs, self.train_labels = self.get_imgs() 333 | # 每10次,使用测试集,测试一下准确率 334 | if i % 10 == 0: 335 | batch_x_test, batch_y_test = self.get_next_batch(False, 100) 336 | summary, acc = sess.run([merged, accuracy], feed_dict={self.X: batch_x_test, self.Y: batch_y_test, self.keep_prob: 1}) 337 | print('迭代第%d次 accuracy:%f' % (i+1, acc)) 338 | test_writer.add_summary(summary, i) 339 | 340 | # 如果准确率大于85%,则保存模型并退出。 341 | if acc > 0.95: 342 | train_writer.close() 343 | test_writer.close() 344 | saver.save(sess, "crack_capcha.model", global_step=i) 345 | break 346 | # 一直训练 347 | else: 348 | batch_x, batch_y = self.get_next_batch(True, 100) 349 | loss_value, _ = sess.run([loss, optimizer], feed_dict={self.X: batch_x, self.Y: batch_y, self.keep_prob: 1}) 350 | print('迭代第%d次 loss:%f' % (i+1, loss_value)) 351 | curve = sess.run(merged, feed_dict={self.X: batch_x_test, self.Y: batch_y_test, self.keep_prob: 1}) 352 | train_writer.add_summary(curve, i) 353 | 354 | train_writer.close() 355 | test_writer.close() 356 | saver.save(sess, "crack_capcha.model", global_step=self.max_steps) 357 | 358 | 359 | if __name__ == '__main__': 360 | dz = Discuz() 361 | dz.train_crack_captcha_cnn() 362 | -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-1/unet_model.py: -------------------------------------------------------------------------------- 1 | """ Full assembly of the parts to form the complete network """ 2 | """Refer https://github.com/milesial/Pytorch-UNet/blob/master/unet/unet_model.py""" 3 | 4 | import torch.nn.functional as F 5 | 6 | from unet_parts import * 7 | 8 | 9 | class UNet(nn.Module): 10 | def __init__(self, n_channels, n_classes, bilinear=False): 11 | super(UNet, self).__init__() 12 | self.n_channels = n_channels 13 | self.n_classes = n_classes 14 | self.bilinear = bilinear 15 | 16 | self.inc = DoubleConv(n_channels, 64) 17 | self.down1 = Down(64, 128) 18 | self.down2 = Down(128, 256) 19 | self.down3 = Down(256, 512) 20 | self.down4 = Down(512, 1024) 21 | self.up1 = Up(1024, 512, bilinear) 22 | self.up2 = Up(512, 256, bilinear) 23 | self.up3 = Up(256, 128, bilinear) 24 | self.up4 = Up(128, 64, bilinear) 25 | self.outc = OutConv(64, n_classes) 26 | 27 | def forward(self, x): 28 | x1 = self.inc(x) 29 | x2 = self.down1(x1) 30 | x3 = self.down2(x2) 31 | x4 = self.down3(x3) 32 | x5 = self.down4(x4) 33 | x = self.up1(x5, x4) 34 | x = self.up2(x, x3) 35 | x = self.up3(x, x2) 36 | x = self.up4(x, x1) 37 | logits = self.outc(x) 38 | return logits 39 | 40 | if __name__ == '__main__': 41 | net = UNet(n_channels=3, n_classes=1) 42 | print(net) 43 | -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-1/unet_parts.py: -------------------------------------------------------------------------------- 1 | """ Parts of the U-Net model """ 2 | """https://github.com/milesial/Pytorch-UNet/blob/master/unet/unet_parts.py""" 3 | 4 | import torch 5 | import torch.nn as nn 6 | import torch.nn.functional as F 7 | 8 | 9 | class DoubleConv(nn.Module): 10 | """(convolution => [BN] => ReLU) * 2""" 11 | 12 | def __init__(self, in_channels, out_channels): 13 | super().__init__() 14 | self.double_conv = nn.Sequential( 15 | nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=0), 16 | nn.BatchNorm2d(out_channels), 17 | nn.ReLU(inplace=True), 18 | nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=0), 19 | nn.BatchNorm2d(out_channels), 20 | nn.ReLU(inplace=True) 21 | ) 22 | 23 | def forward(self, x): 24 | return self.double_conv(x) 25 | 26 | 27 | class Down(nn.Module): 28 | """Downscaling with maxpool then double conv""" 29 | 30 | def __init__(self, in_channels, out_channels): 31 | super().__init__() 32 | self.maxpool_conv = nn.Sequential( 33 | nn.MaxPool2d(2), 34 | DoubleConv(in_channels, out_channels) 35 | ) 36 | 37 | def forward(self, x): 38 | return self.maxpool_conv(x) 39 | 40 | 41 | class Up(nn.Module): 42 | """Upscaling then double conv""" 43 | 44 | def __init__(self, in_channels, out_channels, bilinear=True): 45 | super().__init__() 46 | 47 | # if bilinear, use the normal convolutions to reduce the number of channels 48 | if bilinear: 49 | self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True) 50 | else: 51 | self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=2, stride=2) 52 | 53 | self.conv = DoubleConv(in_channels, out_channels) 54 | 55 | def forward(self, x1, x2): 56 | x1 = self.up(x1) 57 | # input is CHW 58 | diffY = torch.tensor([x2.size()[2] - x1.size()[2]]) 59 | diffX = torch.tensor([x2.size()[3] - x1.size()[3]]) 60 | 61 | x1 = F.pad(x1, [diffX // 2, diffX - diffX // 2, 62 | diffY // 2, diffY - diffY // 2]) 63 | # if you have padding issues, see 64 | # https://github.com/HaiyongJiang/U-Net-Pytorch-Unstructured-Buggy/commit/0e854509c2cea854e247a9c615f175f76fbb2e3a 65 | # https://github.com/xiaopeng-liao/Pytorch-UNet/commit/8ebac70e633bac59fc22bb5195e513d5832fb3bd 66 | x = torch.cat([x2, x1], dim=1) 67 | return self.conv(x) 68 | 69 | 70 | class OutConv(nn.Module): 71 | def __init__(self, in_channels, out_channels): 72 | super(OutConv, self).__init__() 73 | self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1) 74 | 75 | def forward(self, x): 76 | return self.conv(x) 77 | -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/best_model.pth: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/best_model.pth -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/0.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/1.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/10.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/11.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/12.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/13.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/14.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/15.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/16.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/17.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/18.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/19.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/2.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/20.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/21.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/22.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/23.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/24.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/25.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/26.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/27.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/28.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/29.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/3.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/4.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/5.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/6.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/7.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/8.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/test/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/test/9.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/0.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/1.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/10.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/11.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/12.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/13.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/14.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/15.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/16.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/17.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/18.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/19.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/2.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/20.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/21.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/22.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/23.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/24.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/25.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/26.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/27.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/28.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/29.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/3.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/4.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/5.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/6.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/7.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/8.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/image/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/image/9.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/0.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/1.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/10.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/11.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/12.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/13.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/14.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/15.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/16.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/17.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/18.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/19.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/2.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/20.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/21.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/22.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/23.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/24.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/25.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/26.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/27.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/28.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/29.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/3.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/4.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/5.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/6.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/7.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/8.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/data/train/label/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/data/train/label/9.png -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/model/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/model/__init__.py -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/model/unet_model.py: -------------------------------------------------------------------------------- 1 | """ Full assembly of the parts to form the complete network """ 2 | """Refer https://github.com/milesial/Pytorch-UNet/blob/master/unet/unet_model.py""" 3 | 4 | import torch.nn.functional as F 5 | 6 | from .unet_parts import * 7 | 8 | 9 | class UNet(nn.Module): 10 | def __init__(self, n_channels, n_classes, bilinear=True): 11 | super(UNet, self).__init__() 12 | self.n_channels = n_channels 13 | self.n_classes = n_classes 14 | self.bilinear = bilinear 15 | 16 | self.inc = DoubleConv(n_channels, 64) 17 | self.down1 = Down(64, 128) 18 | self.down2 = Down(128, 256) 19 | self.down3 = Down(256, 512) 20 | self.down4 = Down(512, 512) 21 | self.up1 = Up(1024, 256, bilinear) 22 | self.up2 = Up(512, 128, bilinear) 23 | self.up3 = Up(256, 64, bilinear) 24 | self.up4 = Up(128, 64, bilinear) 25 | self.outc = OutConv(64, n_classes) 26 | 27 | def forward(self, x): 28 | x1 = self.inc(x) 29 | x2 = self.down1(x1) 30 | x3 = self.down2(x2) 31 | x4 = self.down3(x3) 32 | x5 = self.down4(x4) 33 | x = self.up1(x5, x4) 34 | x = self.up2(x, x3) 35 | x = self.up3(x, x2) 36 | x = self.up4(x, x1) 37 | logits = self.outc(x) 38 | return logits 39 | 40 | if __name__ == '__main__': 41 | net = UNet(n_channels=3, n_classes=1) 42 | print(net) -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/model/unet_parts.py: -------------------------------------------------------------------------------- 1 | """ Parts of the U-Net model """ 2 | """https://github.com/milesial/Pytorch-UNet/blob/master/unet/unet_parts.py""" 3 | 4 | import torch 5 | import torch.nn as nn 6 | import torch.nn.functional as F 7 | 8 | 9 | class DoubleConv(nn.Module): 10 | """(convolution => [BN] => ReLU) * 2""" 11 | 12 | def __init__(self, in_channels, out_channels): 13 | super().__init__() 14 | self.double_conv = nn.Sequential( 15 | nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1), 16 | nn.BatchNorm2d(out_channels), 17 | nn.ReLU(inplace=True), 18 | nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1), 19 | nn.BatchNorm2d(out_channels), 20 | nn.ReLU(inplace=True) 21 | ) 22 | 23 | def forward(self, x): 24 | return self.double_conv(x) 25 | 26 | 27 | class Down(nn.Module): 28 | """Downscaling with maxpool then double conv""" 29 | 30 | def __init__(self, in_channels, out_channels): 31 | super().__init__() 32 | self.maxpool_conv = nn.Sequential( 33 | nn.MaxPool2d(2), 34 | DoubleConv(in_channels, out_channels) 35 | ) 36 | 37 | def forward(self, x): 38 | return self.maxpool_conv(x) 39 | 40 | 41 | class Up(nn.Module): 42 | """Upscaling then double conv""" 43 | 44 | def __init__(self, in_channels, out_channels, bilinear=True): 45 | super().__init__() 46 | 47 | # if bilinear, use the normal convolutions to reduce the number of channels 48 | if bilinear: 49 | self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True) 50 | else: 51 | self.up = nn.ConvTranspose2d(in_channels // 2, in_channels // 2, kernel_size=2, stride=2) 52 | 53 | self.conv = DoubleConv(in_channels, out_channels) 54 | 55 | def forward(self, x1, x2): 56 | x1 = self.up(x1) 57 | # input is CHW 58 | diffY = torch.tensor([x2.size()[2] - x1.size()[2]]) 59 | diffX = torch.tensor([x2.size()[3] - x1.size()[3]]) 60 | 61 | x1 = F.pad(x1, [diffX // 2, diffX - diffX // 2, 62 | diffY // 2, diffY - diffY // 2]) 63 | 64 | x = torch.cat([x2, x1], dim=1) 65 | return self.conv(x) 66 | 67 | 68 | class OutConv(nn.Module): 69 | def __init__(self, in_channels, out_channels): 70 | super(OutConv, self).__init__() 71 | self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1) 72 | 73 | def forward(self, x): 74 | return self.conv(x) -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/predict.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import numpy as np 3 | import torch 4 | import os 5 | import cv2 6 | from model.unet_model import UNet 7 | 8 | if __name__ == "__main__": 9 | # 选择设备,有cuda用cuda,没有就用cpu 10 | device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 11 | # 加载网络,图片单通道,分类为1。 12 | net = UNet(n_channels=1, n_classes=1) 13 | # 将网络拷贝到deivce中 14 | net.to(device=device) 15 | # 加载模型参数 16 | net.load_state_dict(torch.load('best_model.pth', map_location=device)) 17 | # 测试模式 18 | net.eval() 19 | # 读取所有图片路径 20 | tests_path = glob.glob('data/test/*.png') 21 | # 遍历素有图片 22 | for test_path in tests_path: 23 | # 保存结果地址 24 | save_res_path = test_path.split('.')[0] + '_res.png' 25 | # 读取图片 26 | img = cv2.imread(test_path) 27 | # 转为灰度图 28 | img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) 29 | # 转为batch为1,通道为1,大小为512*512的数组 30 | img = img.reshape(1, 1, img.shape[0], img.shape[1]) 31 | # 转为tensor 32 | img_tensor = torch.from_numpy(img) 33 | # 将tensor拷贝到device中,只用cpu就是拷贝到cpu中,用cuda就是拷贝到cuda中。 34 | img_tensor = img_tensor.to(device=device, dtype=torch.float32) 35 | # 预测 36 | pred = net(img_tensor) 37 | # 提取结果 38 | pred = np.array(pred.data.cpu()[0])[0] 39 | # 处理结果 40 | pred[pred >= 0.5] = 255 41 | pred[pred < 0.5] = 0 42 | # 保存图片 43 | cv2.imwrite(save_res_path, pred) -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/train.py: -------------------------------------------------------------------------------- 1 | from model.unet_model import UNet 2 | from utils.dataset import ISBI_Loader 3 | from torch import optim 4 | import torch.nn as nn 5 | import torch 6 | 7 | def train_net(net, device, data_path, epochs=40, batch_size=1, lr=0.00001): 8 | # 加载训练集 9 | isbi_dataset = ISBI_Loader(data_path) 10 | train_loader = torch.utils.data.DataLoader(dataset=isbi_dataset, 11 | batch_size=batch_size, 12 | shuffle=True) 13 | # 定义RMSprop算法 14 | optimizer = optim.RMSprop(net.parameters(), lr=lr, weight_decay=1e-8, momentum=0.9) 15 | # 定义Loss算法 16 | criterion = nn.BCEWithLogitsLoss() 17 | # best_loss统计,初始化为正无穷 18 | best_loss = float('inf') 19 | # 训练epochs次 20 | for epoch in range(epochs): 21 | # 训练模式 22 | net.train() 23 | # 按照batch_size开始训练 24 | for image, label in train_loader: 25 | optimizer.zero_grad() 26 | # 将数据拷贝到device中 27 | image = image.to(device=device, dtype=torch.float32) 28 | label = label.to(device=device, dtype=torch.float32) 29 | # 使用网络参数,输出预测结果 30 | pred = net(image) 31 | # 计算loss 32 | loss = criterion(pred, label) 33 | print('Loss/train', loss.item()) 34 | # 保存loss值最小的网络参数 35 | if loss < best_loss: 36 | best_loss = loss 37 | torch.save(net.state_dict(), 'best_model.pth') 38 | # 更新参数 39 | loss.backward() 40 | optimizer.step() 41 | 42 | if __name__ == "__main__": 43 | # 选择设备,有cuda用cuda,没有就用cpu 44 | device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 45 | # 加载网络,图片单通道1,分类为1。 46 | net = UNet(n_channels=1, n_classes=1) 47 | # 将网络拷贝到deivce中 48 | net.to(device=device) 49 | # 指定训练集地址,开始训练 50 | data_path = "data/train/" 51 | train_net(net, device, data_path) -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/utils/__pycache__/dataset.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/5fd254b61ad45367fbae28c49976e82b14ff7110/Pytorch-Seg/lesson-2/utils/__pycache__/dataset.cpython-37.pyc -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-2/utils/dataset.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import cv2 3 | import os 4 | import glob 5 | from torch.utils.data import Dataset 6 | import random 7 | 8 | class ISBI_Loader(Dataset): 9 | def __init__(self, data_path): 10 | # 初始化函数,读取所有data_path下的图片 11 | self.data_path = data_path 12 | self.imgs_path = glob.glob(os.path.join(data_path, 'image/*.png')) 13 | 14 | def augment(self, image, flipCode): 15 | # 使用cv2.flip进行数据增强,filpCode为1水平翻转,0垂直翻转,-1水平+垂直翻转 16 | flip = cv2.flip(image, flipCode) 17 | return flip 18 | 19 | def __getitem__(self, index): 20 | # 根据index读取图片 21 | image_path = self.imgs_path[index] 22 | # 根据image_path生成label_path 23 | label_path = image_path.replace('image', 'label') 24 | # 读取训练图片和标签图片 25 | image = cv2.imread(image_path) 26 | label = cv2.imread(label_path) 27 | # 将数据转为单通道的图片 28 | image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 29 | label = cv2.cvtColor(label, cv2.COLOR_BGR2GRAY) 30 | image = image.reshape(1, image.shape[0], image.shape[1]) 31 | label = label.reshape(1, label.shape[0], label.shape[1]) 32 | # 处理标签,将像素值为255的改为1 33 | if label.max() > 1: 34 | label = label / 255 35 | # 随机进行数据增强,为2时不做处理 36 | flipCode = random.choice([-1, 0, 1, 2]) 37 | if flipCode != 2: 38 | image = self.augment(image, flipCode) 39 | label = self.augment(label, flipCode) 40 | return image, label 41 | 42 | def __len__(self): 43 | # 返回训练集大小 44 | return len(self.imgs_path) 45 | 46 | 47 | if __name__ == "__main__": 48 | isbi_dataset = ISBI_Loader("data/train/") 49 | print("数据个数:", len(isbi_dataset)) 50 | train_loader = torch.utils.data.DataLoader(dataset=isbi_dataset, 51 | batch_size=2, 52 | shuffle=True) 53 | for image, label in train_loader: 54 | print(image.shape) -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-3/log.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | class Logger(): 4 | def __init__(self, filename="log.txt"): 5 | self.terminal = sys.stdout 6 | self.log = open(filename, "w") 7 | 8 | def write(self, message): 9 | self.terminal.write(message) 10 | self.log.write(message) 11 | 12 | def flush(self): 13 | pass 14 | 15 | sys.stdout = Logger() 16 | 17 | print("Jack Cui") 18 | print("https://cuijiahua.com") 19 | print("https://mp.weixin.qq.com/s/OCWwRVDFNslIuKyiCVUoTA") 20 | -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-3/logger.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | def get_logger(LEVEL, log_file = None): 4 | head = '[%(asctime)-15s] [%(levelname)s] %(message)s' 5 | if LEVEL == 'info': 6 | logging.basicConfig(level=logging.INFO, format=head) 7 | elif LEVEL == 'debug': 8 | logging.basicConfig(level=logging.DEBUG, format=head) 9 | logger = logging.getLogger() 10 | if log_file != None: 11 | fh = logging.FileHandler(log_file) 12 | logger.addHandler(fh) 13 | return logger 14 | 15 | logger = get_logger('info') 16 | 17 | logger.info('Jack Cui') 18 | logger.info('https://cuijiahua.com') 19 | logger.info('https://mp.weixin.qq.com/s/OCWwRVDFNslIuKyiCVUoTA') -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-3/show_loss.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | # Jupyter notebook 中开启 3 | # %matplotlib inline 4 | with open('train_loss.txt', 'r') as f: 5 | train_loss = f.readlines() 6 | train_loss = list(map(lambda x:float(x.strip()), train_loss)) 7 | x = range(len(train_loss)) 8 | y = train_loss 9 | plt.plot(x, y, label='train loss', linewidth=2, color='r', marker='o', markerfacecolor='r', markersize=5) 10 | plt.xlabel('Epoch') 11 | plt.ylabel('Loss Value') 12 | plt.legend() 13 | plt.show() -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-3/tensorboardX_test.py: -------------------------------------------------------------------------------- 1 | from tensorboardX import SummaryWriter 2 | from urllib.request import urlretrieve 3 | import cv2 4 | 5 | # 选择运行那个示例 6 | choose_example = 1 7 | 8 | if choose_example == 1: 9 | """ 10 | Example 1:创建 writer 示例 11 | """ 12 | # 创建 writer1 对象 13 | # log 会保存到 runs/exp 文件夹中 14 | writer1 = SummaryWriter('runs/exp') 15 | 16 | # 使用默认参数创建 writer2 对象 17 | # log 会保存到 runs/日期_用户名 格式的文件夹中 18 | writer2 = SummaryWriter() 19 | 20 | # 使用 commet 参数,创建 writer3 对象 21 | # log 会保存到 runs/日期_用户名_resnet 格式的文件中 22 | writer3 = SummaryWriter(comment='_resnet') 23 | 24 | if choose_example == 2: 25 | """ 26 | Example 2:写入数字示例 27 | """ 28 | writer = SummaryWriter('runs/scalar_example') 29 | for i in range(10): 30 | writer.add_scalar('quadratic', i**2, global_step=i) 31 | writer.add_scalar('exponential', 2**i, global_step=i) 32 | writer.close() 33 | 34 | if choose_example == 3: 35 | """ 36 | Example 3:写入图片示例 37 | """ 38 | urlretrieve(url = 'https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/master/Pytorch-Seg/lesson-2/data/train/label/0.png',filename = '1.jpg') 39 | urlretrieve(url = 'https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/master/Pytorch-Seg/lesson-2/data/train/label/1.png',filename = '2.jpg') 40 | urlretrieve(url = 'https://raw.githubusercontent.com/Jack-Cherish/Deep-Learning/master/Pytorch-Seg/lesson-2/data/train/label/2.png',filename = '3.jpg') 41 | 42 | writer = SummaryWriter('runs/image_example') 43 | for i in range(1, 4): 44 | writer.add_image('UNet_Seg', 45 | cv2.cvtColor(cv2.imread('{}.jpg'.format(i)), cv2.COLOR_BGR2RGB), 46 | global_step=i, 47 | dataformats='HWC') 48 | writer.close() -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-3/train_loss.txt: -------------------------------------------------------------------------------- 1 | 32.464943 2 | 17.410048 3 | 16.051996 4 | 15.255561 5 | 14.547606 6 | 13.847259 7 | 13.449913 8 | 12.980925 9 | 12.948830 10 | 12.398992 11 | 12.485570 12 | 12.089432 13 | 12.118484 14 | 12.021638 15 | 11.817263 16 | 11.644099 17 | 11.417233 18 | 11.549695 19 | 11.223548 20 | 11.172435 21 | 11.027787 22 | 10.939758 23 | 10.666803 24 | 10.993714 25 | 10.574224 26 | 10.658235 27 | 10.631421 28 | 10.498351 29 | 10.557507 30 | 10.502128 31 | 10.543790 32 | 10.523225 33 | 10.231854 34 | 10.398646 35 | 10.406532 36 | 10.283625 37 | 10.105809 38 | 9.987217 39 | 9.936296 40 | 9.876533 41 | 9.953513 42 | 9.899665 43 | 9.926085 44 | 9.877600 45 | 9.829120 46 | 9.865887 47 | 9.770892 48 | 9.576312 49 | 9.615096 50 | 9.722373 51 | 9.715674 52 | 9.644127 53 | 9.581133 54 | 9.565999 55 | 9.459929 56 | 9.518677 57 | 9.321252 58 | 9.382160 59 | 9.545680 60 | 8.467113 61 | 8.369641 62 | 8.301431 63 | 8.306873 64 | 8.244370 65 | 8.223052 66 | 8.215305 67 | 8.191195 68 | 8.174629 69 | 8.184194 70 | 8.139848 71 | 8.143331 72 | 8.107319 73 | 8.110783 74 | 8.083336 75 | 8.056860 76 | 8.053325 77 | 8.038514 78 | 8.047304 79 | 8.027021 80 | 7.909974 81 | 7.896411 82 | 7.891089 83 | 7.892738 84 | 7.902834 85 | 7.896441 86 | 7.903152 87 | 7.878296 88 | 7.888803 89 | 7.879333 90 | 7.881098 91 | 7.868000 92 | 7.871295 93 | 7.887029 94 | 7.880289 95 | 7.863110 96 | 7.889467 97 | 7.876264 98 | 7.871953 99 | 7.869154 100 | 7.860284 -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-4/dataset.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from PIL import Image 3 | import os 4 | import glob 5 | from torch.utils.data import Dataset 6 | import random 7 | import torchvision.transforms as transforms 8 | from PIL import ImageFile 9 | ImageFile.LOAD_TRUNCATED_IMAGES = True 10 | 11 | class Garbage_Loader(Dataset): 12 | def __init__(self, txt_path, train_flag=True): 13 | self.imgs_info = self.get_images(txt_path) 14 | self.train_flag = train_flag 15 | 16 | self.train_tf = transforms.Compose([ 17 | transforms.Resize(224), 18 | transforms.RandomHorizontalFlip(), 19 | transforms.RandomVerticalFlip(), 20 | transforms.ToTensor(), 21 | 22 | ]) 23 | self.val_tf = transforms.Compose([ 24 | transforms.Resize(224), 25 | transforms.ToTensor(), 26 | ]) 27 | 28 | def get_images(self, txt_path): 29 | with open(txt_path, 'r', encoding='utf-8') as f: 30 | imgs_info = f.readlines() 31 | imgs_info = list(map(lambda x:x.strip().split('\t'), imgs_info)) 32 | return imgs_info 33 | 34 | def padding_black(self, img): 35 | 36 | w, h = img.size 37 | 38 | scale = 224. / max(w, h) 39 | img_fg = img.resize([int(x) for x in [w * scale, h * scale]]) 40 | 41 | size_fg = img_fg.size 42 | size_bg = 224 43 | 44 | img_bg = Image.new("RGB", (size_bg, size_bg)) 45 | 46 | img_bg.paste(img_fg, ((size_bg - size_fg[0]) // 2, 47 | (size_bg - size_fg[1]) // 2)) 48 | 49 | img = img_bg 50 | return img 51 | 52 | def __getitem__(self, index): 53 | img_path, label = self.imgs_info[index] 54 | img = Image.open(img_path) 55 | img = img.convert('RGB') 56 | img = self.padding_black(img) 57 | if self.train_flag: 58 | img = self.train_tf(img) 59 | else: 60 | img = self.val_tf(img) 61 | label = int(label) 62 | 63 | return img, label 64 | 65 | def __len__(self): 66 | return len(self.imgs_info) 67 | 68 | 69 | if __name__ == "__main__": 70 | train_dataset = Garbage_Loader("train.txt", True) 71 | print("数据个数:", len(train_dataset)) 72 | train_loader = torch.utils.data.DataLoader(dataset=train_dataset, 73 | batch_size=1, 74 | shuffle=True) 75 | for image, label in train_loader: 76 | print(image.shape) 77 | print(label) -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-4/dir_label.txt: -------------------------------------------------------------------------------- 1 | 其他垃圾_PE塑料袋 0 0 2 | 其他垃圾_U型回形针 1 0 3 | 其他垃圾_一次性杯子 2 0 4 | 其他垃圾_一次性棉签 3 0 5 | 其他垃圾_串串竹签 4 0 6 | 其他垃圾_便利贴 5 0 7 | 其他垃圾_创可贴 6 0 8 | 其他垃圾_厨房手套 7 0 9 | 其他垃圾_口罩 8 0 10 | 其他垃圾_唱片 9 0 11 | 其他垃圾_图钉 10 0 12 | 其他垃圾_大龙虾头 11 0 13 | 其他垃圾_奶茶杯 12 0 14 | 其他垃圾_干果壳 13 0 15 | 其他垃圾_干燥剂 14 0 16 | 其他垃圾_打泡网 15 0 17 | 其他垃圾_打火机 16 0 18 | 其他垃圾_放大镜 17 0 19 | 其他垃圾_毛巾 18 0 20 | 其他垃圾_涂改带 19 0 21 | 其他垃圾_湿纸巾 20 0 22 | 其他垃圾_烟蒂 21 0 23 | 其他垃圾_牙刷 22 0 24 | 其他垃圾_百洁布 23 0 25 | 其他垃圾_眼镜 24 0 26 | 其他垃圾_票据 25 0 27 | 其他垃圾_空调滤芯 26 0 28 | 其他垃圾_笔及笔芯 27 0 29 | 其他垃圾_纸巾 28 0 30 | 其他垃圾_胶带 29 0 31 | 其他垃圾_胶水废包装 30 0 32 | 其他垃圾_苍蝇拍 31 0 33 | 其他垃圾_茶壶碎片 32 0 34 | 其他垃圾_餐盒 33 0 35 | 其他垃圾_验孕棒 34 0 36 | 其他垃圾_鸡毛掸 35 0 37 | 厨余垃圾_八宝粥 36 1 38 | 厨余垃圾_冰糖葫芦 37 1 39 | 厨余垃圾_咖啡渣 38 1 40 | 厨余垃圾_哈密瓜 39 1 41 | 厨余垃圾_圣女果 40 1 42 | 厨余垃圾_巴旦木 41 1 43 | 厨余垃圾_开心果 42 1 44 | 厨余垃圾_普通面包 43 1 45 | 厨余垃圾_板栗 44 1 46 | 厨余垃圾_果冻 45 1 47 | 厨余垃圾_核桃 46 1 48 | 厨余垃圾_梨 47 1 49 | 厨余垃圾_橙子 48 1 50 | 厨余垃圾_残渣剩饭 49 1 51 | 厨余垃圾_汉堡 50 1 52 | 厨余垃圾_火龙果 51 1 53 | 厨余垃圾_炸鸡 52 1 54 | 厨余垃圾_烤鸡烤鸭 53 1 55 | 厨余垃圾_牛肉干 54 1 56 | 厨余垃圾_瓜子 55 1 57 | 厨余垃圾_甘蔗 56 1 58 | 厨余垃圾_生肉 57 1 59 | 厨余垃圾_番茄 58 1 60 | 厨余垃圾_白菜 59 1 61 | 厨余垃圾_白萝卜 60 1 62 | 厨余垃圾_粉条 61 1 63 | 厨余垃圾_糕点 62 1 64 | 厨余垃圾_红豆 63 1 65 | 厨余垃圾_肠(火腿) 64 1 66 | 厨余垃圾_胡萝卜 65 1 67 | 厨余垃圾_花生皮 66 1 68 | 厨余垃圾_苹果 67 1 69 | 厨余垃圾_茶叶 68 1 70 | 厨余垃圾_草莓 69 1 71 | 厨余垃圾_荷包蛋 70 1 72 | 厨余垃圾_菠萝 71 1 73 | 厨余垃圾_菠萝包 72 1 74 | 厨余垃圾_菠萝蜜 73 1 75 | 厨余垃圾_蒜 74 1 76 | 厨余垃圾_薯条 75 1 77 | 厨余垃圾_蘑菇 76 1 78 | 厨余垃圾_蚕豆 77 1 79 | 厨余垃圾_蛋 78 1 80 | 厨余垃圾_蛋挞 79 1 81 | 厨余垃圾_西瓜皮 80 1 82 | 厨余垃圾_贝果 81 1 83 | 厨余垃圾_辣椒 82 1 84 | 厨余垃圾_陈皮 83 1 85 | 厨余垃圾_青菜 84 1 86 | 厨余垃圾_饼干 85 1 87 | 厨余垃圾_香蕉皮 86 1 88 | 厨余垃圾_骨肉相连 87 1 89 | 厨余垃圾_鸡翅 88 1 90 | 可回收物_乒乓球拍 89 2 91 | 可回收物_书 90 2 92 | 可回收物_保温杯 91 2 93 | 可回收物_保鲜盒 92 2 94 | 可回收物_信封 93 2 95 | 可回收物_充电头 94 2 96 | 可回收物_充电宝 95 2 97 | 可回收物_充电线 96 2 98 | 可回收物_八宝粥罐 97 2 99 | 可回收物_刀 98 2 100 | 可回收物_剃须刀片 99 2 101 | 可回收物_剪刀 100 2 102 | 可回收物_勺子 101 2 103 | 可回收物_单肩包手提包 102 2 104 | 可回收物_卡 103 2 105 | 可回收物_叉子 104 2 106 | 可回收物_变形玩具 105 2 107 | 可回收物_台历 106 2 108 | 可回收物_台灯 107 2 109 | 可回收物_吹风机 108 2 110 | 可回收物_呼啦圈 109 2 111 | 可回收物_地球仪 110 2 112 | 可回收物_地铁票 111 2 113 | 可回收物_垫子 112 2 114 | 可回收物_塑料瓶 113 2 115 | 可回收物_塑料盆 114 2 116 | 可回收物_奶盒 115 2 117 | 可回收物_奶粉罐 116 2 118 | 可回收物_奶粉罐铝盖 117 2 119 | 可回收物_尺子 118 2 120 | 可回收物_帽子 119 2 121 | 可回收物_废弃扩声器 120 2 122 | 可回收物_手提包 121 2 123 | 可回收物_手机 122 2 124 | 可回收物_手电筒 123 2 125 | 可回收物_手链 124 2 126 | 可回收物_打印机墨盒 125 2 127 | 可回收物_打气筒 126 2 128 | 可回收物_护肤品空瓶 127 2 129 | 可回收物_报纸 128 2 130 | 可回收物_拖鞋 129 2 131 | 可回收物_插线板 130 2 132 | 可回收物_搓衣板 131 2 133 | 可回收物_收音机 132 2 134 | 可回收物_放大镜 133 2 135 | 可回收物_易拉罐 134 2 136 | 可回收物_暖宝宝 135 2 137 | 可回收物_望远镜 136 2 138 | 可回收物_木制切菜板 137 2 139 | 可回收物_木制玩具 138 2 140 | 可回收物_木质梳子 139 2 141 | 可回收物_木质锅铲 140 2 142 | 可回收物_枕头 141 2 143 | 可回收物_档案袋 142 2 144 | 可回收物_水杯 143 2 145 | 可回收物_泡沫盒子 144 2 146 | 可回收物_灯罩 145 2 147 | 可回收物_烟灰缸 146 2 148 | 可回收物_烧水壶 147 2 149 | 可回收物_热水瓶 148 2 150 | 可回收物_玩偶 149 2 151 | 可回收物_玻璃器皿 150 2 152 | 可回收物_玻璃壶 151 2 153 | 可回收物_玻璃球 152 2 154 | 可回收物_电动剃须刀 153 2 155 | 可回收物_电动卷发棒 154 2 156 | 可回收物_电动牙刷 155 2 157 | 可回收物_电熨斗 156 2 158 | 可回收物_电视遥控器 157 2 159 | 可回收物_电路板 158 2 160 | 可回收物_登机牌 159 2 161 | 可回收物_盘子 160 2 162 | 可回收物_碗 161 2 163 | 可回收物_空气加湿器 162 2 164 | 可回收物_空调遥控器 163 2 165 | 可回收物_纸牌 164 2 166 | 可回收物_纸箱 165 2 167 | 可回收物_罐头瓶 166 2 168 | 可回收物_网卡 167 2 169 | 可回收物_耳套 168 2 170 | 可回收物_耳机 169 2 171 | 可回收物_耳钉耳环 170 2 172 | 可回收物_芭比娃娃 171 2 173 | 可回收物_茶叶罐 172 2 174 | 可回收物_蛋糕盒 173 2 175 | 可回收物_螺丝刀 174 2 176 | 可回收物_衣架 175 2 177 | 可回收物_袜子 176 2 178 | 可回收物_裤子 177 2 179 | 可回收物_计算器 178 2 180 | 可回收物_订书机 179 2 181 | 可回收物_话筒 180 2 182 | 可回收物_购物纸袋 181 2 183 | 可回收物_路由器 182 2 184 | 可回收物_车钥匙 183 2 185 | 可回收物_量杯 184 2 186 | 可回收物_钉子 185 2 187 | 可回收物_钟表 186 2 188 | 可回收物_钢丝球 187 2 189 | 可回收物_锅 188 2 190 | 可回收物_锅盖 189 2 191 | 可回收物_键盘 190 2 192 | 可回收物_镊子 191 2 193 | 可回收物_鞋 192 2 194 | 可回收物_餐垫 193 2 195 | 可回收物_鼠标 194 2 196 | 有害垃圾_LED灯泡 195 3 197 | 有害垃圾_保健品瓶 196 3 198 | 有害垃圾_口服液瓶 197 3 199 | 有害垃圾_指甲油 198 3 200 | 有害垃圾_杀虫剂 199 3 201 | 有害垃圾_温度计 200 3 202 | 有害垃圾_滴眼液瓶 201 3 203 | 有害垃圾_玻璃灯管 202 3 204 | 有害垃圾_电池 203 3 205 | 有害垃圾_电池板 204 3 206 | 有害垃圾_碘伏空瓶 205 3 207 | 有害垃圾_红花油 206 3 208 | 有害垃圾_纽扣电池 207 3 209 | 有害垃圾_胶水 208 3 210 | 有害垃圾_药品包装 209 3 211 | 有害垃圾_药片 210 3 212 | 有害垃圾_药膏 211 3 213 | 有害垃圾_蓄电池 212 3 214 | 有害垃圾_血压计 213 3 215 | -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-4/infer.py: -------------------------------------------------------------------------------- 1 | from dataset import Garbage_Loader 2 | from torch.utils.data import DataLoader 3 | import torchvision.transforms as transforms 4 | from torchvision import models 5 | import torch.nn as nn 6 | import torch 7 | import os 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | #%matplotlib inline 11 | os.environ["CUDA_VISIBLE_DEVICES"] = "0" 12 | 13 | def softmax(x): 14 | exp_x = np.exp(x) 15 | softmax_x = exp_x / np.sum(exp_x, 0) 16 | return softmax_x 17 | 18 | with open('dir_label.txt', 'r', encoding='utf-8') as f: 19 | labels = f.readlines() 20 | labels = list(map(lambda x:x.strip().split('\t'), labels)) 21 | 22 | if __name__ == "__main__": 23 | test_list = 'test.txt' 24 | test_data = Garbage_Loader(test_list, train_flag=False) 25 | test_loader = DataLoader(dataset=test_data, num_workers=1, pin_memory=True, batch_size=1) 26 | model = models.resnet50(pretrained=False) 27 | fc_inputs = model.fc.in_features 28 | model.fc = nn.Linear(fc_inputs, 214) 29 | model = model.cuda() 30 | # 加载训练好的模型 31 | checkpoint = torch.load('model_best_checkpoint_resnet50.pth.tar') 32 | model.load_state_dict(checkpoint['state_dict']) 33 | model.eval() 34 | for i, (image, label) in enumerate(test_loader): 35 | src = image.numpy() 36 | src = src.reshape(3, 224, 224) 37 | src = np.transpose(src, (1, 2, 0)) 38 | image = image.cuda() 39 | label = label.cuda() 40 | pred = model(image) 41 | pred = pred.data.cpu().numpy()[0] 42 | score = softmax(pred) 43 | pred_id = np.argmax(score) 44 | plt.imshow(src) 45 | print('预测结果:', labels[pred_id][0]) 46 | plt.show() 47 | -------------------------------------------------------------------------------- /Pytorch-Seg/lesson-4/train.py: -------------------------------------------------------------------------------- 1 | from dataset import Garbage_Loader 2 | from torch.utils.data import DataLoader 3 | from torchvision import models 4 | import torch.nn as nn 5 | import torch.optim as optim 6 | import torch 7 | import time 8 | import os 9 | import shutil 10 | os.environ["CUDA_VISIBLE_DEVICES"] = "0" 11 | 12 | """ 13 | Author : Jack Cui 14 | Wechat : https://mp.weixin.qq.com/s/OCWwRVDFNslIuKyiCVUoTA 15 | """ 16 | 17 | from tensorboardX import SummaryWriter 18 | 19 | def accuracy(output, target, topk=(1,)): 20 | """ 21 | 计算topk的准确率 22 | """ 23 | with torch.no_grad(): 24 | maxk = max(topk) 25 | batch_size = target.size(0) 26 | 27 | _, pred = output.topk(maxk, 1, True, True) 28 | pred = pred.t() 29 | correct = pred.eq(target.view(1, -1).expand_as(pred)) 30 | 31 | class_to = pred[0].cpu().numpy() 32 | 33 | res = [] 34 | for k in topk: 35 | correct_k = correct[:k].view(-1).float().sum(0, keepdim=True) 36 | res.append(correct_k.mul_(100.0 / batch_size)) 37 | return res, class_to 38 | 39 | def save_checkpoint(state, is_best, filename='checkpoint.pth.tar'): 40 | """ 41 | 根据 is_best 存模型,一般保存 valid acc 最好的模型 42 | """ 43 | torch.save(state, filename) 44 | if is_best: 45 | shutil.copyfile(filename, 'model_best_' + filename) 46 | 47 | def train(train_loader, model, criterion, optimizer, epoch, writer): 48 | """ 49 | 训练代码 50 | 参数: 51 | train_loader - 训练集的 DataLoader 52 | model - 模型 53 | criterion - 损失函数 54 | optimizer - 优化器 55 | epoch - 进行第几个 epoch 56 | writer - 用于写 tensorboardX 57 | """ 58 | batch_time = AverageMeter() 59 | data_time = AverageMeter() 60 | losses = AverageMeter() 61 | top1 = AverageMeter() 62 | top5 = AverageMeter() 63 | 64 | # switch to train mode 65 | model.train() 66 | 67 | end = time.time() 68 | for i, (input, target) in enumerate(train_loader): 69 | # measure data loading time 70 | data_time.update(time.time() - end) 71 | 72 | input = input.cuda() 73 | target = target.cuda() 74 | 75 | # compute output 76 | output = model(input) 77 | loss = criterion(output, target) 78 | 79 | # measure accuracy and record loss 80 | [prec1, prec5], class_to = accuracy(output, target, topk=(1, 5)) 81 | losses.update(loss.item(), input.size(0)) 82 | top1.update(prec1[0], input.size(0)) 83 | top5.update(prec5[0], input.size(0)) 84 | 85 | # compute gradient and do SGD step 86 | optimizer.zero_grad() 87 | loss.backward() 88 | optimizer.step() 89 | 90 | # measure elapsed time 91 | batch_time.update(time.time() - end) 92 | end = time.time() 93 | 94 | if i % 10 == 0: 95 | print('Epoch: [{0}][{1}/{2}]\t' 96 | 'Time {batch_time.val:.3f} ({batch_time.avg:.3f})\t' 97 | 'Data {data_time.val:.3f} ({data_time.avg:.3f})\t' 98 | 'Loss {loss.val:.4f} ({loss.avg:.4f})\t' 99 | 'Prec@1 {top1.val:.3f} ({top1.avg:.3f})\t' 100 | 'Prec@5 {top5.val:.3f} ({top5.avg:.3f})'.format( 101 | epoch, i, len(train_loader), batch_time=batch_time, 102 | data_time=data_time, loss=losses, top1=top1, top5=top5)) 103 | writer.add_scalar('loss/train_loss', losses.val, global_step=epoch) 104 | 105 | def validate(val_loader, model, criterion, epoch, writer, phase="VAL"): 106 | """ 107 | 验证代码 108 | 参数: 109 | val_loader - 验证集的 DataLoader 110 | model - 模型 111 | criterion - 损失函数 112 | epoch - 进行第几个 epoch 113 | writer - 用于写 tensorboardX 114 | """ 115 | batch_time = AverageMeter() 116 | losses = AverageMeter() 117 | top1 = AverageMeter() 118 | top5 = AverageMeter() 119 | 120 | # switch to evaluate mode 121 | model.eval() 122 | 123 | with torch.no_grad(): 124 | end = time.time() 125 | for i, (input, target) in enumerate(val_loader): 126 | input = input.cuda() 127 | target = target.cuda() 128 | # compute output 129 | output = model(input) 130 | loss = criterion(output, target) 131 | 132 | # measure accuracy and record loss 133 | [prec1, prec5], class_to = accuracy(output, target, topk=(1, 5)) 134 | losses.update(loss.item(), input.size(0)) 135 | top1.update(prec1[0], input.size(0)) 136 | top5.update(prec5[0], input.size(0)) 137 | 138 | # measure elapsed time 139 | batch_time.update(time.time() - end) 140 | end = time.time() 141 | 142 | if i % 10 == 0: 143 | print('Test-{0}: [{1}/{2}]\t' 144 | 'Time {batch_time.val:.3f} ({batch_time.avg:.3f})\t' 145 | 'Loss {loss.val:.4f} ({loss.avg:.4f})\t' 146 | 'Prec@1 {top1.val:.3f} ({top1.avg:.3f})\t' 147 | 'Prec@5 {top5.val:.3f} ({top5.avg:.3f})'.format( 148 | phase, i, len(val_loader), 149 | batch_time=batch_time, 150 | loss=losses, 151 | top1=top1, top5=top5)) 152 | 153 | print(' * {} Prec@1 {top1.avg:.3f} Prec@5 {top5.avg:.3f}' 154 | .format(phase, top1=top1, top5=top5)) 155 | writer.add_scalar('loss/valid_loss', losses.val, global_step=epoch) 156 | return top1.avg, top5.avg 157 | 158 | class AverageMeter(object): 159 | """Computes and stores the average and current value""" 160 | def __init__(self): 161 | self.reset() 162 | 163 | def reset(self): 164 | self.val = 0 165 | self.avg = 0 166 | self.sum = 0 167 | self.count = 0 168 | 169 | def update(self, val, n=1): 170 | self.val = val 171 | self.sum += val * n 172 | self.count += n 173 | self.avg = self.sum / self.count 174 | 175 | if __name__ == "__main__": 176 | # -------------------------------------------- step 1/4 : 加载数据 --------------------------- 177 | train_dir_list = 'train.txt' 178 | valid_dir_list = 'val.txt' 179 | batch_size = 64 180 | epochs = 80 181 | num_classes = 214 182 | train_data = Garbage_Loader(train_dir_list, train_flag=True) 183 | valid_data = Garbage_Loader(valid_dir_list, train_flag=False) 184 | train_loader = DataLoader(dataset=train_data, num_workers=8, pin_memory=True, batch_size=batch_size, shuffle=True) 185 | valid_loader = DataLoader(dataset=valid_data, num_workers=8, pin_memory=True, batch_size=batch_size) 186 | train_data_size = len(train_data) 187 | print('训练集数量:%d' % train_data_size) 188 | valid_data_size = len(valid_data) 189 | print('验证集数量:%d' % valid_data_size) 190 | # ------------------------------------ step 2/4 : 定义网络 ------------------------------------ 191 | model = models.resnet50(pretrained=True) 192 | fc_inputs = model.fc.in_features 193 | model.fc = nn.Linear(fc_inputs, num_classes) 194 | model = model.cuda() 195 | # ------------------------------------ step 3/4 : 定义损失函数和优化器等 ------------------------- 196 | lr_init = 0.0001 197 | lr_stepsize = 20 198 | weight_decay = 0.001 199 | criterion = nn.CrossEntropyLoss().cuda() 200 | optimizer = optim.Adam(model.parameters(), lr=lr_init, weight_decay=weight_decay) 201 | scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=lr_stepsize, gamma=0.1) 202 | 203 | writer = SummaryWriter('runs/resnet50') 204 | # ------------------------------------ step 4/4 : 训练 ----------------------------------------- 205 | best_prec1 = 0 206 | for epoch in range(epochs): 207 | scheduler.step() 208 | train(train_loader, model, criterion, optimizer, epoch, writer) 209 | # 在验证集上测试效果 210 | valid_prec1, valid_prec5 = validate(valid_loader, model, criterion, epoch, writer, phase="VAL") 211 | is_best = valid_prec1 > best_prec1 212 | best_prec1 = max(valid_prec1, best_prec1) 213 | save_checkpoint({ 214 | 'epoch': epoch + 1, 215 | 'arch': 'resnet50', 216 | 'state_dict': model.state_dict(), 217 | 'best_prec1': best_prec1, 218 | 'optimizer' : optimizer.state_dict(), 219 | }, is_best, 220 | filename='checkpoint_resnet50.pth.tar') 221 | writer.close() 222 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deep-Learning 2 | 3 | * 贵有恒,何必三更起五更睡;最无益,只怕一日曝十日寒。
4 | 5 | 原创文章每周最少两篇,**后续最新文章**会在[【公众号】](https://cuijiahua.com/wp-content/uploads/2020/05/gzh-w.jpg)首发,视频[【B站】](https://space.bilibili.com/331507846)首发,大家可以加我[【微信】](https://cuijiahua.com/wp-content/uploads/2020/05/gzh-w.jpg)进**交流群**,技术交流或提意见都可以,欢迎**Star**! 6 | 7 |

8 | 微信群 9 | 公众号 10 | B站 11 | 知乎 12 | CSDN 13 | 头条 14 | 掘金 15 |

16 | 17 | ## Pytorch实战 18 | | 文章 | 公众号 | 代码 | 19 | | :------ | :--------: | :--------: | 20 | | Pytorch深度学习实战教程(一):语义分割基础与环境搭建 | [公众号](https://mp.weixin.qq.com/s/KI-9z7FBjfoWfZK3PEPXJA "Pytorch深度学习实战教程(一):语义分割基础与环境搭建") | no | 21 | | Pytorch深度学习实战教程(二):UNet语义分割网络 | [公众号](https://mp.weixin.qq.com/s/6tZVUbyEjLVewM8vGK9Zhw "Pytorch深度学习实战教程(二):UNet语义分割网络") | [Code](https://github.com/Jack-Cherish/Deep-Learning/tree/master/Pytorch-Seg/lesson-1 "Pytorch深度学习实战教程(二):UNet语义分割网络") | 22 | | Pytorch深度学习实战教程(三):UNet模型训练 | [公众号](https://mp.weixin.qq.com/s/7FY77k3xtK-UyfoXpFXgBQ "Pytorch深度学习实战教程(三):UNet模型训练") | [Code](https://github.com/Jack-Cherish/Deep-Learning/tree/master/Pytorch-Seg/lesson-2 "Pytorch深度学习实战教程(三):UNet模型训练") | 23 | | Pytorch深度学习实战教程(四):必知必会的炼丹法宝 | [公众号](https://mp.weixin.qq.com/s/RhvCeesbzAQmK0yv8D6LjQ "Pytorch深度学习实战教程(四):必知必会的炼丹法宝") | [Code](https://github.com/Jack-Cherish/Deep-Learning/tree/master/Pytorch-Seg/lesson-3 "Pytorch深度学习实战教程(四):必知必会的炼丹法宝") | 24 | | Pytorch深度学习实战教程(五):今天,你垃圾分类了吗 | [公众号](https://mp.weixin.qq.com/s/Yt2M-cjLdRgOowNNm2Ll8Q "Pytorch深度学习实战教程(五):今天,你垃圾分类了吗") | [Code](https://github.com/Jack-Cherish/Deep-Learning/tree/master/Pytorch-Seg/lesson-4 "Pytorch深度学习实战教程(五):今天,你垃圾分类了吗") | 25 | | Pytorch 深度学习实战教程(六):仝卓自爆,快本打码 | [公众号](https://mp.weixin.qq.com/s/za4DsGMcLZYCbTir9QFPKQ "Pytorch 深度学习实战教程(六):仝卓自爆,快本打码") | [Code](https://github.com/Jack-Cherish/Deep-Learning/blob/master/face/video_mosaic.py "Pytorch 深度学习实战教程(六):仝卓自爆,快本打码") | 26 | 更新ing... 27 | 28 | ## 深度学习实战教程 29 | | 文章 | 个人网站 | CSDN | 知乎 | 30 | | :------ | :--------: | :--------: | :--------: | 31 | | 深度学习实战教程(一) :感知器 | [个人网站](https://cuijiahua.com/blog/2018/10/dl-7.html "深度学习实战教程(一)") | no | no | 32 | | 深度学习实战教程(二):线性单元和梯度下降 | [个人网站](https://cuijiahua.com/blog/2018/11/dl-8.html "深度学习实战教程(二)") | no | no | 33 | | 深度学习实战教程(三):神经网络和反向传播算法 | [个人网站](https://cuijiahua.com/blog/2018/11/dl-9.html "深度学习实战教程(三)") | no | no | 34 | | 深度学习实战教程(四):卷积神经网络 | [个人网站](https://cuijiahua.com/blog/2018/12/dl-10.html "深度学习实战教程(四)") | no | no | 35 | | 深度学习实战教程(五):循环神经网络 | [个人网站](https://cuijiahua.com/blog/2018/12/dl-11.html "深度学习实战教程(五)") | no | no | 36 | | 深度学习实战教程(六):长短时记忆网络(LSTM) | [个人网站](https://cuijiahua.com/blog/2019/01/dl-12.html "深度学习实战教程(六)") | no | no | 37 | 38 | ## Tensorflow实战 39 | 40 | | 文章 | 个人网站 | CSDN | 知乎 | 41 | | :------ | :--------: | :--------: | :--------: | 42 | | Tensorflow实战(一):打响深度学习的第一枪 – 手写数字识别(Tensorboard可视化) | [个人网站](http://cuijiahua.com/blog/2018/01/dl_4.html "手写数字识别") | no | no | 43 | | Tensorflow实战(二):Discuz验证码识别 | [个人网站](http://cuijiahua.com/blog/2018/01/dl_5.html "Discuz验证码识别") | no | no | 44 | 45 | 46 | 更多精彩,敬请期待! 47 | 48 | 49 | 50 | wechat 51 | -------------------------------------------------------------------------------- /Tutorial/README.md: -------------------------------------------------------------------------------- 1 | ## 深度学习实战教程 2 | 3 | #### 说明 4 | 5 | * 该系列教程为[@hanbingtao](https://github.com/hanbt/learn_dl "悬停显示")所做,感觉很适合入门,顾转载到了个人网站。开源免费的好教程不多,望多多支持和推荐。 6 | 7 | * 可以到我的个人网站看该系列文章:[点击查看](https://cuijiahua.com/blog/dl/ "悬停显示") 8 | 9 | * 也可以到原作者的作业部落学习:[点击查看](https://www.zybuluo.com/hanbingtao/note/448086 "悬停显示") -------------------------------------------------------------------------------- /Tutorial/lesson-1/perceptron.py: -------------------------------------------------------------------------------- 1 | """ 2 | 作者:https://github.com/hanbt/learn_dl 3 | """ 4 | from functools import reduce 5 | 6 | class Perceptron(): 7 | def __init__(self, input_num, activator): 8 | ''' 9 | 初始化感知器,设置输入参数的个数,以及激活函数。 10 | 激活函数的类型为double -> double 11 | ''' 12 | self.activator = activator 13 | # 权重向量初始化为0 14 | self.weights = [0.0 for _ in range(input_num)] 15 | # 偏置项初始化为0 16 | self.bias = 0.0 17 | def __str__(self): 18 | ''' 19 | 打印学习到的权重、偏置项 20 | ''' 21 | return 'weights\t:%s\nbias\t:%f\n' % (self.weights, self.bias) 22 | def predict(self, input_vec): 23 | ''' 24 | 输入向量,输出感知器的计算结果 25 | ''' 26 | # 把input_vec[x1,x2,x3...]和weights[w1,w2,w3,...]打包在一起 27 | # 变成[(x1,w1),(x2,w2),(x3,w3),...] 28 | # 然后利用map函数计算[x1*w1, x2*w2, x3*w3] 29 | # 最后利用reduce求和 30 | return self.activator( 31 | reduce(lambda a, b: a + b,list(map(lambda x, w: x * w, input_vec, self.weights)), 0.0) + self.bias) 32 | def train(self, input_vecs, labels, iteration, rate): 33 | ''' 34 | 输入训练数据:一组向量、与每个向量对应的label;以及训练轮数、学习率 35 | ''' 36 | for i in range(iteration): 37 | self._one_iteration(input_vecs, labels, rate) 38 | def _one_iteration(self, input_vecs, labels, rate): 39 | ''' 40 | 一次迭代,把所有的训练数据过一遍 41 | ''' 42 | # 把输入和输出打包在一起,成为样本的列表[(input_vec, label), ...] 43 | # 而每个训练样本是(input_vec, label) 44 | samples = zip(input_vecs, labels) 45 | # 对每个样本,按照感知器规则更新权重 46 | for (input_vec, label) in samples: 47 | # 计算感知器在当前权重下的输出 48 | output = self.predict(input_vec) 49 | # 更新权重 50 | self._update_weights(input_vec, output, label, rate) 51 | def _update_weights(self, input_vec, output, label, rate): 52 | ''' 53 | 按照感知器规则更新权重 54 | ''' 55 | # 把input_vec[x1,x2,x3,...]和weights[w1,w2,w3,...]打包在一起 56 | # 变成[(x1,w1),(x2,w2),(x3,w3),...] 57 | # 然后利用感知器规则更新权重 58 | delta = label - output 59 | self.weights = list(map(lambda x, w: w + rate * delta * x, input_vec, self.weights)) 60 | # 更新bias 61 | self.bias += rate * delta 62 | 63 | def f(x): 64 | ''' 65 | 定义激活函数f 66 | ''' 67 | return 1 if x > 0 else 0 68 | def get_training_dataset(): 69 | ''' 70 | 基于and真值表构建训练数据 71 | ''' 72 | # 构建训练数据 73 | # 输入向量列表 74 | input_vecs = [[1,1], [0,0], [1,0], [0,1]] 75 | # 期望的输出列表,注意要与输入一一对应 76 | # [1,1] -> 1, [0,0] -> 0, [1,0] -> 0, [0,1] -> 0 77 | labels = [1, 0, 0, 0] 78 | return input_vecs, labels 79 | def train_and_perceptron(): 80 | ''' 81 | 使用and真值表训练感知器 82 | ''' 83 | # 创建感知器,输入参数个数为2(因为and是二元函数),激活函数为f 84 | p = Perceptron(2, f) 85 | # 训练,迭代10轮, 学习速率为0.1 86 | input_vecs, labels = get_training_dataset() 87 | p.train(input_vecs, labels, 10, 0.1) 88 | #返回训练好的感知器 89 | return p 90 | if __name__ == '__main__': 91 | # 训练and感知器 92 | and_perception = train_and_perceptron() 93 | # 打印训练获得的权重 94 | print(and_perception) 95 | # 测试 96 | print('1 and 1 = %d' % and_perception.predict([1, 1])) 97 | print('0 and 0 = %d' % and_perception.predict([0, 0])) 98 | print('1 and 0 = %d' % and_perception.predict([1, 0])) 99 | print('0 and 1 = %d' % and_perception.predict([0, 1])) -------------------------------------------------------------------------------- /Tutorial/lesson-2/linear_unit.py: -------------------------------------------------------------------------------- 1 | from perceptron import Perceptron 2 | #定义激活函数f 3 | f = lambda x: x 4 | class LinearUnit(Perceptron): 5 | def __init__(self, input_num): 6 | '''初始化线性单元,设置输入参数的个数''' 7 | Perceptron.__init__(self, input_num, f) 8 | 9 | def get_training_dataset(): 10 | ''' 11 | 捏造5个人的收入数据 12 | ''' 13 | # 构建训练数据 14 | # 输入向量列表,每一项是工作年限 15 | input_vecs = [[5], [3], [8], [1.4], [10.1]] 16 | # 期望的输出列表,月薪,注意要与输入一一对应 17 | labels = [5500, 2300, 7600, 1800, 11400] 18 | return input_vecs, labels 19 | def train_linear_unit(): 20 | ''' 21 | 使用数据训练线性单元 22 | ''' 23 | # 创建感知器,输入参数的特征数为1(工作年限) 24 | lu = LinearUnit(1) 25 | # 训练,迭代10轮, 学习速率为0.01 26 | input_vecs, labels = get_training_dataset() 27 | lu.train(input_vecs, labels, 10, 0.01) 28 | #返回训练好的线性单元 29 | return lu 30 | if __name__ == '__main__': 31 | '''训练线性单元''' 32 | linear_unit = train_linear_unit() 33 | # 打印训练获得的权重 34 | print(linear_unit) 35 | # 测试 36 | print('Work 3.4 years, monthly salary = %.2f' % linear_unit.predict([3.4])) 37 | print('Work 15 years, monthly salary = %.2f' % linear_unit.predict([15])) 38 | print('Work 1.5 years, monthly salary = %.2f' % linear_unit.predict([1.5])) 39 | print('Work 6.3 years, monthly salary = %.2f' % linear_unit.predict([6.3])) -------------------------------------------------------------------------------- /Tutorial/lesson-2/perceptron.py: -------------------------------------------------------------------------------- 1 | """ 2 | 作者:https://github.com/hanbt/learn_dl 3 | """ 4 | from functools import reduce 5 | 6 | class Perceptron(): 7 | def __init__(self, input_num, activator): 8 | ''' 9 | 初始化感知器,设置输入参数的个数,以及激活函数。 10 | 激活函数的类型为double -> double 11 | ''' 12 | self.activator = activator 13 | # 权重向量初始化为0 14 | self.weights = [0.0 for _ in range(input_num)] 15 | # 偏置项初始化为0 16 | self.bias = 0.0 17 | def __str__(self): 18 | ''' 19 | 打印学习到的权重、偏置项 20 | ''' 21 | return 'weights\t:%s\nbias\t:%f\n' % (self.weights, self.bias) 22 | def predict(self, input_vec): 23 | ''' 24 | 输入向量,输出感知器的计算结果 25 | ''' 26 | # 把input_vec[x1,x2,x3...]和weights[w1,w2,w3,...]打包在一起 27 | # 变成[(x1,w1),(x2,w2),(x3,w3),...] 28 | # 然后利用map函数计算[x1*w1, x2*w2, x3*w3] 29 | # 最后利用reduce求和 30 | return self.activator( 31 | reduce(lambda a, b: a + b,list(map(lambda x, w: x * w, input_vec, self.weights)), 0.0) + self.bias) 32 | def train(self, input_vecs, labels, iteration, rate): 33 | ''' 34 | 输入训练数据:一组向量、与每个向量对应的label;以及训练轮数、学习率 35 | ''' 36 | for i in range(iteration): 37 | self._one_iteration(input_vecs, labels, rate) 38 | def _one_iteration(self, input_vecs, labels, rate): 39 | ''' 40 | 一次迭代,把所有的训练数据过一遍 41 | ''' 42 | # 把输入和输出打包在一起,成为样本的列表[(input_vec, label), ...] 43 | # 而每个训练样本是(input_vec, label) 44 | samples = zip(input_vecs, labels) 45 | # 对每个样本,按照感知器规则更新权重 46 | for (input_vec, label) in samples: 47 | # 计算感知器在当前权重下的输出 48 | output = self.predict(input_vec) 49 | # 更新权重 50 | self._update_weights(input_vec, output, label, rate) 51 | def _update_weights(self, input_vec, output, label, rate): 52 | ''' 53 | 按照感知器规则更新权重 54 | ''' 55 | # 把input_vec[x1,x2,x3,...]和weights[w1,w2,w3,...]打包在一起 56 | # 变成[(x1,w1),(x2,w2),(x3,w3),...] 57 | # 然后利用感知器规则更新权重 58 | delta = label - output 59 | self.weights = list(map(lambda x, w: w + rate * delta * x, input_vec, self.weights)) 60 | # 更新bias 61 | self.bias += rate * delta 62 | 63 | def f(x): 64 | ''' 65 | 定义激活函数f 66 | ''' 67 | return 1 if x > 0 else 0 68 | def get_training_dataset(): 69 | ''' 70 | 基于and真值表构建训练数据 71 | ''' 72 | # 构建训练数据 73 | # 输入向量列表 74 | input_vecs = [[1,1], [0,0], [1,0], [0,1]] 75 | # 期望的输出列表,注意要与输入一一对应 76 | # [1,1] -> 1, [0,0] -> 0, [1,0] -> 0, [0,1] -> 0 77 | labels = [1, 0, 0, 0] 78 | return input_vecs, labels 79 | def train_and_perceptron(): 80 | ''' 81 | 使用and真值表训练感知器 82 | ''' 83 | # 创建感知器,输入参数个数为2(因为and是二元函数),激活函数为f 84 | p = Perceptron(2, f) 85 | # 训练,迭代10轮, 学习速率为0.1 86 | input_vecs, labels = get_training_dataset() 87 | p.train(input_vecs, labels, 10, 0.1) 88 | #返回训练好的感知器 89 | return p 90 | if __name__ == '__main__': 91 | # 训练and感知器 92 | and_perception = train_and_perceptron() 93 | # 打印训练获得的权重 94 | print(and_perception) 95 | # 测试 96 | print('1 and 1 = %d' % and_perception.predict([1, 1])) 97 | print('0 and 0 = %d' % and_perception.predict([0, 0])) 98 | print('1 and 0 = %d' % and_perception.predict([1, 0])) 99 | print('0 and 1 = %d' % and_perception.predict([0, 1])) -------------------------------------------------------------------------------- /Tutorial/lesson-3/bp.py: -------------------------------------------------------------------------------- 1 | #-*- coding:utf-8 -*- 2 | import random 3 | from functools import reduce 4 | from numpy import exp 5 | 6 | def sigmoid(inX): 7 | return 1.0 / (1 + exp(-inX)) 8 | 9 | # 节点类,负责记录和维护节点自身信息以及与这个节点相关的上下游连接,实现输出值和误差项的计算。 10 | class Node(): 11 | def __init__(self, layer_index, node_index): 12 | ''' 13 | 构造节点对象。 14 | layer_index: 节点所属的层的编号 15 | node_index: 节点的编号 16 | ''' 17 | self.layer_index = layer_index 18 | self.node_index = node_index 19 | self.downstream = [] 20 | self.upstream = [] 21 | self.output = 0 22 | self.delta = 0 23 | 24 | def set_output(self, output): 25 | ''' 26 | 设置节点的输出值。如果节点属于输入层会用到这个函数。 27 | ''' 28 | self.output = output 29 | 30 | def append_downstream_connection(self, conn): 31 | ''' 32 | 添加一个到下游节点的连接 33 | ''' 34 | self.downstream.append(conn) 35 | 36 | def append_upstream_connection(self, conn): 37 | ''' 38 | 添加一个到上游节点的连接 39 | ''' 40 | self.upstream.append(conn) 41 | 42 | def calc_output(self): 43 | ''' 44 | 根据式1计算节点的输出 45 | ''' 46 | # 每个节点的输出算法,N元一次方程求和 47 | output = reduce(lambda ret, conn: ret + conn.upstream_node.output * conn.weight, self.upstream, 0) 48 | # 结果放入激活函数 49 | self.output = sigmoid(output) 50 | 51 | def calc_hidden_layer_delta(self): 52 | ''' 53 | 节点属于隐藏层时,根据式4计算delta 54 | ''' 55 | downstream_delta = reduce( 56 | lambda ret, conn: ret + conn.downstream_node.delta * conn.weight, 57 | self.downstream, 0.0) 58 | self.delta = self.output * (1 - self.output) * downstream_delta 59 | 60 | def calc_output_layer_delta(self, label): 61 | ''' 62 | 节点属于输出层时,根据式3计算delta 63 | ''' 64 | self.delta = self.output * (1 - self.output) * (label - self.output) 65 | 66 | def __str__(self): 67 | ''' 68 | 打印节点的信息 69 | ''' 70 | node_str = '%u-%u: output: %f delta: %f' % (self.layer_index, self.node_index, self.output, self.delta) 71 | downstream_str = reduce(lambda ret, conn: ret + '\n\t' + str(conn), self.downstream, '') 72 | upstream_str = reduce(lambda ret, conn: ret + '\n\t' + str(conn), self.upstream, '') 73 | return node_str + '\n\tdownstream:' + downstream_str + '\n\tupstream:' + upstream_str 74 | 75 | class ConstNode(): 76 | def __init__(self, layer_index, node_index): 77 | ''' 78 | 构造节点对象。 79 | layer_index: 节点所属的层的编号 80 | node_index: 节点的编号 81 | ''' 82 | self.layer_index = layer_index 83 | self.node_index = node_index 84 | self.downstream = [] 85 | self.output = 1 86 | def append_downstream_connection(self, conn): 87 | ''' 88 | 添加一个到下游节点的连接 89 | ''' 90 | self.downstream.append(conn) 91 | def calc_hidden_layer_delta(self): 92 | ''' 93 | 节点属于隐藏层时,根据式4计算delta 94 | ''' 95 | downstream_delta = reduce( 96 | lambda ret, conn: ret + conn.downstream_node.delta * conn.weight, 97 | self.downstream, 0.0) 98 | self.delta = self.output * (1 - self.output) * downstream_delta 99 | def __str__(self): 100 | ''' 101 | 打印节点的信息 102 | ''' 103 | node_str = '%u-%u: output: 1' % (self.layer_index, self.node_index) 104 | downstream_str = reduce(lambda ret, conn: ret + '\n\t' + str(conn), self.downstream, '') 105 | return node_str + '\n\tdownstream:' + downstream_str 106 | 107 | class Layer(): 108 | def __init__(self, layer_index, node_count): 109 | ''' 110 | 初始化一层 111 | layer_index: 层编号 112 | node_count: 层所包含的节点个数 113 | ''' 114 | self.layer_index = layer_index 115 | self.nodes = [] 116 | for i in range(node_count): 117 | self.nodes.append(Node(layer_index, i)) 118 | self.nodes.append(ConstNode(layer_index, node_count)) 119 | def set_output(self, data): 120 | ''' 121 | 设置层的输出。当层是输入层时会用到。 122 | ''' 123 | for i in range(len(data)): 124 | self.nodes[i].set_output(data[i]) 125 | def calc_output(self): 126 | ''' 127 | 计算层的输出向量 128 | ''' 129 | for node in self.nodes[:-1]: 130 | node.calc_output() 131 | def dump(self): 132 | ''' 133 | 打印层的信息 134 | ''' 135 | for node in self.nodes: 136 | print(node) 137 | 138 | class Connection(): 139 | def __init__(self, upstream_node, downstream_node): 140 | ''' 141 | 初始化连接,权重初始化为是一个很小的随机数 142 | upstream_node: 连接的上游节点 143 | downstream_node: 连接的下游节点 144 | ''' 145 | self.upstream_node = upstream_node 146 | self.downstream_node = downstream_node 147 | self.weight = random.uniform(-0.1, 0.1) 148 | self.gradient = 0.0 149 | def calc_gradient(self): 150 | ''' 151 | 计算梯度 152 | ''' 153 | self.gradient = self.downstream_node.delta * self.upstream_node.output 154 | def get_gradient(self): 155 | ''' 156 | 获取当前的梯度 157 | ''' 158 | return self.gradient 159 | def update_weight(self, rate): 160 | ''' 161 | 根据梯度下降算法更新权重 162 | ''' 163 | self.calc_gradient() 164 | self.weight += rate * self.gradient 165 | def __str__(self): 166 | ''' 167 | 打印连接信息 168 | ''' 169 | return '(%u-%u) -> (%u-%u) = %f' % ( 170 | self.upstream_node.layer_index, 171 | self.upstream_node.node_index, 172 | self.downstream_node.layer_index, 173 | self.downstream_node.node_index, 174 | self.weight) 175 | 176 | class Connections(): 177 | def __init__(self): 178 | self.connections = [] 179 | def add_connection(self, connection): 180 | self.connections.append(connection) 181 | def dump(self): 182 | for conn in self.connections: 183 | print(conn) 184 | 185 | class Network(): 186 | def __init__(self, layers): 187 | ''' 188 | 初始化一个全连接神经网络 189 | layers: 二维数组,描述神经网络每层节点数 190 | ''' 191 | self.connections = Connections() 192 | self.layers = [] 193 | layer_count = len(layers) 194 | node_count = 0; 195 | for i in range(layer_count): 196 | self.layers.append(Layer(i, layers[i])) 197 | for layer in range(layer_count - 1): 198 | connections = [Connection(upstream_node, downstream_node) 199 | for upstream_node in self.layers[layer].nodes 200 | for downstream_node in self.layers[layer + 1].nodes[:-1]] 201 | for conn in connections: 202 | self.connections.add_connection(conn) 203 | conn.downstream_node.append_upstream_connection(conn) 204 | conn.upstream_node.append_downstream_connection(conn) 205 | def train(self, labels, data_set, rate, iteration): 206 | ''' 207 | 训练神经网络 208 | labels: 数组,训练样本标签。每个元素是一个样本的标签。 209 | data_set: 二维数组,训练样本特征。每个元素是一个样本的特征。 210 | ''' 211 | for i in range(iteration): 212 | for d in range(len(data_set)): 213 | self.train_one_sample(labels[d], data_set[d], rate) 214 | def train_one_sample(self, label, sample, rate): 215 | ''' 216 | 内部函数,用一个样本训练网络 217 | ''' 218 | self.predict(sample) 219 | self.calc_delta(label) 220 | self.update_weight(rate) 221 | def calc_delta(self, label): 222 | ''' 223 | 内部函数,计算每个节点的delta 224 | ''' 225 | output_nodes = self.layers[-1].nodes 226 | for i in range(len(label)): 227 | output_nodes[i].calc_output_layer_delta(label[i]) 228 | for layer in self.layers[-2::-1]: 229 | for node in layer.nodes: 230 | node.calc_hidden_layer_delta() 231 | def update_weight(self, rate): 232 | ''' 233 | 内部函数,更新每个连接权重 234 | ''' 235 | for layer in self.layers[:-1]: 236 | for node in layer.nodes: 237 | for conn in node.downstream: 238 | conn.update_weight(rate) 239 | def calc_gradient(self): 240 | ''' 241 | 内部函数,计算每个连接的梯度 242 | ''' 243 | for layer in self.layers[:-1]: 244 | for node in layer.nodes: 245 | for conn in node.downstream: 246 | conn.calc_gradient() 247 | def get_gradient(self, label, sample): 248 | ''' 249 | 获得网络在一个样本下,每个连接上的梯度 250 | label: 样本标签 251 | sample: 样本输入 252 | ''' 253 | self.predict(sample) 254 | self.calc_delta(label) 255 | self.calc_gradient() 256 | def predict(self, sample): 257 | ''' 258 | 根据输入的样本预测输出值 259 | sample: 数组,样本的特征,也就是网络的输入向量 260 | ''' 261 | self.layers[0].set_output(sample) 262 | for i in range(1, len(self.layers)): 263 | self.layers[i].calc_output() 264 | return list(map(lambda node: node.output, self.layers[-1].nodes[:-1])) 265 | def dump(self): 266 | ''' 267 | 打印网络信息 268 | ''' 269 | for layer in self.layers: 270 | layer.dump() 271 | 272 | def gradient_check(network, sample_feature, sample_label): 273 | ''' 274 | 梯度检查 275 | network: 神经网络对象 276 | sample_feature: 样本的特征 277 | sample_label: 样本的标签 278 | ''' 279 | # 计算网络误差 280 | network_error = lambda vec1, vec2: \ 281 | 0.5 * reduce(lambda a, b: a + b, 282 | map(lambda v: (v[0] - v[1]) * (v[0] - v[1]), 283 | zip(vec1, vec2))) 284 | # 获取网络在当前样本下每个连接的梯度 285 | network.get_gradient(sample_feature, sample_label) 286 | # 对每个权重做梯度检查 287 | for conn in network.connections.connections: 288 | # 获取指定连接的梯度 289 | actual_gradient = conn.get_gradient() 290 | # 增加一个很小的值,计算网络的误差 291 | epsilon = 0.0001 292 | conn.weight += epsilon 293 | error1 = network_error(network.predict(sample_feature), sample_label) 294 | # 减去一个很小的值,计算网络的误差 295 | conn.weight -= 2 * epsilon # 刚才加过了一次,因此这里需要减去2倍 296 | error2 = network_error(network.predict(sample_feature), sample_label) 297 | # 根据式6计算期望的梯度值 298 | expected_gradient = (error2 - error1) / (2 * epsilon) 299 | # 打印 300 | print('expected gradient: \t%f\nactual gradient: \t%f' % ( 301 | expected_gradient, actual_gradient)) -------------------------------------------------------------------------------- /Tutorial/lesson-3/fc.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import numpy as np 3 | 4 | # 全连接层实现类 5 | class FullConnectedLayer(): 6 | def __init__(self, input_size, output_size, 7 | activator): 8 | ''' 9 | 构造函数 10 | input_size: 本层输入向量的维度 11 | output_size: 本层输出向量的维度 12 | activator: 激活函数 13 | ''' 14 | self.input_size = input_size 15 | self.output_size = output_size 16 | self.activator = activator 17 | # 权重数组W 18 | self.W = np.random.uniform(-0.1, 0.1, 19 | (output_size, input_size)) 20 | # 偏置项b 21 | self.b = np.zeros((output_size, 1)) 22 | # 输出向量 23 | self.output = np.zeros((output_size, 1)) 24 | def forward(self, input_array): 25 | ''' 26 | 前向计算 27 | input_array: 输入向量,维度必须等于input_size 28 | ''' 29 | # 式2 30 | self.input = input_array 31 | self.output = self.activator.forward( 32 | np.dot(self.W, input_array) + self.b) 33 | def backward(self, delta_array): 34 | ''' 35 | 反向计算W和b的梯度 36 | delta_array: 从上一层传递过来的误差项 37 | ''' 38 | # 式8 39 | print(delta_array.shape) 40 | print(self.activator.backward(self.input).shape) 41 | print(self.W.T.shape) 42 | self.delta = self.activator.backward(self.input) * np.dot( 43 | self.W.T, delta_array) 44 | self.W_grad = np.dot(delta_array, self.input.T) 45 | self.b_grad = delta_array 46 | def update(self, learning_rate): 47 | ''' 48 | 使用梯度下降算法更新权重 49 | ''' 50 | self.W += learning_rate * self.W_grad 51 | self.b += learning_rate * self.b_grad 52 | 53 | # Sigmoid激活函数类 54 | class SigmoidActivator(): 55 | def forward(self, weighted_input): 56 | return 1.0 / (1.0 + np.exp(-weighted_input)) 57 | def backward(self, output): 58 | return output * (1 - output) 59 | 60 | # 神经网络类 61 | class Network(): 62 | def __init__(self, layers): 63 | ''' 64 | 构造函数 65 | ''' 66 | self.layers = [] 67 | for i in range(len(layers) - 1): 68 | self.layers.append( 69 | FullConnectedLayer( 70 | layers[i], layers[i+1], 71 | SigmoidActivator() 72 | ) 73 | ) 74 | def predict(self, sample): 75 | ''' 76 | 使用神经网络实现预测 77 | sample: 输入样本 78 | ''' 79 | output = sample 80 | for layer in self.layers: 81 | layer.forward(output) 82 | output = layer.output 83 | return output 84 | def train(self, labels, data_set, rate, epoch): 85 | ''' 86 | 训练函数 87 | labels: 样本标签 88 | data_set: 输入样本 89 | rate: 学习速率 90 | epoch: 训练轮数 91 | ''' 92 | for i in range(epoch): 93 | for d in range(len(data_set)): 94 | self.train_one_sample(labels[d], 95 | data_set[d], rate) 96 | def train_one_sample(self, label, sample, rate): 97 | self.predict(sample) 98 | self.calc_gradient(label) 99 | self.update_weight(rate) 100 | def calc_gradient(self, label): 101 | delta = self.layers[-1].activator.backward( 102 | self.layers[-1].output 103 | ) * (label - self.layers[-1].output) 104 | for layer in self.layers[::-1]: 105 | layer.backward(delta) 106 | delta = layer.delta 107 | return delta 108 | def update_weight(self, rate): 109 | for layer in self.layers: 110 | layer.update(rate) 111 | 112 | def dump(self): 113 | for layer in self.layers: 114 | layer.dump() 115 | 116 | def loss(self, output, label): 117 | return 0.5 * ((label - output) * (label - output)).sum() 118 | 119 | def gradient_check(self, sample_feature, sample_label): 120 | ''' 121 | 梯度检查 122 | network: 神经网络对象 123 | sample_feature: 样本的特征 124 | sample_label: 样本的标签 125 | ''' 126 | 127 | # 获取网络在当前样本下每个连接的梯度 128 | self.predict(sample_feature) 129 | self.calc_gradient(sample_label) 130 | 131 | # 检查梯度 132 | epsilon = 10e-4 133 | for fc in self.layers: 134 | for i in range(fc.W.shape[0]): 135 | for j in range(fc.W.shape[1]): 136 | fc.W[i, j] += epsilon 137 | output = self.predict(sample_feature) 138 | err1 = self.loss(sample_label, output) 139 | fc.W[i, j] -= 2 * epsilon 140 | output = self.predict(sample_feature) 141 | err2 = self.loss(sample_label, output) 142 | expect_grad = (err1 - err2) / (2 * epsilon) 143 | fc.W[i, j] += epsilon 144 | print('weights(%d,%d): expected - actural %.4e - %.4e' % ( 145 | i, j, expect_grad, fc.W_grad[i, j])) 146 | 147 | def transpose(args): 148 | return list(map( 149 | lambda arg: list(map( 150 | lambda line: np.array(line).reshape(len(line), 1) 151 | , arg)) 152 | , args 153 | )) 154 | 155 | class Normalizer(object): 156 | def __init__(self): 157 | self.mask = [ 158 | 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 159 | ] 160 | 161 | def norm(self, number): 162 | data = list(map(lambda m: 0.9 if number & m else 0.1, self.mask)) 163 | return np.array(data).reshape(8, 1) 164 | 165 | def denorm(self, vec): 166 | binary = list(map(lambda i: 1 if i > 0.5 else 0, vec[:, 0])) 167 | for i in range(len(self.mask)): 168 | binary[i] = binary[i] * self.mask[i] 169 | return reduce(lambda x, y: x + y, binary) 170 | 171 | def train_data_set(): 172 | normalizer = Normalizer() 173 | data_set = [] 174 | labels = [] 175 | for i in range(0, 256): 176 | n = normalizer.norm(i) 177 | data_set.append(n) 178 | labels.append(n) 179 | return labels, data_set 180 | 181 | 182 | def correct_ratio(network): 183 | normalizer = Normalizer() 184 | correct = 0.0; 185 | for i in range(256): 186 | if normalizer.denorm(network.predict(normalizer.norm(i))) == i: 187 | correct += 1.0 188 | print('correct_ratio: %.2f%%' % (correct / 256 * 100)) 189 | 190 | 191 | def test(): 192 | labels, data_set = transpose(train_data_set()) 193 | net = Network([8, 3, 8]) 194 | rate = 0.5 195 | mini_batch = 20 196 | epoch = 10 197 | for i in range(epoch): 198 | net.train(labels, data_set, rate, mini_batch) 199 | print('after epoch %d loss: %f' % ( 200 | (i + 1), 201 | net.loss(labels[-1], net.predict(data_set[-1])) 202 | )) 203 | rate /= 2 204 | correct_ratio(net) 205 | 206 | 207 | def gradient_check(): 208 | ''' 209 | 梯度检查 210 | ''' 211 | labels, data_set = transpose(train_data_set()) 212 | net = Network([8, 3, 8]) 213 | net.gradient_check(data_set[0], labels[0]) 214 | return net -------------------------------------------------------------------------------- /Tutorial/lesson-3/mnist.py: -------------------------------------------------------------------------------- 1 | #-*- coding:utf-8 -*- 2 | from bp import * 3 | from datetime import datetime 4 | 5 | # 数据加载器基类 6 | class Loader(): 7 | def __init__(self, path, count): 8 | ''' 9 | 初始化加载器 10 | path: 数据文件路径 11 | count: 文件中的样本个数 12 | ''' 13 | self.path = path 14 | self.count = count 15 | def get_file_content(self): 16 | ''' 17 | 读取文件内容 18 | ''' 19 | f = open(self.path, 'rb') 20 | content = f.read() 21 | f.close() 22 | return content 23 | 24 | # 图像数据加载器 25 | class ImageLoader(Loader): 26 | def get_picture(self, content, index): 27 | ''' 28 | 内部函数,从文件中获取图像 29 | ''' 30 | start = index * 28 * 28 + 16 31 | picture = [] 32 | for i in range(28): 33 | picture.append([]) 34 | for j in range(28): 35 | picture[i].append( 36 | int(content[start + i * 28 + j])) 37 | return picture 38 | def get_one_sample(self, picture): 39 | ''' 40 | 内部函数,将图像转化为样本的输入向量 41 | ''' 42 | sample = [] 43 | for i in range(28): 44 | for j in range(28): 45 | sample.append(picture[i][j]) 46 | return sample 47 | def load(self): 48 | ''' 49 | 加载数据文件,获得全部样本的输入向量 50 | ''' 51 | content = self.get_file_content() 52 | data_set = [] 53 | for index in range(self.count): 54 | data_set.append( 55 | self.get_one_sample( 56 | self.get_picture(content, index))) 57 | return data_set 58 | # 标签数据加载器 59 | class LabelLoader(Loader): 60 | def load(self): 61 | ''' 62 | 加载数据文件,获得全部样本的标签向量 63 | ''' 64 | content = self.get_file_content() 65 | labels = [] 66 | for index in range(self.count): 67 | labels.append(self.norm(content[index + 8])) 68 | return labels 69 | def norm(self, label): 70 | ''' 71 | 内部函数,将一个值转换为10维标签向量 72 | ''' 73 | label_vec = [] 74 | label_value = int(label) 75 | for i in range(10): 76 | if i == label_value: 77 | label_vec.append(0.9) 78 | else: 79 | label_vec.append(0.1) 80 | return label_vec 81 | def get_training_data_set(): 82 | ''' 83 | 获得训练数据集 84 | ''' 85 | image_loader = ImageLoader('train-images.idx3-ubyte', 60000) 86 | label_loader = LabelLoader('train-labels.idx1-ubyte', 60000) 87 | return image_loader.load(), label_loader.load() 88 | def get_test_data_set(): 89 | ''' 90 | 获得测试数据集 91 | ''' 92 | image_loader = ImageLoader('t10k-images.idx3-ubyte', 10000) 93 | label_loader = LabelLoader('t10k-labels.idx1-ubyte', 10000) 94 | return image_loader.load(), label_loader.load() 95 | 96 | 97 | def get_result(vec): 98 | max_value_index = 0 99 | max_value = 0 100 | for i in range(len(vec)): 101 | if vec[i] > max_value: 102 | max_value = vec[i] 103 | max_value_index = i 104 | return max_value_index 105 | 106 | def evaluate(network, test_data_set, test_labels): 107 | error = 0 108 | total = len(test_data_set) 109 | for i in range(total): 110 | label = get_result(test_labels[i]) 111 | predict = get_result(network.predict(test_data_set[i])) 112 | if label != predict: 113 | error += 1 114 | return float(error) / float(total) 115 | 116 | def train_and_evaluate(): 117 | last_error_ratio = 1.0 118 | epoch = 0 119 | train_data_set, train_labels = get_training_data_set() 120 | test_data_set, test_labels = get_test_data_set() 121 | network = Network([784, 300, 10]) 122 | while True: 123 | epoch += 1 124 | network.train(train_labels, train_data_set, 0.3, 1) 125 | print('%s epoch %d finished' % (now(), epoch)) 126 | if epoch % 10 == 0: 127 | error_ratio = evaluate(network, test_data_set, test_labels) 128 | print('%s after epoch %d, error ratio is %f' % (now(), epoch, error_ratio)) 129 | if error_ratio > last_error_ratio: 130 | break 131 | else: 132 | last_error_ratio = error_ratio 133 | 134 | if __name__ == '__main__': 135 | train_and_evaluate() -------------------------------------------------------------------------------- /Tutorial/lesson-4/activators.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | 4 | import numpy as np 5 | 6 | 7 | class ReluActivator(object): 8 | def forward(self, weighted_input): 9 | #return weighted_input 10 | return max(0, weighted_input) 11 | 12 | def backward(self, output): 13 | return 1 if output > 0 else 0 14 | 15 | 16 | class IdentityActivator(object): 17 | def forward(self, weighted_input): 18 | return weighted_input 19 | 20 | def backward(self, output): 21 | return 1 22 | 23 | 24 | class SigmoidActivator(object): 25 | def forward(self, weighted_input): 26 | return 1.0 / (1.0 + np.exp(-weighted_input)) 27 | 28 | def backward(self, output): 29 | return output * (1 - output) 30 | 31 | 32 | class TanhActivator(object): 33 | def forward(self, weighted_input): 34 | return 2.0 / (1.0 + np.exp(-2 * weighted_input)) - 1.0 35 | 36 | def backward(self, output): 37 | return 1 - output * output -------------------------------------------------------------------------------- /Tutorial/lesson-4/cnn.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | import numpy as np 3 | from activators import ReluActivator, IdentityActivator 4 | 5 | 6 | # 获取卷积区域 7 | def get_patch(input_array, i, j, filter_width, 8 | filter_height, stride): 9 | ''' 10 | 从输入数组中获取本次卷积的区域, 11 | 自动适配输入为2D和3D的情况 12 | ''' 13 | start_i = i * stride 14 | start_j = j * stride 15 | if input_array.ndim == 2: 16 | return input_array[ 17 | start_i : start_i + filter_height, 18 | start_j : start_j + filter_width] 19 | elif input_array.ndim == 3: 20 | return input_array[:, 21 | start_i : start_i + filter_height, 22 | start_j : start_j + filter_width] 23 | 24 | 25 | # 获取一个2D区域的最大值所在的索引 26 | def get_max_index(array): 27 | max_i = 0 28 | max_j = 0 29 | max_value = array[0,0] 30 | for i in range(array.shape[0]): 31 | for j in range(array.shape[1]): 32 | if array[i,j] > max_value: 33 | max_value = array[i,j] 34 | max_i, max_j = i, j 35 | return max_i, max_j 36 | 37 | 38 | # 计算卷积 39 | def conv(input_array, 40 | kernel_array, 41 | output_array, 42 | stride, bias): 43 | ''' 44 | 计算卷积,自动适配输入为2D和3D的情况 45 | ''' 46 | channel_number = input_array.ndim 47 | output_width = output_array.shape[1] 48 | output_height = output_array.shape[0] 49 | kernel_width = kernel_array.shape[-1] 50 | kernel_height = kernel_array.shape[-2] 51 | for i in range(output_height): 52 | for j in range(output_width): 53 | output_array[i][j] = ( 54 | get_patch(input_array, i, j, kernel_width, 55 | kernel_height, stride) * kernel_array 56 | ).sum() + bias 57 | 58 | 59 | # 为数组增加Zero padding 60 | def padding(input_array, zp): 61 | ''' 62 | 为数组增加Zero padding,自动适配输入为2D和3D的情况 63 | ''' 64 | if zp == 0: 65 | return input_array 66 | else: 67 | if input_array.ndim == 3: 68 | input_width = input_array.shape[2] 69 | input_height = input_array.shape[1] 70 | input_depth = input_array.shape[0] 71 | padded_array = np.zeros(( 72 | input_depth, 73 | input_height + 2 * zp, 74 | input_width + 2 * zp)) 75 | padded_array[:, 76 | zp : zp + input_height, 77 | zp : zp + input_width] = input_array 78 | return padded_array 79 | elif input_array.ndim == 2: 80 | input_width = input_array.shape[1] 81 | input_height = input_array.shape[0] 82 | padded_array = np.zeros(( 83 | input_height + 2 * zp, 84 | input_width + 2 * zp)) 85 | padded_array[zp : zp + input_height, 86 | zp : zp + input_width] = input_array 87 | return padded_array 88 | 89 | 90 | # 对numpy数组进行element wise操作 91 | def element_wise_op(array, op): 92 | for i in np.nditer(array, 93 | op_flags=['readwrite']): 94 | i[...] = op(i) 95 | 96 | 97 | class Filter(object): 98 | def __init__(self, width, height, depth): 99 | self.weights = np.random.uniform(-1e-4, 1e-4, 100 | (depth, height, width)) 101 | self.bias = 0 102 | self.weights_grad = np.zeros( 103 | self.weights.shape) 104 | self.bias_grad = 0 105 | 106 | def __repr__(self): 107 | return 'filter weights:\n%s\nbias:\n%s' % ( 108 | repr(self.weights), repr(self.bias)) 109 | 110 | def get_weights(self): 111 | return self.weights 112 | 113 | def get_bias(self): 114 | return self.bias 115 | 116 | def update(self, learning_rate): 117 | self.weights -= learning_rate * self.weights_grad 118 | self.bias -= learning_rate * self.bias_grad 119 | 120 | 121 | class ConvLayer(object): 122 | def __init__(self, input_width, input_height, 123 | channel_number, filter_width, 124 | filter_height, filter_number, 125 | zero_padding, stride, activator, 126 | learning_rate): 127 | self.input_width = input_width 128 | self.input_height = input_height 129 | self.channel_number = channel_number 130 | self.filter_width = filter_width 131 | self.filter_height = filter_height 132 | self.filter_number = filter_number 133 | self.zero_padding = zero_padding 134 | self.stride = stride 135 | self.output_width = \ 136 | ConvLayer.calculate_output_size( 137 | self.input_width, filter_width, zero_padding, 138 | stride) 139 | self.output_height = \ 140 | ConvLayer.calculate_output_size( 141 | self.input_height, filter_height, zero_padding, 142 | stride) 143 | self.output_array = np.zeros((self.filter_number, 144 | self.output_height, self.output_width)) 145 | self.filters = [] 146 | for i in range(filter_number): 147 | self.filters.append(Filter(filter_width, 148 | filter_height, self.channel_number)) 149 | self.activator = activator 150 | self.learning_rate = learning_rate 151 | 152 | def forward(self, input_array): 153 | ''' 154 | 计算卷积层的输出 155 | 输出结果保存在self.output_array 156 | ''' 157 | self.input_array = input_array 158 | self.padded_input_array = padding(input_array, 159 | self.zero_padding) 160 | for f in range(self.filter_number): 161 | filter = self.filters[f] 162 | conv(self.padded_input_array, 163 | filter.get_weights(), self.output_array[f], 164 | self.stride, filter.get_bias()) 165 | element_wise_op(self.output_array, 166 | self.activator.forward) 167 | 168 | def backward(self, input_array, sensitivity_array, 169 | activator): 170 | ''' 171 | 计算传递给前一层的误差项,以及计算每个权重的梯度 172 | 前一层的误差项保存在self.delta_array 173 | 梯度保存在Filter对象的weights_grad 174 | ''' 175 | self.forward(input_array) 176 | self.bp_sensitivity_map(sensitivity_array, 177 | activator) 178 | self.bp_gradient(sensitivity_array) 179 | 180 | def update(self): 181 | ''' 182 | 按照梯度下降,更新权重 183 | ''' 184 | for filter in self.filters: 185 | filter.update(self.learning_rate) 186 | 187 | def bp_sensitivity_map(self, sensitivity_array, 188 | activator): 189 | ''' 190 | 计算传递到上一层的sensitivity map 191 | sensitivity_array: 本层的sensitivity map 192 | activator: 上一层的激活函数 193 | ''' 194 | # 处理卷积步长,对原始sensitivity map进行扩展 195 | expanded_array = self.expand_sensitivity_map( 196 | sensitivity_array) 197 | # full卷积,对sensitivitiy map进行zero padding 198 | # 虽然原始输入的zero padding单元也会获得残差 199 | # 但这个残差不需要继续向上传递,因此就不计算了 200 | expanded_width = expanded_array.shape[2] 201 | zp = int((self.input_width + 202 | self.filter_width - 1 - expanded_width) / 2) 203 | padded_array = padding(expanded_array, zp) 204 | # 初始化delta_array,用于保存传递到上一层的 205 | # sensitivity map 206 | self.delta_array = self.create_delta_array() 207 | # 对于具有多个filter的卷积层来说,最终传递到上一层的 208 | # sensitivity map相当于所有的filter的 209 | # sensitivity map之和 210 | for f in range(self.filter_number): 211 | filter = self.filters[f] 212 | # 将filter权重翻转180度 213 | flipped_weights = np.array(list(map( 214 | lambda i: np.rot90(i, 2), 215 | filter.get_weights()))) 216 | # 计算与一个filter对应的delta_array 217 | delta_array = self.create_delta_array() 218 | for d in range(delta_array.shape[0]): 219 | conv(padded_array[f], flipped_weights[d],delta_array[d], 1, 0) 220 | self.delta_array += delta_array 221 | # 将计算结果与激活函数的偏导数做element-wise乘法操作 222 | derivative_array = np.array(self.input_array) 223 | element_wise_op(derivative_array, 224 | activator.backward) 225 | self.delta_array *= derivative_array 226 | 227 | def bp_gradient(self, sensitivity_array): 228 | # 处理卷积步长,对原始sensitivity map进行扩展 229 | expanded_array = self.expand_sensitivity_map( 230 | sensitivity_array) 231 | for f in range(self.filter_number): 232 | # 计算每个权重的梯度 233 | filter = self.filters[f] 234 | for d in range(filter.weights.shape[0]): 235 | conv(self.padded_input_array[d], 236 | expanded_array[f], 237 | filter.weights_grad[d], 1, 0) 238 | # 计算偏置项的梯度 239 | filter.bias_grad = expanded_array[f].sum() 240 | 241 | def expand_sensitivity_map(self, sensitivity_array): 242 | depth = sensitivity_array.shape[0] 243 | # 确定扩展后sensitivity map的大小 244 | # 计算stride为1时sensitivity map的大小 245 | expanded_width = (self.input_width - 246 | self.filter_width + 2 * self.zero_padding + 1) 247 | expanded_height = (self.input_height - 248 | self.filter_height + 2 * self.zero_padding + 1) 249 | # 构建新的sensitivity_map 250 | expand_array = np.zeros((depth, expanded_height, 251 | expanded_width)) 252 | # 从原始sensitivity map拷贝误差值 253 | for i in range(self.output_height): 254 | for j in range(self.output_width): 255 | i_pos = i * self.stride 256 | j_pos = j * self.stride 257 | expand_array[:,i_pos,j_pos] = \ 258 | sensitivity_array[:,i,j] 259 | return expand_array 260 | 261 | def create_delta_array(self): 262 | return np.zeros((self.channel_number, 263 | self.input_height, self.input_width)) 264 | 265 | @staticmethod 266 | def calculate_output_size(input_size, 267 | filter_size, zero_padding, stride): 268 | return int((input_size - filter_size + 269 | 2 * zero_padding) / stride + 1) 270 | 271 | 272 | class MaxPoolingLayer(object): 273 | def __init__(self, input_width, input_height, 274 | channel_number, filter_width, 275 | filter_height, stride): 276 | self.input_width = input_width 277 | self.input_height = input_height 278 | self.channel_number = channel_number 279 | self.filter_width = filter_width 280 | self.filter_height = filter_height 281 | self.stride = stride 282 | self.output_width = int((input_width - 283 | filter_width) / self.stride + 1) 284 | self.output_height = int((input_height - 285 | filter_height) / self.stride + 1) 286 | self.output_array = np.zeros((self.channel_number, 287 | self.output_height, self.output_width)) 288 | 289 | def forward(self, input_array): 290 | for d in range(self.channel_number): 291 | for i in range(self.output_height): 292 | for j in range(self.output_width): 293 | self.output_array[d,i,j] = ( 294 | get_patch(input_array[d], i, j, 295 | self.filter_width, 296 | self.filter_height, 297 | self.stride).max()) 298 | 299 | def backward(self, input_array, sensitivity_array): 300 | self.delta_array = np.zeros(input_array.shape) 301 | for d in range(self.channel_number): 302 | for i in range(self.output_height): 303 | for j in range(self.output_width): 304 | patch_array = get_patch( 305 | input_array[d], i, j, 306 | self.filter_width, 307 | self.filter_height, 308 | self.stride) 309 | k, l = get_max_index(patch_array) 310 | self.delta_array[d, 311 | i * self.stride + k, 312 | j * self.stride + l] = \ 313 | sensitivity_array[d,i,j] 314 | 315 | 316 | def init_test(): 317 | a = np.array( 318 | [[[0,1,1,0,2], 319 | [2,2,2,2,1], 320 | [1,0,0,2,0], 321 | [0,1,1,0,0], 322 | [1,2,0,0,2]], 323 | [[1,0,2,2,0], 324 | [0,0,0,2,0], 325 | [1,2,1,2,1], 326 | [1,0,0,0,0], 327 | [1,2,1,1,1]], 328 | [[2,1,2,0,0], 329 | [1,0,0,1,0], 330 | [0,2,1,0,1], 331 | [0,1,2,2,2], 332 | [2,1,0,0,1]]]) 333 | b = np.array( 334 | [[[0,1,1], 335 | [2,2,2], 336 | [1,0,0]], 337 | [[1,0,2], 338 | [0,0,0], 339 | [1,2,1]]]) 340 | cl = ConvLayer(5,5,3,3,3,2,1,2,IdentityActivator(),0.001) 341 | cl.filters[0].weights = np.array( 342 | [[[-1,1,0], 343 | [0,1,0], 344 | [0,1,1]], 345 | [[-1,-1,0], 346 | [0,0,0], 347 | [0,-1,0]], 348 | [[0,0,-1], 349 | [0,1,0], 350 | [1,-1,-1]]], dtype=np.float64) 351 | cl.filters[0].bias=1 352 | cl.filters[1].weights = np.array( 353 | [[[1,1,-1], 354 | [-1,-1,1], 355 | [0,-1,1]], 356 | [[0,1,0], 357 | [-1,0,-1], 358 | [-1,1,0]], 359 | [[-1,0,0], 360 | [-1,0,1], 361 | [-1,0,0]]], dtype=np.float64) 362 | return a, b, cl 363 | 364 | 365 | def test(): 366 | a, b, cl = init_test() 367 | cl.forward(a) 368 | print(cl.output_array) 369 | 370 | def test_bp(): 371 | a, b, cl = init_test() 372 | cl.backward(a, b, IdentityActivator()) 373 | cl.update() 374 | print(cl.filters[0]) 375 | print(cl.filters[1]) 376 | 377 | 378 | def gradient_check(): 379 | ''' 380 | 梯度检查 381 | ''' 382 | # 设计一个误差函数,取所有节点输出项之和 383 | error_function = lambda o: o.sum() 384 | 385 | # 计算forward值 386 | a, b, cl = init_test() 387 | cl.forward(a) 388 | 389 | # 求取sensitivity map 390 | sensitivity_array = np.ones(cl.output_array.shape, 391 | dtype=np.float64) 392 | # 计算梯度 393 | cl.backward(a, sensitivity_array, 394 | IdentityActivator()) 395 | # 检查梯度 396 | epsilon = 10e-4 397 | for d in range(cl.filters[0].weights_grad.shape[0]): 398 | for i in range(cl.filters[0].weights_grad.shape[1]): 399 | for j in range(cl.filters[0].weights_grad.shape[2]): 400 | cl.filters[0].weights[d,i,j] += epsilon 401 | cl.forward(a) 402 | err1 = error_function(cl.output_array) 403 | cl.filters[0].weights[d,i,j] -= 2*epsilon 404 | cl.forward(a) 405 | err2 = error_function(cl.output_array) 406 | expect_grad = int((err1 - err2) / (2 * epsilon)) 407 | cl.filters[0].weights[d,i,j] += epsilon 408 | print('weights(%d,%d,%d): expected - actural %f - %f' % ( 409 | d, i, j, expect_grad, cl.filters[0].weights_grad[d,i,j])) 410 | 411 | 412 | def init_pool_test(): 413 | a = np.array( 414 | [[[1,1,2,4], 415 | [5,6,7,8], 416 | [3,2,1,0], 417 | [1,2,3,4]], 418 | [[0,1,2,3], 419 | [4,5,6,7], 420 | [8,9,0,1], 421 | [3,4,5,6]]], dtype=np.float64) 422 | 423 | b = np.array( 424 | [[[1,2], 425 | [2,4]], 426 | [[3,5], 427 | [8,2]]], dtype=np.float64) 428 | 429 | mpl = MaxPoolingLayer(4,4,2,2,2,2) 430 | 431 | return a, b, mpl 432 | 433 | 434 | def test_pool(): 435 | a, b, mpl = init_pool_test() 436 | mpl.forward(a) 437 | print('input array:\n%s\noutput array:\n%s' % (a, 438 | mpl.output_array)) 439 | 440 | 441 | def test_pool_bp(): 442 | a, b, mpl = init_pool_test() 443 | mpl.backward(a, b) 444 | print('input array:\n%s\nsensitivity array:\n%s\ndelta array:\n%s' % ( 445 | a, b, mpl.delta_array)) 446 | -------------------------------------------------------------------------------- /Tutorial/lesson-5/activators.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | 4 | import numpy as np 5 | 6 | 7 | class ReluActivator(object): 8 | def forward(self, weighted_input): 9 | #return weighted_input 10 | return max(0, weighted_input) 11 | 12 | def backward(self, output): 13 | return 1 if output > 0 else 0 14 | 15 | 16 | class IdentityActivator(object): 17 | def forward(self, weighted_input): 18 | return weighted_input 19 | 20 | def backward(self, output): 21 | return 1 22 | 23 | 24 | class SigmoidActivator(object): 25 | def forward(self, weighted_input): 26 | return 1.0 / (1.0 + np.exp(-weighted_input)) 27 | 28 | def backward(self, output): 29 | return output * (1 - output) 30 | 31 | 32 | class TanhActivator(object): 33 | def forward(self, weighted_input): 34 | return 2.0 / (1.0 + np.exp(-2 * weighted_input)) - 1.0 35 | 36 | def backward(self, output): 37 | return 1 - output * output -------------------------------------------------------------------------------- /Tutorial/lesson-5/cnn.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | import numpy as np 3 | from activators import ReluActivator, IdentityActivator 4 | 5 | 6 | # 获取卷积区域 7 | def get_patch(input_array, i, j, filter_width, 8 | filter_height, stride): 9 | ''' 10 | 从输入数组中获取本次卷积的区域, 11 | 自动适配输入为2D和3D的情况 12 | ''' 13 | start_i = i * stride 14 | start_j = j * stride 15 | if input_array.ndim == 2: 16 | return input_array[ 17 | start_i : start_i + filter_height, 18 | start_j : start_j + filter_width] 19 | elif input_array.ndim == 3: 20 | return input_array[:, 21 | start_i : start_i + filter_height, 22 | start_j : start_j + filter_width] 23 | 24 | 25 | # 获取一个2D区域的最大值所在的索引 26 | def get_max_index(array): 27 | max_i = 0 28 | max_j = 0 29 | max_value = array[0,0] 30 | for i in range(array.shape[0]): 31 | for j in range(array.shape[1]): 32 | if array[i,j] > max_value: 33 | max_value = array[i,j] 34 | max_i, max_j = i, j 35 | return max_i, max_j 36 | 37 | 38 | # 计算卷积 39 | def conv(input_array, 40 | kernel_array, 41 | output_array, 42 | stride, bias): 43 | ''' 44 | 计算卷积,自动适配输入为2D和3D的情况 45 | ''' 46 | channel_number = input_array.ndim 47 | output_width = output_array.shape[1] 48 | output_height = output_array.shape[0] 49 | kernel_width = kernel_array.shape[-1] 50 | kernel_height = kernel_array.shape[-2] 51 | for i in range(output_height): 52 | for j in range(output_width): 53 | output_array[i][j] = ( 54 | get_patch(input_array, i, j, kernel_width, 55 | kernel_height, stride) * kernel_array 56 | ).sum() + bias 57 | 58 | 59 | # 为数组增加Zero padding 60 | def padding(input_array, zp): 61 | ''' 62 | 为数组增加Zero padding,自动适配输入为2D和3D的情况 63 | ''' 64 | if zp == 0: 65 | return input_array 66 | else: 67 | if input_array.ndim == 3: 68 | input_width = input_array.shape[2] 69 | input_height = input_array.shape[1] 70 | input_depth = input_array.shape[0] 71 | padded_array = np.zeros(( 72 | input_depth, 73 | input_height + 2 * zp, 74 | input_width + 2 * zp)) 75 | padded_array[:, 76 | zp : zp + input_height, 77 | zp : zp + input_width] = input_array 78 | return padded_array 79 | elif input_array.ndim == 2: 80 | input_width = input_array.shape[1] 81 | input_height = input_array.shape[0] 82 | padded_array = np.zeros(( 83 | input_height + 2 * zp, 84 | input_width + 2 * zp)) 85 | padded_array[zp : zp + input_height, 86 | zp : zp + input_width] = input_array 87 | return padded_array 88 | 89 | 90 | # 对numpy数组进行element wise操作 91 | def element_wise_op(array, op): 92 | for i in np.nditer(array, 93 | op_flags=['readwrite']): 94 | i[...] = op(i) 95 | 96 | 97 | class Filter(object): 98 | def __init__(self, width, height, depth): 99 | self.weights = np.random.uniform(-1e-4, 1e-4, 100 | (depth, height, width)) 101 | self.bias = 0 102 | self.weights_grad = np.zeros( 103 | self.weights.shape) 104 | self.bias_grad = 0 105 | 106 | def __repr__(self): 107 | return 'filter weights:\n%s\nbias:\n%s' % ( 108 | repr(self.weights), repr(self.bias)) 109 | 110 | def get_weights(self): 111 | return self.weights 112 | 113 | def get_bias(self): 114 | return self.bias 115 | 116 | def update(self, learning_rate): 117 | self.weights -= learning_rate * self.weights_grad 118 | self.bias -= learning_rate * self.bias_grad 119 | 120 | 121 | class ConvLayer(object): 122 | def __init__(self, input_width, input_height, 123 | channel_number, filter_width, 124 | filter_height, filter_number, 125 | zero_padding, stride, activator, 126 | learning_rate): 127 | self.input_width = input_width 128 | self.input_height = input_height 129 | self.channel_number = channel_number 130 | self.filter_width = filter_width 131 | self.filter_height = filter_height 132 | self.filter_number = filter_number 133 | self.zero_padding = zero_padding 134 | self.stride = stride 135 | self.output_width = \ 136 | ConvLayer.calculate_output_size( 137 | self.input_width, filter_width, zero_padding, 138 | stride) 139 | self.output_height = \ 140 | ConvLayer.calculate_output_size( 141 | self.input_height, filter_height, zero_padding, 142 | stride) 143 | self.output_array = np.zeros((self.filter_number, 144 | self.output_height, self.output_width)) 145 | self.filters = [] 146 | for i in range(filter_number): 147 | self.filters.append(Filter(filter_width, 148 | filter_height, self.channel_number)) 149 | self.activator = activator 150 | self.learning_rate = learning_rate 151 | 152 | def forward(self, input_array): 153 | ''' 154 | 计算卷积层的输出 155 | 输出结果保存在self.output_array 156 | ''' 157 | self.input_array = input_array 158 | self.padded_input_array = padding(input_array, 159 | self.zero_padding) 160 | for f in range(self.filter_number): 161 | filter = self.filters[f] 162 | conv(self.padded_input_array, 163 | filter.get_weights(), self.output_array[f], 164 | self.stride, filter.get_bias()) 165 | element_wise_op(self.output_array, 166 | self.activator.forward) 167 | 168 | def backward(self, input_array, sensitivity_array, 169 | activator): 170 | ''' 171 | 计算传递给前一层的误差项,以及计算每个权重的梯度 172 | 前一层的误差项保存在self.delta_array 173 | 梯度保存在Filter对象的weights_grad 174 | ''' 175 | self.forward(input_array) 176 | self.bp_sensitivity_map(sensitivity_array, 177 | activator) 178 | self.bp_gradient(sensitivity_array) 179 | 180 | def update(self): 181 | ''' 182 | 按照梯度下降,更新权重 183 | ''' 184 | for filter in self.filters: 185 | filter.update(self.learning_rate) 186 | 187 | def bp_sensitivity_map(self, sensitivity_array, 188 | activator): 189 | ''' 190 | 计算传递到上一层的sensitivity map 191 | sensitivity_array: 本层的sensitivity map 192 | activator: 上一层的激活函数 193 | ''' 194 | # 处理卷积步长,对原始sensitivity map进行扩展 195 | expanded_array = self.expand_sensitivity_map( 196 | sensitivity_array) 197 | # full卷积,对sensitivitiy map进行zero padding 198 | # 虽然原始输入的zero padding单元也会获得残差 199 | # 但这个残差不需要继续向上传递,因此就不计算了 200 | expanded_width = expanded_array.shape[2] 201 | zp = int((self.input_width + 202 | self.filter_width - 1 - expanded_width) / 2) 203 | padded_array = padding(expanded_array, zp) 204 | # 初始化delta_array,用于保存传递到上一层的 205 | # sensitivity map 206 | self.delta_array = self.create_delta_array() 207 | # 对于具有多个filter的卷积层来说,最终传递到上一层的 208 | # sensitivity map相当于所有的filter的 209 | # sensitivity map之和 210 | for f in range(self.filter_number): 211 | filter = self.filters[f] 212 | # 将filter权重翻转180度 213 | flipped_weights = np.array(list(map( 214 | lambda i: np.rot90(i, 2), 215 | filter.get_weights()))) 216 | # 计算与一个filter对应的delta_array 217 | delta_array = self.create_delta_array() 218 | for d in range(delta_array.shape[0]): 219 | conv(padded_array[f], flipped_weights[d],delta_array[d], 1, 0) 220 | self.delta_array += delta_array 221 | # 将计算结果与激活函数的偏导数做element-wise乘法操作 222 | derivative_array = np.array(self.input_array) 223 | element_wise_op(derivative_array, 224 | activator.backward) 225 | self.delta_array *= derivative_array 226 | 227 | def bp_gradient(self, sensitivity_array): 228 | # 处理卷积步长,对原始sensitivity map进行扩展 229 | expanded_array = self.expand_sensitivity_map( 230 | sensitivity_array) 231 | for f in range(self.filter_number): 232 | # 计算每个权重的梯度 233 | filter = self.filters[f] 234 | for d in range(filter.weights.shape[0]): 235 | conv(self.padded_input_array[d], 236 | expanded_array[f], 237 | filter.weights_grad[d], 1, 0) 238 | # 计算偏置项的梯度 239 | filter.bias_grad = expanded_array[f].sum() 240 | 241 | def expand_sensitivity_map(self, sensitivity_array): 242 | depth = sensitivity_array.shape[0] 243 | # 确定扩展后sensitivity map的大小 244 | # 计算stride为1时sensitivity map的大小 245 | expanded_width = (self.input_width - 246 | self.filter_width + 2 * self.zero_padding + 1) 247 | expanded_height = (self.input_height - 248 | self.filter_height + 2 * self.zero_padding + 1) 249 | # 构建新的sensitivity_map 250 | expand_array = np.zeros((depth, expanded_height, 251 | expanded_width)) 252 | # 从原始sensitivity map拷贝误差值 253 | for i in range(self.output_height): 254 | for j in range(self.output_width): 255 | i_pos = i * self.stride 256 | j_pos = j * self.stride 257 | expand_array[:,i_pos,j_pos] = \ 258 | sensitivity_array[:,i,j] 259 | return expand_array 260 | 261 | def create_delta_array(self): 262 | return np.zeros((self.channel_number, 263 | self.input_height, self.input_width)) 264 | 265 | @staticmethod 266 | def calculate_output_size(input_size, 267 | filter_size, zero_padding, stride): 268 | return int((input_size - filter_size + 269 | 2 * zero_padding) / stride + 1) 270 | 271 | 272 | class MaxPoolingLayer(object): 273 | def __init__(self, input_width, input_height, 274 | channel_number, filter_width, 275 | filter_height, stride): 276 | self.input_width = input_width 277 | self.input_height = input_height 278 | self.channel_number = channel_number 279 | self.filter_width = filter_width 280 | self.filter_height = filter_height 281 | self.stride = stride 282 | self.output_width = int((input_width - 283 | filter_width) / self.stride + 1) 284 | self.output_height = int((input_height - 285 | filter_height) / self.stride + 1) 286 | self.output_array = np.zeros((self.channel_number, 287 | self.output_height, self.output_width)) 288 | 289 | def forward(self, input_array): 290 | for d in range(self.channel_number): 291 | for i in range(self.output_height): 292 | for j in range(self.output_width): 293 | self.output_array[d,i,j] = ( 294 | get_patch(input_array[d], i, j, 295 | self.filter_width, 296 | self.filter_height, 297 | self.stride).max()) 298 | 299 | def backward(self, input_array, sensitivity_array): 300 | self.delta_array = np.zeros(input_array.shape) 301 | for d in range(self.channel_number): 302 | for i in range(self.output_height): 303 | for j in range(self.output_width): 304 | patch_array = get_patch( 305 | input_array[d], i, j, 306 | self.filter_width, 307 | self.filter_height, 308 | self.stride) 309 | k, l = get_max_index(patch_array) 310 | self.delta_array[d, 311 | i * self.stride + k, 312 | j * self.stride + l] = \ 313 | sensitivity_array[d,i,j] 314 | 315 | 316 | def init_test(): 317 | a = np.array( 318 | [[[0,1,1,0,2], 319 | [2,2,2,2,1], 320 | [1,0,0,2,0], 321 | [0,1,1,0,0], 322 | [1,2,0,0,2]], 323 | [[1,0,2,2,0], 324 | [0,0,0,2,0], 325 | [1,2,1,2,1], 326 | [1,0,0,0,0], 327 | [1,2,1,1,1]], 328 | [[2,1,2,0,0], 329 | [1,0,0,1,0], 330 | [0,2,1,0,1], 331 | [0,1,2,2,2], 332 | [2,1,0,0,1]]]) 333 | b = np.array( 334 | [[[0,1,1], 335 | [2,2,2], 336 | [1,0,0]], 337 | [[1,0,2], 338 | [0,0,0], 339 | [1,2,1]]]) 340 | cl = ConvLayer(5,5,3,3,3,2,1,2,IdentityActivator(),0.001) 341 | cl.filters[0].weights = np.array( 342 | [[[-1,1,0], 343 | [0,1,0], 344 | [0,1,1]], 345 | [[-1,-1,0], 346 | [0,0,0], 347 | [0,-1,0]], 348 | [[0,0,-1], 349 | [0,1,0], 350 | [1,-1,-1]]], dtype=np.float64) 351 | cl.filters[0].bias=1 352 | cl.filters[1].weights = np.array( 353 | [[[1,1,-1], 354 | [-1,-1,1], 355 | [0,-1,1]], 356 | [[0,1,0], 357 | [-1,0,-1], 358 | [-1,1,0]], 359 | [[-1,0,0], 360 | [-1,0,1], 361 | [-1,0,0]]], dtype=np.float64) 362 | return a, b, cl 363 | 364 | 365 | def test(): 366 | a, b, cl = init_test() 367 | cl.forward(a) 368 | print(cl.output_array) 369 | 370 | def test_bp(): 371 | a, b, cl = init_test() 372 | cl.backward(a, b, IdentityActivator()) 373 | cl.update() 374 | print(cl.filters[0]) 375 | print(cl.filters[1]) 376 | 377 | 378 | def gradient_check(): 379 | ''' 380 | 梯度检查 381 | ''' 382 | # 设计一个误差函数,取所有节点输出项之和 383 | error_function = lambda o: o.sum() 384 | 385 | # 计算forward值 386 | a, b, cl = init_test() 387 | cl.forward(a) 388 | 389 | # 求取sensitivity map 390 | sensitivity_array = np.ones(cl.output_array.shape, 391 | dtype=np.float64) 392 | # 计算梯度 393 | cl.backward(a, sensitivity_array, 394 | IdentityActivator()) 395 | # 检查梯度 396 | epsilon = 10e-4 397 | for d in range(cl.filters[0].weights_grad.shape[0]): 398 | for i in range(cl.filters[0].weights_grad.shape[1]): 399 | for j in range(cl.filters[0].weights_grad.shape[2]): 400 | cl.filters[0].weights[d,i,j] += epsilon 401 | cl.forward(a) 402 | err1 = error_function(cl.output_array) 403 | cl.filters[0].weights[d,i,j] -= 2*epsilon 404 | cl.forward(a) 405 | err2 = error_function(cl.output_array) 406 | expect_grad = int((err1 - err2) / (2 * epsilon)) 407 | cl.filters[0].weights[d,i,j] += epsilon 408 | print('weights(%d,%d,%d): expected - actural %f - %f' % ( 409 | d, i, j, expect_grad, cl.filters[0].weights_grad[d,i,j])) 410 | 411 | 412 | def init_pool_test(): 413 | a = np.array( 414 | [[[1,1,2,4], 415 | [5,6,7,8], 416 | [3,2,1,0], 417 | [1,2,3,4]], 418 | [[0,1,2,3], 419 | [4,5,6,7], 420 | [8,9,0,1], 421 | [3,4,5,6]]], dtype=np.float64) 422 | 423 | b = np.array( 424 | [[[1,2], 425 | [2,4]], 426 | [[3,5], 427 | [8,2]]], dtype=np.float64) 428 | 429 | mpl = MaxPoolingLayer(4,4,2,2,2,2) 430 | 431 | return a, b, mpl 432 | 433 | 434 | def test_pool(): 435 | a, b, mpl = init_pool_test() 436 | mpl.forward(a) 437 | print('input array:\n%s\noutput array:\n%s' % (a, 438 | mpl.output_array)) 439 | 440 | 441 | def test_pool_bp(): 442 | a, b, mpl = init_pool_test() 443 | mpl.backward(a, b) 444 | print('input array:\n%s\nsensitivity array:\n%s\ndelta array:\n%s' % ( 445 | a, b, mpl.delta_array)) 446 | -------------------------------------------------------------------------------- /Tutorial/lesson-5/rnn.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | import numpy as np 3 | from cnn import element_wise_op 4 | from activators import ReluActivator, IdentityActivator 5 | from functools import reduce 6 | 7 | 8 | class RecurrentLayer(): 9 | def __init__(self, input_width, state_width, 10 | activator, learning_rate): 11 | self.input_width = input_width 12 | self.state_width = state_width 13 | self.activator = activator 14 | self.learning_rate = learning_rate 15 | self.times = 0 # 当前时刻初始化为t0 16 | self.state_list = [] # 保存各个时刻的state 17 | self.state_list.append(np.zeros( 18 | (state_width, 1))) # 初始化s0 19 | self.U = np.random.uniform(-1e-4, 1e-4, 20 | (state_width, input_width)) # 初始化U 21 | self.W = np.random.uniform(-1e-4, 1e-4, 22 | (state_width, state_width)) # 初始化W 23 | 24 | def forward(self, input_array): 25 | ''' 26 | 根据『式2』进行前向计算 27 | ''' 28 | self.times += 1 29 | state = (np.dot(self.U, input_array) + 30 | np.dot(self.W, self.state_list[-1])) 31 | element_wise_op(state, self.activator.forward) 32 | self.state_list.append(state) 33 | 34 | def backward(self, sensitivity_array, 35 | activator): 36 | ''' 37 | 实现BPTT算法 38 | ''' 39 | self.calc_delta(sensitivity_array, activator) 40 | self.calc_gradient() 41 | 42 | def update(self): 43 | ''' 44 | 按照梯度下降,更新权重 45 | ''' 46 | self.W -= self.learning_rate * self.gradient 47 | 48 | def calc_delta(self, sensitivity_array, activator): 49 | self.delta_list = [] # 用来保存各个时刻的误差项 50 | for i in range(self.times): 51 | self.delta_list.append(np.zeros( 52 | (self.state_width, 1))) 53 | self.delta_list.append(sensitivity_array) 54 | # 迭代计算每个时刻的误差项 55 | for k in range(self.times - 1, 0, -1): 56 | self.calc_delta_k(k, activator) 57 | 58 | def calc_delta_k(self, k, activator): 59 | ''' 60 | 根据k+1时刻的delta计算k时刻的delta 61 | ''' 62 | state = self.state_list[k+1].copy() 63 | element_wise_op(self.state_list[k+1], 64 | activator.backward) 65 | self.delta_list[k] = np.dot( 66 | np.dot(self.delta_list[k+1].T, self.W), 67 | np.diag(state[:,0])).T 68 | 69 | def calc_gradient(self): 70 | self.gradient_list = [] # 保存各个时刻的权重梯度 71 | for t in range(self.times + 1): 72 | self.gradient_list.append(np.zeros( 73 | (self.state_width, self.state_width))) 74 | for t in range(self.times, 0, -1): 75 | self.calc_gradient_t(t) 76 | # 实际的梯度是各个时刻梯度之和 77 | self.gradient = reduce( 78 | lambda a, b: a + b, self.gradient_list, 79 | self.gradient_list[0]) # [0]被初始化为0且没有被修改过 80 | 81 | def calc_gradient_t(self, t): 82 | ''' 83 | 计算每个时刻t权重的梯度 84 | ''' 85 | gradient = np.dot(self.delta_list[t], 86 | self.state_list[t-1].T) 87 | self.gradient_list[t] = gradient 88 | 89 | def reset_state(self): 90 | self.times = 0 # 当前时刻初始化为t0 91 | self.state_list = [] # 保存各个时刻的state 92 | self.state_list.append(np.zeros( 93 | (self.state_width, 1))) # 初始化s0 94 | 95 | 96 | def data_set(): 97 | x = [np.array([[1], [2], [3]]), 98 | np.array([[2], [3], [4]])] 99 | d = np.array([[1], [2]]) 100 | return x, d 101 | 102 | 103 | def gradient_check(): 104 | ''' 105 | 梯度检查 106 | ''' 107 | # 设计一个误差函数,取所有节点输出项之和 108 | error_function = lambda o: o.sum() 109 | 110 | rl = RecurrentLayer(3, 2, IdentityActivator(), 1e-3) 111 | 112 | # 计算forward值 113 | x, d = data_set() 114 | rl.forward(x[0]) 115 | rl.forward(x[1]) 116 | 117 | # 求取sensitivity map 118 | sensitivity_array = np.ones(rl.state_list[-1].shape, 119 | dtype=np.float64) 120 | # 计算梯度 121 | rl.backward(sensitivity_array, IdentityActivator()) 122 | 123 | # 检查梯度 124 | epsilon = 10e-4 125 | for i in range(rl.W.shape[0]): 126 | for j in range(rl.W.shape[1]): 127 | rl.W[i,j] += epsilon 128 | rl.reset_state() 129 | rl.forward(x[0]) 130 | rl.forward(x[1]) 131 | err1 = error_function(rl.state_list[-1]) 132 | rl.W[i,j] -= 2*epsilon 133 | rl.reset_state() 134 | rl.forward(x[0]) 135 | rl.forward(x[1]) 136 | err2 = error_function(rl.state_list[-1]) 137 | expect_grad = (err1 - err2) / (2 * epsilon) 138 | rl.W[i,j] += epsilon 139 | print('weights(%d,%d): expected - actural %f - %f' % ( 140 | i, j, expect_grad, rl.gradient[i,j])) 141 | 142 | 143 | def test(): 144 | l = RecurrentLayer(3, 2, ReluActivator(), 1e-3) 145 | x, d = data_set() 146 | l.forward(x[0]) 147 | l.forward(x[1]) 148 | l.backward(d, ReluActivator()) 149 | return l -------------------------------------------------------------------------------- /Tutorial/lesson-6/activators.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | 4 | import numpy as np 5 | 6 | 7 | class ReluActivator(object): 8 | def forward(self, weighted_input): 9 | #return weighted_input 10 | return max(0, weighted_input) 11 | 12 | def backward(self, output): 13 | return 1 if output > 0 else 0 14 | 15 | 16 | class IdentityActivator(object): 17 | def forward(self, weighted_input): 18 | return weighted_input 19 | 20 | def backward(self, output): 21 | return 1 22 | 23 | 24 | class SigmoidActivator(object): 25 | def forward(self, weighted_input): 26 | return 1.0 / (1.0 + np.exp(-weighted_input)) 27 | 28 | def backward(self, output): 29 | return output * (1 - output) 30 | 31 | 32 | class TanhActivator(object): 33 | def forward(self, weighted_input): 34 | return 2.0 / (1.0 + np.exp(-2 * weighted_input)) - 1.0 35 | 36 | def backward(self, output): 37 | return 1 - output * output -------------------------------------------------------------------------------- /Tutorial/lesson-6/cnn.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | import numpy as np 3 | from activators import ReluActivator, IdentityActivator 4 | 5 | 6 | # 获取卷积区域 7 | def get_patch(input_array, i, j, filter_width, 8 | filter_height, stride): 9 | ''' 10 | 从输入数组中获取本次卷积的区域, 11 | 自动适配输入为2D和3D的情况 12 | ''' 13 | start_i = i * stride 14 | start_j = j * stride 15 | if input_array.ndim == 2: 16 | return input_array[ 17 | start_i : start_i + filter_height, 18 | start_j : start_j + filter_width] 19 | elif input_array.ndim == 3: 20 | return input_array[:, 21 | start_i : start_i + filter_height, 22 | start_j : start_j + filter_width] 23 | 24 | 25 | # 获取一个2D区域的最大值所在的索引 26 | def get_max_index(array): 27 | max_i = 0 28 | max_j = 0 29 | max_value = array[0,0] 30 | for i in range(array.shape[0]): 31 | for j in range(array.shape[1]): 32 | if array[i,j] > max_value: 33 | max_value = array[i,j] 34 | max_i, max_j = i, j 35 | return max_i, max_j 36 | 37 | 38 | # 计算卷积 39 | def conv(input_array, 40 | kernel_array, 41 | output_array, 42 | stride, bias): 43 | ''' 44 | 计算卷积,自动适配输入为2D和3D的情况 45 | ''' 46 | channel_number = input_array.ndim 47 | output_width = output_array.shape[1] 48 | output_height = output_array.shape[0] 49 | kernel_width = kernel_array.shape[-1] 50 | kernel_height = kernel_array.shape[-2] 51 | for i in range(output_height): 52 | for j in range(output_width): 53 | output_array[i][j] = ( 54 | get_patch(input_array, i, j, kernel_width, 55 | kernel_height, stride) * kernel_array 56 | ).sum() + bias 57 | 58 | 59 | # 为数组增加Zero padding 60 | def padding(input_array, zp): 61 | ''' 62 | 为数组增加Zero padding,自动适配输入为2D和3D的情况 63 | ''' 64 | if zp == 0: 65 | return input_array 66 | else: 67 | if input_array.ndim == 3: 68 | input_width = input_array.shape[2] 69 | input_height = input_array.shape[1] 70 | input_depth = input_array.shape[0] 71 | padded_array = np.zeros(( 72 | input_depth, 73 | input_height + 2 * zp, 74 | input_width + 2 * zp)) 75 | padded_array[:, 76 | zp : zp + input_height, 77 | zp : zp + input_width] = input_array 78 | return padded_array 79 | elif input_array.ndim == 2: 80 | input_width = input_array.shape[1] 81 | input_height = input_array.shape[0] 82 | padded_array = np.zeros(( 83 | input_height + 2 * zp, 84 | input_width + 2 * zp)) 85 | padded_array[zp : zp + input_height, 86 | zp : zp + input_width] = input_array 87 | return padded_array 88 | 89 | 90 | # 对numpy数组进行element wise操作 91 | def element_wise_op(array, op): 92 | for i in np.nditer(array, 93 | op_flags=['readwrite']): 94 | i[...] = op(i) 95 | 96 | 97 | class Filter(object): 98 | def __init__(self, width, height, depth): 99 | self.weights = np.random.uniform(-1e-4, 1e-4, 100 | (depth, height, width)) 101 | self.bias = 0 102 | self.weights_grad = np.zeros( 103 | self.weights.shape) 104 | self.bias_grad = 0 105 | 106 | def __repr__(self): 107 | return 'filter weights:\n%s\nbias:\n%s' % ( 108 | repr(self.weights), repr(self.bias)) 109 | 110 | def get_weights(self): 111 | return self.weights 112 | 113 | def get_bias(self): 114 | return self.bias 115 | 116 | def update(self, learning_rate): 117 | self.weights -= learning_rate * self.weights_grad 118 | self.bias -= learning_rate * self.bias_grad 119 | 120 | 121 | class ConvLayer(object): 122 | def __init__(self, input_width, input_height, 123 | channel_number, filter_width, 124 | filter_height, filter_number, 125 | zero_padding, stride, activator, 126 | learning_rate): 127 | self.input_width = input_width 128 | self.input_height = input_height 129 | self.channel_number = channel_number 130 | self.filter_width = filter_width 131 | self.filter_height = filter_height 132 | self.filter_number = filter_number 133 | self.zero_padding = zero_padding 134 | self.stride = stride 135 | self.output_width = \ 136 | ConvLayer.calculate_output_size( 137 | self.input_width, filter_width, zero_padding, 138 | stride) 139 | self.output_height = \ 140 | ConvLayer.calculate_output_size( 141 | self.input_height, filter_height, zero_padding, 142 | stride) 143 | self.output_array = np.zeros((self.filter_number, 144 | self.output_height, self.output_width)) 145 | self.filters = [] 146 | for i in range(filter_number): 147 | self.filters.append(Filter(filter_width, 148 | filter_height, self.channel_number)) 149 | self.activator = activator 150 | self.learning_rate = learning_rate 151 | 152 | def forward(self, input_array): 153 | ''' 154 | 计算卷积层的输出 155 | 输出结果保存在self.output_array 156 | ''' 157 | self.input_array = input_array 158 | self.padded_input_array = padding(input_array, 159 | self.zero_padding) 160 | for f in range(self.filter_number): 161 | filter = self.filters[f] 162 | conv(self.padded_input_array, 163 | filter.get_weights(), self.output_array[f], 164 | self.stride, filter.get_bias()) 165 | element_wise_op(self.output_array, 166 | self.activator.forward) 167 | 168 | def backward(self, input_array, sensitivity_array, 169 | activator): 170 | ''' 171 | 计算传递给前一层的误差项,以及计算每个权重的梯度 172 | 前一层的误差项保存在self.delta_array 173 | 梯度保存在Filter对象的weights_grad 174 | ''' 175 | self.forward(input_array) 176 | self.bp_sensitivity_map(sensitivity_array, 177 | activator) 178 | self.bp_gradient(sensitivity_array) 179 | 180 | def update(self): 181 | ''' 182 | 按照梯度下降,更新权重 183 | ''' 184 | for filter in self.filters: 185 | filter.update(self.learning_rate) 186 | 187 | def bp_sensitivity_map(self, sensitivity_array, 188 | activator): 189 | ''' 190 | 计算传递到上一层的sensitivity map 191 | sensitivity_array: 本层的sensitivity map 192 | activator: 上一层的激活函数 193 | ''' 194 | # 处理卷积步长,对原始sensitivity map进行扩展 195 | expanded_array = self.expand_sensitivity_map( 196 | sensitivity_array) 197 | # full卷积,对sensitivitiy map进行zero padding 198 | # 虽然原始输入的zero padding单元也会获得残差 199 | # 但这个残差不需要继续向上传递,因此就不计算了 200 | expanded_width = expanded_array.shape[2] 201 | zp = int((self.input_width + 202 | self.filter_width - 1 - expanded_width) / 2) 203 | padded_array = padding(expanded_array, zp) 204 | # 初始化delta_array,用于保存传递到上一层的 205 | # sensitivity map 206 | self.delta_array = self.create_delta_array() 207 | # 对于具有多个filter的卷积层来说,最终传递到上一层的 208 | # sensitivity map相当于所有的filter的 209 | # sensitivity map之和 210 | for f in range(self.filter_number): 211 | filter = self.filters[f] 212 | # 将filter权重翻转180度 213 | flipped_weights = np.array(list(map( 214 | lambda i: np.rot90(i, 2), 215 | filter.get_weights()))) 216 | # 计算与一个filter对应的delta_array 217 | delta_array = self.create_delta_array() 218 | for d in range(delta_array.shape[0]): 219 | conv(padded_array[f], flipped_weights[d],delta_array[d], 1, 0) 220 | self.delta_array += delta_array 221 | # 将计算结果与激活函数的偏导数做element-wise乘法操作 222 | derivative_array = np.array(self.input_array) 223 | element_wise_op(derivative_array, 224 | activator.backward) 225 | self.delta_array *= derivative_array 226 | 227 | def bp_gradient(self, sensitivity_array): 228 | # 处理卷积步长,对原始sensitivity map进行扩展 229 | expanded_array = self.expand_sensitivity_map( 230 | sensitivity_array) 231 | for f in range(self.filter_number): 232 | # 计算每个权重的梯度 233 | filter = self.filters[f] 234 | for d in range(filter.weights.shape[0]): 235 | conv(self.padded_input_array[d], 236 | expanded_array[f], 237 | filter.weights_grad[d], 1, 0) 238 | # 计算偏置项的梯度 239 | filter.bias_grad = expanded_array[f].sum() 240 | 241 | def expand_sensitivity_map(self, sensitivity_array): 242 | depth = sensitivity_array.shape[0] 243 | # 确定扩展后sensitivity map的大小 244 | # 计算stride为1时sensitivity map的大小 245 | expanded_width = (self.input_width - 246 | self.filter_width + 2 * self.zero_padding + 1) 247 | expanded_height = (self.input_height - 248 | self.filter_height + 2 * self.zero_padding + 1) 249 | # 构建新的sensitivity_map 250 | expand_array = np.zeros((depth, expanded_height, 251 | expanded_width)) 252 | # 从原始sensitivity map拷贝误差值 253 | for i in range(self.output_height): 254 | for j in range(self.output_width): 255 | i_pos = i * self.stride 256 | j_pos = j * self.stride 257 | expand_array[:,i_pos,j_pos] = \ 258 | sensitivity_array[:,i,j] 259 | return expand_array 260 | 261 | def create_delta_array(self): 262 | return np.zeros((self.channel_number, 263 | self.input_height, self.input_width)) 264 | 265 | @staticmethod 266 | def calculate_output_size(input_size, 267 | filter_size, zero_padding, stride): 268 | return int((input_size - filter_size + 269 | 2 * zero_padding) / stride + 1) 270 | 271 | 272 | class MaxPoolingLayer(object): 273 | def __init__(self, input_width, input_height, 274 | channel_number, filter_width, 275 | filter_height, stride): 276 | self.input_width = input_width 277 | self.input_height = input_height 278 | self.channel_number = channel_number 279 | self.filter_width = filter_width 280 | self.filter_height = filter_height 281 | self.stride = stride 282 | self.output_width = int((input_width - 283 | filter_width) / self.stride + 1) 284 | self.output_height = int((input_height - 285 | filter_height) / self.stride + 1) 286 | self.output_array = np.zeros((self.channel_number, 287 | self.output_height, self.output_width)) 288 | 289 | def forward(self, input_array): 290 | for d in range(self.channel_number): 291 | for i in range(self.output_height): 292 | for j in range(self.output_width): 293 | self.output_array[d,i,j] = ( 294 | get_patch(input_array[d], i, j, 295 | self.filter_width, 296 | self.filter_height, 297 | self.stride).max()) 298 | 299 | def backward(self, input_array, sensitivity_array): 300 | self.delta_array = np.zeros(input_array.shape) 301 | for d in range(self.channel_number): 302 | for i in range(self.output_height): 303 | for j in range(self.output_width): 304 | patch_array = get_patch( 305 | input_array[d], i, j, 306 | self.filter_width, 307 | self.filter_height, 308 | self.stride) 309 | k, l = get_max_index(patch_array) 310 | self.delta_array[d, 311 | i * self.stride + k, 312 | j * self.stride + l] = \ 313 | sensitivity_array[d,i,j] 314 | 315 | 316 | def init_test(): 317 | a = np.array( 318 | [[[0,1,1,0,2], 319 | [2,2,2,2,1], 320 | [1,0,0,2,0], 321 | [0,1,1,0,0], 322 | [1,2,0,0,2]], 323 | [[1,0,2,2,0], 324 | [0,0,0,2,0], 325 | [1,2,1,2,1], 326 | [1,0,0,0,0], 327 | [1,2,1,1,1]], 328 | [[2,1,2,0,0], 329 | [1,0,0,1,0], 330 | [0,2,1,0,1], 331 | [0,1,2,2,2], 332 | [2,1,0,0,1]]]) 333 | b = np.array( 334 | [[[0,1,1], 335 | [2,2,2], 336 | [1,0,0]], 337 | [[1,0,2], 338 | [0,0,0], 339 | [1,2,1]]]) 340 | cl = ConvLayer(5,5,3,3,3,2,1,2,IdentityActivator(),0.001) 341 | cl.filters[0].weights = np.array( 342 | [[[-1,1,0], 343 | [0,1,0], 344 | [0,1,1]], 345 | [[-1,-1,0], 346 | [0,0,0], 347 | [0,-1,0]], 348 | [[0,0,-1], 349 | [0,1,0], 350 | [1,-1,-1]]], dtype=np.float64) 351 | cl.filters[0].bias=1 352 | cl.filters[1].weights = np.array( 353 | [[[1,1,-1], 354 | [-1,-1,1], 355 | [0,-1,1]], 356 | [[0,1,0], 357 | [-1,0,-1], 358 | [-1,1,0]], 359 | [[-1,0,0], 360 | [-1,0,1], 361 | [-1,0,0]]], dtype=np.float64) 362 | return a, b, cl 363 | 364 | 365 | def test(): 366 | a, b, cl = init_test() 367 | cl.forward(a) 368 | print(cl.output_array) 369 | 370 | def test_bp(): 371 | a, b, cl = init_test() 372 | cl.backward(a, b, IdentityActivator()) 373 | cl.update() 374 | print(cl.filters[0]) 375 | print(cl.filters[1]) 376 | 377 | 378 | def gradient_check(): 379 | ''' 380 | 梯度检查 381 | ''' 382 | # 设计一个误差函数,取所有节点输出项之和 383 | error_function = lambda o: o.sum() 384 | 385 | # 计算forward值 386 | a, b, cl = init_test() 387 | cl.forward(a) 388 | 389 | # 求取sensitivity map 390 | sensitivity_array = np.ones(cl.output_array.shape, 391 | dtype=np.float64) 392 | # 计算梯度 393 | cl.backward(a, sensitivity_array, 394 | IdentityActivator()) 395 | # 检查梯度 396 | epsilon = 10e-4 397 | for d in range(cl.filters[0].weights_grad.shape[0]): 398 | for i in range(cl.filters[0].weights_grad.shape[1]): 399 | for j in range(cl.filters[0].weights_grad.shape[2]): 400 | cl.filters[0].weights[d,i,j] += epsilon 401 | cl.forward(a) 402 | err1 = error_function(cl.output_array) 403 | cl.filters[0].weights[d,i,j] -= 2*epsilon 404 | cl.forward(a) 405 | err2 = error_function(cl.output_array) 406 | expect_grad = int((err1 - err2) / (2 * epsilon)) 407 | cl.filters[0].weights[d,i,j] += epsilon 408 | print('weights(%d,%d,%d): expected - actural %f - %f' % ( 409 | d, i, j, expect_grad, cl.filters[0].weights_grad[d,i,j])) 410 | 411 | 412 | def init_pool_test(): 413 | a = np.array( 414 | [[[1,1,2,4], 415 | [5,6,7,8], 416 | [3,2,1,0], 417 | [1,2,3,4]], 418 | [[0,1,2,3], 419 | [4,5,6,7], 420 | [8,9,0,1], 421 | [3,4,5,6]]], dtype=np.float64) 422 | 423 | b = np.array( 424 | [[[1,2], 425 | [2,4]], 426 | [[3,5], 427 | [8,2]]], dtype=np.float64) 428 | 429 | mpl = MaxPoolingLayer(4,4,2,2,2,2) 430 | 431 | return a, b, mpl 432 | 433 | 434 | def test_pool(): 435 | a, b, mpl = init_pool_test() 436 | mpl.forward(a) 437 | print('input array:\n%s\noutput array:\n%s' % (a, 438 | mpl.output_array)) 439 | 440 | 441 | def test_pool_bp(): 442 | a, b, mpl = init_pool_test() 443 | mpl.backward(a, b) 444 | print('input array:\n%s\nsensitivity array:\n%s\ndelta array:\n%s' % ( 445 | a, b, mpl.delta_array)) 446 | -------------------------------------------------------------------------------- /Tutorial/lesson-6/lstm.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | from cnn import element_wise_op 5 | from activators import SigmoidActivator, TanhActivator, IdentityActivator 6 | 7 | class LstmLayer(): 8 | def __init__(self, input_width, state_width, 9 | learning_rate): 10 | self.input_width = input_width 11 | self.state_width = state_width 12 | self.learning_rate = learning_rate 13 | # 门的激活函数 14 | self.gate_activator = SigmoidActivator() 15 | # 输出的激活函数 16 | self.output_activator = TanhActivator() 17 | # 当前时刻初始化为t0 18 | self.times = 0 19 | # 各个时刻的单元状态向量c 20 | self.c_list = self.init_state_vec() 21 | # 各个时刻的输出向量h 22 | self.h_list = self.init_state_vec() 23 | # 各个时刻的遗忘门f 24 | self.f_list = self.init_state_vec() 25 | # 各个时刻的输入门i 26 | self.i_list = self.init_state_vec() 27 | # 各个时刻的输出门o 28 | self.o_list = self.init_state_vec() 29 | # 各个时刻的即时状态c~ 30 | self.ct_list = self.init_state_vec() 31 | # 遗忘门权重矩阵Wfh, Wfx, 偏置项bf 32 | self.Wfh, self.Wfx, self.bf = ( 33 | self.init_weight_mat()) 34 | # 输入门权重矩阵Wfh, Wfx, 偏置项bf 35 | self.Wih, self.Wix, self.bi = ( 36 | self.init_weight_mat()) 37 | # 输出门权重矩阵Wfh, Wfx, 偏置项bf 38 | self.Woh, self.Wox, self.bo = ( 39 | self.init_weight_mat()) 40 | # 单元状态权重矩阵Wfh, Wfx, 偏置项bf 41 | self.Wch, self.Wcx, self.bc = ( 42 | self.init_weight_mat()) 43 | 44 | def init_state_vec(self): 45 | ''' 46 | 初始化保存状态的向量 47 | ''' 48 | state_vec_list = [] 49 | state_vec_list.append(np.zeros( 50 | (self.state_width, 1))) 51 | return state_vec_list 52 | 53 | def init_weight_mat(self): 54 | ''' 55 | 初始化权重矩阵 56 | ''' 57 | Wh = np.random.uniform(-1e-4, 1e-4, 58 | (self.state_width, self.state_width)) 59 | Wx = np.random.uniform(-1e-4, 1e-4, 60 | (self.state_width, self.input_width)) 61 | b = np.zeros((self.state_width, 1)) 62 | return Wh, Wx, b 63 | 64 | def forward(self, x): 65 | ''' 66 | 根据式1-式6进行前向计算 67 | ''' 68 | self.times += 1 69 | # 遗忘门 70 | fg = self.calc_gate(x, self.Wfx, self.Wfh, 71 | self.bf, self.gate_activator) 72 | self.f_list.append(fg) 73 | # 输入门 74 | ig = self.calc_gate(x, self.Wix, self.Wih, 75 | self.bi, self.gate_activator) 76 | self.i_list.append(ig) 77 | # 输出门 78 | og = self.calc_gate(x, self.Wox, self.Woh, 79 | self.bo, self.gate_activator) 80 | self.o_list.append(og) 81 | # 即时状态 82 | ct = self.calc_gate(x, self.Wcx, self.Wch, 83 | self.bc, self.output_activator) 84 | self.ct_list.append(ct) 85 | # 单元状态 86 | c = fg * self.c_list[self.times - 1] + ig * ct 87 | self.c_list.append(c) 88 | # 输出 89 | h = og * self.output_activator.forward(c) 90 | self.h_list.append(h) 91 | 92 | def calc_gate(self, x, Wx, Wh, b, activator): 93 | ''' 94 | 计算门 95 | ''' 96 | h = self.h_list[self.times - 1] # 上次的LSTM输出 97 | net = np.dot(Wh, h) + np.dot(Wx, x) + b 98 | gate = activator.forward(net) 99 | return gate 100 | 101 | 102 | def backward(self, x, delta_h, activator): 103 | ''' 104 | 实现LSTM训练算法 105 | ''' 106 | self.calc_delta(delta_h, activator) 107 | self.calc_gradient(x) 108 | 109 | def update(self): 110 | ''' 111 | 按照梯度下降,更新权重 112 | ''' 113 | self.Wfh -= self.learning_rate * self.Whf_grad 114 | self.Wfx -= self.learning_rate * self.Whx_grad 115 | self.bf -= self.learning_rate * self.bf_grad 116 | self.Wih -= self.learning_rate * self.Whi_grad 117 | self.Wix -= self.learning_rate * self.Whi_grad 118 | self.bi -= self.learning_rate * self.bi_grad 119 | self.Woh -= self.learning_rate * self.Wof_grad 120 | self.Wox -= self.learning_rate * self.Wox_grad 121 | self.bo -= self.learning_rate * self.bo_grad 122 | self.Wch -= self.learning_rate * self.Wcf_grad 123 | self.Wcx -= self.learning_rate * self.Wcx_grad 124 | self.bc -= self.learning_rate * self.bc_grad 125 | 126 | def calc_delta(self, delta_h, activator): 127 | # 初始化各个时刻的误差项 128 | self.delta_h_list = self.init_delta() # 输出误差项 129 | self.delta_o_list = self.init_delta() # 输出门误差项 130 | self.delta_i_list = self.init_delta() # 输入门误差项 131 | self.delta_f_list = self.init_delta() # 遗忘门误差项 132 | self.delta_ct_list = self.init_delta() # 即时输出误差项 133 | 134 | # 保存从上一层传递下来的当前时刻的误差项 135 | self.delta_h_list[-1] = delta_h 136 | 137 | # 迭代计算每个时刻的误差项 138 | for k in range(self.times, 0, -1): 139 | self.calc_delta_k(k) 140 | 141 | def init_delta(self): 142 | ''' 143 | 初始化误差项 144 | ''' 145 | delta_list = [] 146 | for i in range(self.times + 1): 147 | delta_list.append(np.zeros( 148 | (self.state_width, 1))) 149 | return delta_list 150 | 151 | def calc_delta_k(self, k): 152 | ''' 153 | 根据k时刻的delta_h,计算k时刻的delta_f、 154 | delta_i、delta_o、delta_ct,以及k-1时刻的delta_h 155 | ''' 156 | # 获得k时刻前向计算的值 157 | ig = self.i_list[k] 158 | og = self.o_list[k] 159 | fg = self.f_list[k] 160 | ct = self.ct_list[k] 161 | c = self.c_list[k] 162 | c_prev = self.c_list[k-1] 163 | tanh_c = self.output_activator.forward(c) 164 | delta_k = self.delta_h_list[k] 165 | 166 | # 根据式9计算delta_o 167 | delta_o = (delta_k * tanh_c * 168 | self.gate_activator.backward(og)) 169 | delta_f = (delta_k * og * 170 | (1 - tanh_c * tanh_c) * c_prev * 171 | self.gate_activator.backward(fg)) 172 | delta_i = (delta_k * og * 173 | (1 - tanh_c * tanh_c) * ct * 174 | self.gate_activator.backward(ig)) 175 | delta_ct = (delta_k * og * 176 | (1 - tanh_c * tanh_c) * ig * 177 | self.output_activator.backward(ct)) 178 | delta_h_prev = ( 179 | np.dot(delta_o.transpose(), self.Woh) + 180 | np.dot(delta_i.transpose(), self.Wih) + 181 | np.dot(delta_f.transpose(), self.Wfh) + 182 | np.dot(delta_ct.transpose(), self.Wch) 183 | ).transpose() 184 | 185 | # 保存全部delta值 186 | self.delta_h_list[k-1] = delta_h_prev 187 | self.delta_f_list[k] = delta_f 188 | self.delta_i_list[k] = delta_i 189 | self.delta_o_list[k] = delta_o 190 | self.delta_ct_list[k] = delta_ct 191 | 192 | def calc_gradient(self, x): 193 | # 初始化遗忘门权重梯度矩阵和偏置项 194 | self.Wfh_grad, self.Wfx_grad, self.bf_grad = ( 195 | self.init_weight_gradient_mat()) 196 | # 初始化输入门权重梯度矩阵和偏置项 197 | self.Wih_grad, self.Wix_grad, self.bi_grad = ( 198 | self.init_weight_gradient_mat()) 199 | # 初始化输出门权重梯度矩阵和偏置项 200 | self.Woh_grad, self.Wox_grad, self.bo_grad = ( 201 | self.init_weight_gradient_mat()) 202 | # 初始化单元状态权重梯度矩阵和偏置项 203 | self.Wch_grad, self.Wcx_grad, self.bc_grad = ( 204 | self.init_weight_gradient_mat()) 205 | 206 | # 计算对上一次输出h的权重梯度 207 | for t in range(self.times, 0, -1): 208 | # 计算各个时刻的梯度 209 | (Wfh_grad, bf_grad, 210 | Wih_grad, bi_grad, 211 | Woh_grad, bo_grad, 212 | Wch_grad, bc_grad) = ( 213 | self.calc_gradient_t(t)) 214 | # 实际梯度是各时刻梯度之和 215 | self.Wfh_grad += Wfh_grad 216 | self.bf_grad += bf_grad 217 | self.Wih_grad += Wih_grad 218 | self.bi_grad += bi_grad 219 | self.Woh_grad += Woh_grad 220 | self.bo_grad += bo_grad 221 | self.Wch_grad += Wch_grad 222 | self.bc_grad += bc_grad 223 | 224 | # 计算对本次输入x的权重梯度 225 | xt = x.transpose() 226 | self.Wfx_grad = np.dot(self.delta_f_list[-1], xt) 227 | self.Wix_grad = np.dot(self.delta_i_list[-1], xt) 228 | self.Wox_grad = np.dot(self.delta_o_list[-1], xt) 229 | self.Wcx_grad = np.dot(self.delta_ct_list[-1], xt) 230 | 231 | def init_weight_gradient_mat(self): 232 | ''' 233 | 初始化权重矩阵 234 | ''' 235 | Wh_grad = np.zeros((self.state_width, 236 | self.state_width)) 237 | Wx_grad = np.zeros((self.state_width, 238 | self.input_width)) 239 | b_grad = np.zeros((self.state_width, 1)) 240 | return Wh_grad, Wx_grad, b_grad 241 | 242 | def calc_gradient_t(self, t): 243 | ''' 244 | 计算每个时刻t权重的梯度 245 | ''' 246 | h_prev = self.h_list[t-1].transpose() 247 | Wfh_grad = np.dot(self.delta_f_list[t], h_prev) 248 | bf_grad = self.delta_f_list[t] 249 | Wih_grad = np.dot(self.delta_i_list[t], h_prev) 250 | bi_grad = self.delta_f_list[t] 251 | Woh_grad = np.dot(self.delta_o_list[t], h_prev) 252 | bo_grad = self.delta_f_list[t] 253 | Wch_grad = np.dot(self.delta_ct_list[t], h_prev) 254 | bc_grad = self.delta_ct_list[t] 255 | return Wfh_grad, bf_grad, Wih_grad, bi_grad, \ 256 | Woh_grad, bo_grad, Wch_grad, bc_grad 257 | 258 | def reset_state(self): 259 | # 当前时刻初始化为t0 260 | self.times = 0 261 | # 各个时刻的单元状态向量c 262 | self.c_list = self.init_state_vec() 263 | # 各个时刻的输出向量h 264 | self.h_list = self.init_state_vec() 265 | # 各个时刻的遗忘门f 266 | self.f_list = self.init_state_vec() 267 | # 各个时刻的输入门i 268 | self.i_list = self.init_state_vec() 269 | # 各个时刻的输出门o 270 | self.o_list = self.init_state_vec() 271 | # 各个时刻的即时状态c~ 272 | self.ct_list = self.init_state_vec() 273 | 274 | 275 | def data_set(): 276 | x = [np.array([[1], [2], [3]]), 277 | np.array([[2], [3], [4]])] 278 | d = np.array([[1], [2]]) 279 | return x, d 280 | 281 | 282 | def gradient_check(): 283 | ''' 284 | 梯度检查 285 | ''' 286 | # 设计一个误差函数,取所有节点输出项之和 287 | error_function = lambda o: o.sum() 288 | 289 | lstm = LstmLayer(3, 2, 1e-3) 290 | 291 | # 计算forward值 292 | x, d = data_set() 293 | lstm.forward(x[0]) 294 | lstm.forward(x[1]) 295 | 296 | # 求取sensitivity map 297 | sensitivity_array = np.ones(lstm.h_list[-1].shape, 298 | dtype=np.float64) 299 | # 计算梯度 300 | lstm.backward(x[1], sensitivity_array, IdentityActivator()) 301 | 302 | # 检查梯度 303 | epsilon = 10e-4 304 | for i in range(lstm.Wfh.shape[0]): 305 | for j in range(lstm.Wfh.shape[1]): 306 | lstm.Wfh[i,j] += epsilon 307 | lstm.reset_state() 308 | lstm.forward(x[0]) 309 | lstm.forward(x[1]) 310 | err1 = error_function(lstm.h_list[-1]) 311 | lstm.Wfh[i,j] -= 2*epsilon 312 | lstm.reset_state() 313 | lstm.forward(x[0]) 314 | lstm.forward(x[1]) 315 | err2 = error_function(lstm.h_list[-1]) 316 | expect_grad = (err1 - err2) / (2 * epsilon) 317 | lstm.Wfh[i,j] += epsilon 318 | print('weights(%d,%d): expected - actural %.4e - %.4e' % ( 319 | i, j, expect_grad, lstm.Wfh_grad[i,j])) 320 | return lstm 321 | 322 | 323 | def test(): 324 | l = LstmLayer(3, 2, 1e-3) 325 | x, d = data_set() 326 | l.forward(x[0]) 327 | l.forward(x[1]) 328 | l.backward(x[1], d, IdentityActivator()) 329 | return l -------------------------------------------------------------------------------- /Tutorial/lesson-7/activators.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | 4 | import numpy as np 5 | 6 | 7 | class ReluActivator(object): 8 | def forward(self, weighted_input): 9 | #return weighted_input 10 | return max(0, weighted_input) 11 | 12 | def backward(self, output): 13 | return 1 if output > 0 else 0 14 | 15 | 16 | class IdentityActivator(object): 17 | def forward(self, weighted_input): 18 | return weighted_input 19 | 20 | def backward(self, output): 21 | return 1 22 | 23 | 24 | class SigmoidActivator(object): 25 | def forward(self, weighted_input): 26 | return 1.0 / (1.0 + np.exp(-weighted_input)) 27 | 28 | def backward(self, output): 29 | return output * (1 - output) 30 | 31 | 32 | class TanhActivator(object): 33 | def forward(self, weighted_input): 34 | return 2.0 / (1.0 + np.exp(-2 * weighted_input)) - 1.0 35 | 36 | def backward(self, output): 37 | return 1 - output * output -------------------------------------------------------------------------------- /Tutorial/lesson-7/recursive.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | 4 | 5 | import numpy as np 6 | from activators import IdentityActivator 7 | 8 | 9 | class TreeNode(object): 10 | def __init__(self, data, children=[], children_data=[]): 11 | self.parent = None 12 | self.children = children 13 | self.children_data = children_data 14 | self.data = data 15 | for child in children: 16 | child.parent = self 17 | 18 | # 递归神经网络实现 19 | class RecursiveLayer(object): 20 | def __init__(self, node_width, child_count, 21 | activator, learning_rate): 22 | ''' 23 | 递归神经网络构造函数 24 | node_width: 表示每个节点的向量的维度 25 | child_count: 每个父节点有几个子节点 26 | activator: 激活函数对象 27 | learning_rate: 梯度下降算法学习率 28 | ''' 29 | self.node_width = node_width 30 | self.child_count = child_count 31 | self.activator = activator 32 | self.learning_rate = learning_rate 33 | # 权重数组W 34 | self.W = np.random.uniform(-1e-4, 1e-4, 35 | (node_width, node_width * child_count)) 36 | # 偏置项b 37 | self.b = np.zeros((node_width, 1)) 38 | # 递归神经网络生成的树的根节点 39 | self.root = None 40 | 41 | def forward(self, *children): 42 | ''' 43 | 前向计算 44 | ''' 45 | children_data = self.concatenate(children) 46 | parent_data = self.activator.forward( 47 | np.dot(self.W, children_data) + self.b 48 | ) 49 | self.root = TreeNode(parent_data, children 50 | , children_data) 51 | 52 | def backward(self, parent_delta): 53 | ''' 54 | BPTS反向传播算法 55 | ''' 56 | self.calc_delta(parent_delta, self.root) 57 | self.W_grad, self.b_grad = self.calc_gradient(self.root) 58 | 59 | def update(self): 60 | ''' 61 | 使用SGD算法更新权重 62 | ''' 63 | self.W -= self.learning_rate * self.W_grad 64 | self.b -= self.learning_rate * self.b_grad 65 | 66 | def reset_state(self): 67 | self.root = None 68 | 69 | def concatenate(self, tree_nodes): 70 | ''' 71 | 将各个树节点中的数据拼接成一个长向量 72 | ''' 73 | concat = np.zeros((0,1)) 74 | for node in tree_nodes: 75 | concat = np.concatenate((concat, node.data)) 76 | return concat 77 | 78 | def calc_delta(self, parent_delta, parent): 79 | ''' 80 | 计算每个节点的delta 81 | ''' 82 | parent.delta = parent_delta 83 | if parent.children: 84 | # 根据式2计算每个子节点的delta 85 | children_delta = np.dot(self.W.T, parent_delta) * ( 86 | self.activator.backward(parent.children_data) 87 | ) 88 | # slices = [(子节点编号,子节点delta起始位置,子节点delta结束位置)] 89 | slices = [(i, i * self.node_width, 90 | (i + 1) * self.node_width) 91 | for i in range(self.child_count)] 92 | # 针对每个子节点,递归调用calc_delta函数 93 | for s in slices: 94 | self.calc_delta(children_delta[s[1]:s[2]], 95 | parent.children[s[0]]) 96 | 97 | def calc_gradient(self, parent): 98 | ''' 99 | 计算每个节点权重的梯度,并将它们求和,得到最终的梯度 100 | ''' 101 | W_grad = np.zeros((self.node_width, 102 | self.node_width * self.child_count)) 103 | b_grad = np.zeros((self.node_width, 1)) 104 | if not parent.children: 105 | return W_grad, b_grad 106 | parent.W_grad = np.dot(parent.delta, parent.children_data.T) 107 | parent.b_grad = parent.delta 108 | W_grad += parent.W_grad 109 | b_grad += parent.b_grad 110 | for child in parent.children: 111 | W, b = self.calc_gradient(child) 112 | W_grad += W 113 | b_grad += b 114 | return W_grad, b_grad 115 | 116 | def dump(self, **kwArgs): 117 | print('root.data: %s' % self.root.data) 118 | print('root.children_data: %s' % self.root.children_data) 119 | if kwArgs.has_key('dump_grad'): 120 | print('W_grad: %s' % self.W_grad) 121 | print('b_grad: %s' % self.b_grad) 122 | 123 | 124 | def data_set(): 125 | children = [ 126 | TreeNode(np.array([[1],[2]])), 127 | TreeNode(np.array([[3],[4]])), 128 | TreeNode(np.array([[5],[6]])) 129 | ] 130 | d = np.array([[0.5],[0.8]]) 131 | return children, d 132 | 133 | 134 | def gradient_check(): 135 | ''' 136 | 梯度检查 137 | ''' 138 | # 设计一个误差函数,取所有节点输出项之和 139 | error_function = lambda o: o.sum() 140 | 141 | rnn = RecursiveLayer(2, 2, IdentityActivator(), 1e-3) 142 | 143 | # 计算forward值 144 | x, d = data_set() 145 | rnn.forward(x[0], x[1]) 146 | rnn.forward(rnn.root, x[2]) 147 | 148 | # 求取sensitivity map 149 | sensitivity_array = np.ones((rnn.node_width, 1), 150 | dtype=np.float64) 151 | # 计算梯度 152 | rnn.backward(sensitivity_array) 153 | 154 | # 检查梯度 155 | epsilon = 10e-4 156 | for i in range(rnn.W.shape[0]): 157 | for j in range(rnn.W.shape[1]): 158 | rnn.W[i,j] += epsilon 159 | rnn.reset_state() 160 | rnn.forward(x[0], x[1]) 161 | rnn.forward(rnn.root, x[2]) 162 | err1 = error_function(rnn.root.data) 163 | rnn.W[i,j] -= 2*epsilon 164 | rnn.reset_state() 165 | rnn.forward(x[0], x[1]) 166 | rnn.forward(rnn.root, x[2]) 167 | err2 = error_function(rnn.root.data) 168 | expect_grad = (err1 - err2) / (2 * epsilon) 169 | rnn.W[i,j] += epsilon 170 | print('weights(%d,%d): expected - actural %.4e - %.4e' % ( 171 | i, j, expect_grad, rnn.W_grad[i,j])) 172 | return rnn 173 | 174 | 175 | def test(): 176 | children, d = data_set() 177 | rnn = RecursiveLayer(2, 2, IdentityActivator(), 1e-3) 178 | rnn.forward(children[0], children[1]) 179 | rnn.dump() 180 | rnn.forward(rnn.root, children[2]) 181 | rnn.dump() 182 | rnn.backward(d) 183 | rnn.dump(dump_grad='true') 184 | return rnn -------------------------------------------------------------------------------- /face/video_mosaic.py: -------------------------------------------------------------------------------- 1 | # Author : Jack Cui 2 | # Website: https://cuijiahua.com/ 3 | import cv2 4 | import face_recognition 5 | import matplotlib.pyplot as plt 6 | # %matplotlib inline # 在 jupyter 中使用的时候,去掉注释 7 | 8 | import subprocess 9 | import os 10 | from PIL import Image 11 | 12 | def video2mp3(file_name): 13 | """ 14 | 将视频转为音频 15 | :param file_name: 传入视频文件的路径 16 | :return: 17 | """ 18 | outfile_name = file_name.split('.')[0] + '.mp3' 19 | cmd = 'ffmpeg -i ' + file_name + ' -f mp3 ' + outfile_name 20 | print(cmd) 21 | subprocess.call(cmd, shell=True) 22 | 23 | 24 | def video_add_mp3(file_name, mp3_file): 25 | """ 26 | 视频添加音频 27 | :param file_name: 传入视频文件的路径 28 | :param mp3_file: 传入音频文件的路径 29 | :return: 30 | """ 31 | outfile_name = file_name.split('.')[0] + '-f.mp4' 32 | subprocess.call('ffmpeg -i ' + file_name 33 | + ' -i ' + mp3_file + ' -strict -2 -f mp4 ' 34 | + outfile_name, shell=True) 35 | 36 | def mask_video(input_video, output_video, mask_path='mask.jpg'): 37 | # 打码图片 38 | mask = cv2.imread(mask_path) 39 | # 读取视频 40 | cap = cv2.VideoCapture(input_video) 41 | # 读取视频参数,fps、width、heigth 42 | CV_CAP_PROP_FPS = 5 43 | CV_CAP_PROP_FRAME_WIDTH = 3 44 | CV_CAP_PROP_FRAME_HEIGHT = 4 45 | v_fps = cap.get(CV_CAP_PROP_FPS) 46 | v_width = cap.get(CV_CAP_PROP_FRAME_WIDTH) 47 | v_height = cap.get(CV_CAP_PROP_FRAME_HEIGHT) 48 | # 设置写视频参数,格式为 mp4 49 | size = (int(v_width), int(v_height)) 50 | fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v') 51 | out = cv2.VideoWriter(output_video,fourcc, v_fps, size) 52 | 53 | # 已知人脸 54 | known_image = face_recognition.load_image_file("tz.jpg") 55 | biden_encoding = face_recognition.face_encodings(known_image)[0] 56 | # 读取视频 57 | cap = cv2.VideoCapture(input_video) 58 | while(cap.isOpened()): 59 | ret, frame = cap.read() 60 | if ret: 61 | # 检测人脸 62 | face_locations = face_recognition.face_locations(frame) 63 | # 检测每一个人脸 64 | for (top_right_y, top_right_x, left_bottom_y,left_bottom_x) in face_locations: 65 | unknown_image = frame[top_right_y-50:left_bottom_y+50, left_bottom_x-50:top_right_x+50] 66 | unknown_encoding = face_recognition.face_encodings(unknown_image)[0] 67 | # 对比结果 68 | results = face_recognition.compare_faces([biden_encoding], unknown_encoding) 69 | # 是仝卓,就将打码贴图。 70 | if results[0] == True: 71 | mask = cv2.resize(mask, (top_right_x-left_bottom_x, left_bottom_y-top_right_y)) 72 | frame[top_right_y:left_bottom_y, left_bottom_x:top_right_x] = mask 73 | # 写入视频 74 | out.write(frame) 75 | else: 76 | break 77 | 78 | if __name__ == '__main__': 79 | # 将音频保存为cut.mp3 80 | video2mp3(file_name='cut.mp4') 81 | # 处理视频,自动打码,输出视频为output.mp4 82 | mask_video(input_video='cut.mp4', output_video='output.mp4') 83 | # 为 output.mp4 处理好的视频添加声音 84 | video_add_mp3(file_name='output.mp4', mp3_file='cut.mp3') 85 | -------------------------------------------------------------------------------- /mnist.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | from tensorflow.examples.tutorials.mnist import input_data 3 | import os 4 | 5 | ''' 6 | Website:http://cuijiahua.linear_com 7 | Modify:2018-01-23 8 | Author:Jack Cui 9 | ''' 10 | 11 | os.environ["CUDA_VISIBLE_DEVICES"] = "0" 12 | config = tf.ConfigProto(allow_soft_placement = True) 13 | gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction = 0.33) 14 | config.gpu_options.allow_growth = True 15 | 16 | 17 | max_steps = 1000 # 最大迭代次数 18 | learning_rate = 0.001 # 学习率 19 | dropout = 0.9 # dropout时随机保留神经元的比例 20 | data_dir = './MNIST_DATA' # 样本数据存储的路径 21 | log_dir = './MNIST_LOG' # 输出日志保存的路径 22 | 23 | # 获取数据集,并采用采用one_hot热编码 24 | mnist = input_data.read_data_sets(data_dir,one_hot = True) 25 | 26 | sess = tf.InteractiveSession(config = config) 27 | 28 | with tf.name_scope('input'): 29 | x = tf.placeholder(tf.float32, [None, 784], name='x-input') 30 | y_ = tf.placeholder(tf.float32, [None, 10], name='y-input') 31 | 32 | # 保存图像信息 33 | with tf.name_scope('input_reshape'): 34 | image_shaped_input = tf.reshape(x, [-1, 28, 28, 1]) 35 | tf.summary.image('input', image_shaped_input, 10) 36 | 37 | # 初始化权重参数 38 | def weight_variable(shape): 39 | initial = tf.truncated_normal(shape, stddev = 0.1) 40 | return tf.Variable(initial) 41 | 42 | # 初始化偏执参数 43 | def bias_variable(shape): 44 | initial = tf.constant(0.1, shape = shape) 45 | return tf.Variable(initial) 46 | 47 | # 绘制参数变化 48 | def variable_summaries(var): 49 | with tf.name_scope('summaries'): 50 | # 计算参数的均值,并使用tf.summary.scaler记录 51 | mean = tf.reduce_mean(var) 52 | tf.summary.scalar('mean', mean) 53 | 54 | # 计算参数的标准差 55 | with tf.name_scope('stddev'): 56 | stddev = tf.sqrt(tf.reduce_mean(tf.square(var - mean))) 57 | # 使用tf.summary.scaler记录记录下标准差,最大值,最小值 58 | tf.summary.scalar('stddev', stddev) 59 | tf.summary.scalar('max', tf.reduce_max(var)) 60 | tf.summary.scalar('min', tf.reduce_min(var)) 61 | # 用直方图记录参数的分布 62 | tf.summary.histogram('histogram', var) 63 | 64 | # 构建神经网络 65 | def nn_layer(input_tensor, input_dim, output_dim, layer_name, act=tf.nn.relu): 66 | # 设置命名空间 67 | with tf.name_scope(layer_name): 68 | # 调用之前的方法初始化权重w,并且调用参数信息的记录方法,记录w的信息 69 | with tf.name_scope('weights'): 70 | weights = weight_variable([input_dim, output_dim]) 71 | variable_summaries(weights) 72 | # 调用之前的方法初始化权重b,并且调用参数信息的记录方法,记录b的信息 73 | with tf.name_scope('biases'): 74 | biases = bias_variable([output_dim]) 75 | variable_summaries(biases) 76 | # 执行wx+b的线性计算,并且用直方图记录下来 77 | with tf.name_scope('linear_compute'): 78 | preactivate = tf.matmul(input_tensor, weights) + biases 79 | tf.summary.histogram('linear', preactivate) 80 | # 将线性输出经过激励函数,并将输出也用直方图记录下来 81 | activations = act(preactivate, name='activation') 82 | tf.summary.histogram('activations', activations) 83 | # 返回激励层的最终输出 84 | return activations 85 | 86 | hidden1 = nn_layer(x, 784, 500, 'layer1') 87 | 88 | # 创建dropout层 89 | with tf.name_scope('dropout'): 90 | keep_prob = tf.placeholder(tf.float32) 91 | tf.summary.scalar('dropout_keep_probability', keep_prob) 92 | dropped = tf.nn.dropout(hidden1, keep_prob) 93 | 94 | 95 | y = nn_layer(dropped, 500, 10, 'layer2', act=tf.identity) 96 | 97 | # 创建损失函数 98 | with tf.name_scope('loss'): 99 | # 计算交叉熵损失(每个样本都会有一个损失) 100 | diff = tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y) 101 | with tf.name_scope('total'): 102 | # 计算所有样本交叉熵损失的均值 103 | cross_entropy = tf.reduce_mean(diff) 104 | tf.summary.scalar('loss', cross_entropy) 105 | 106 | 107 | # 使用AdamOptimizer优化器训练模型,最小化交叉熵损失 108 | with tf.name_scope('train'): 109 | train_step = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy) 110 | 111 | # 计算准确率 112 | with tf.name_scope('accuracy'): 113 | with tf.name_scope('correct_prediction'): 114 | # 分别将预测和真实的标签中取出最大值的索引,弱相同则返回1(true),不同则返回0(false) 115 | correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1)) 116 | with tf.name_scope('accuracy'): 117 | # 求均值即为准确率 118 | accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) 119 | 120 | 121 | tf.summary.scalar('accuracy', accuracy) 122 | 123 | # summaries合并 124 | merged = tf.summary.merge_all() 125 | # 写到指定的磁盘路径中 126 | train_writer = tf.summary.FileWriter(log_dir + '/train', sess.graph) 127 | test_writer = tf.summary.FileWriter(log_dir + '/test') 128 | 129 | # 运行初始化所有变量 130 | tf.global_variables_initializer().run() 131 | 132 | def feed_dict(train): 133 | """Make a TensorFlow feed_dict: maps data onto Tensor placeholders.""" 134 | if train: 135 | xs, ys = mnist.train.next_batch(100) 136 | k = dropout 137 | else: 138 | xs, ys = mnist.test.images, mnist.test.labels 139 | k = 1.0 140 | return {x: xs, y_: ys, keep_prob: k} 141 | 142 | for i in range(max_steps): 143 | if i % 10 == 0: # 记录测试集的summary与accuracy 144 | summary, acc = sess.run([merged, accuracy], feed_dict=feed_dict(False)) 145 | test_writer.add_summary(summary, i) 146 | print('Accuracy at step %s: %s' % (i, acc)) 147 | else: # 记录训练集的summary 148 | summary, _ = sess.run([merged, train_step], feed_dict=feed_dict(True)) 149 | train_writer.add_summary(summary, i) 150 | 151 | train_writer.close() 152 | test_writer.close() --------------------------------------------------------------------------------