vs2017\vs2019 VGG19处理cifar-10数据集的TensorFlow实现

举报
悲恋花丶无心之人 发表于 2021/02/03 01:19:53 2021/02/03
【摘要】 这是针对于博客vs2017安装和使用教程(详细)和vs2019安装和使用教程(详细)的VGG19-CIFAR10项目新建示例 目录 一、代码(附有重要的注释) 二、项目结构 三、VGG简介 四、程序执行关键部分解析 五、训练过程和结果 六、参考博客和文献 一、代码(附有重要的注释) 1.博主提供的代码包含了很多重要的注释,都是博主精心查阅资料和debug...

这是针对于博客vs2017安装和使用教程(详细)vs2019安装和使用教程(详细)的VGG19-CIFAR10项目新建示例


目录

一、代码(附有重要的注释)

二、项目结构

三、VGG简介

四、程序执行关键部分解析

五、训练过程和结果

六、参考博客和文献


一、代码(附有重要的注释)

1.博主提供的代码包含了很多重要的注释,都是博主精心查阅资料和debug的结果,对于新手了解tensorflow使用以及深度学习框架十分有用。

2.代码如下:

vgg19.py


  
  1. import tensorflow as tf
  2. import numpy as np
  3. import time
  4. import os
  5. import sys
  6. import pickle
  7. import random
  8. class_num = 10
  9. image_size = 32
  10. img_channels = 3
  11. iterations = 200
  12. batch_size = 250
  13. total_epoch = 164
  14. weight_decay = 0.0003
  15. dropout_rate = 0.5
  16. momentum_rate = 0.9
  17. log_save_path = './vgg_logs'
  18. model_save_path = './model/'
  19. def download_data():
  20. dirname = 'cifar-10-batches-py'
  21. origin = 'http://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz'
  22. fname = './CAFIR-10_data/cifar-10-python.tar.gz'
  23. fpath = './' + dirname
  24. download = False
  25. if os.path.exists(fpath) or os.path.isfile(fname):
  26. download = False
  27. print("DataSet already exist!")
  28. else:
  29. download = True
  30. if download:
  31. print('Downloading data from', origin)
  32. import urllib.request
  33. import tarfile
  34. def reporthook(count, block_size, total_size):
  35. global start_time
  36. if count == 0:
  37. start_time = time.time()
  38. return
  39. duration = time.time() - start_time
  40. progress_size = int(count * block_size)
  41. speed = int(progress_size / (1024 * duration))
  42. percent = min(int(count*block_size*100/total_size),100)
  43. sys.stdout.write("\r...%d%%, %d MB, %d KB/s, %d seconds passed" %
  44. (percent, progress_size / (1024 * 1024), speed, duration))
  45. sys.stdout.flush()
  46. urllib.request.urlretrieve(origin, fname, reporthook)
  47. print('Download finished. Start extract!', origin)
  48. if fname.endswith("tar.gz"):
  49. tar = tarfile.open(fname, "r:gz")
  50. tar.extractall()
  51. tar.close()
  52. elif fname.endswith("tar"):
  53. tar = tarfile.open(fname, "r:")
  54. tar.extractall()
  55. tar.close()
  56. def unpickle(file):
  57. with open(file, 'rb') as fo:
  58. dict = pickle.load(fo, encoding='bytes')
  59. return dict
  60. def load_data_one(file):
  61. batch = unpickle(file)#./cifar-10-batches-py/data_batch_1 ./cifar-10-batches-py/test_batch'
  62. data = batch[b'data']#数据
  63. labels = batch[b'labels']#标签
  64. print("Loading %s : %d." % (file, len(data)))
  65. return data, labels
  66. def load_data(files, data_dir, label_count):
  67. global image_size, img_channels
  68. data, labels = load_data_one(data_dir + '/' + files[0])#./cifar-10-batches-py/data_batch_1 [0:10000]
  69. for f in files[1:]:#test_batch时不经历该循环
  70. data_n, labels_n = load_data_one(data_dir + '/' + f)#从./cifar-10-batches-py/data_batch_2
  71. data = np.append(data, data_n, axis=0)#在行末尾追加,第一次循环变为[0:20000]
  72. labels = np.append(labels, labels_n, axis=0)#最终[0:50000]
  73. labels = np.array([[float(i == label) for i in range(label_count)] for label in labels])#labels重组,原数组第i个数字为k则第i行第k个位置位1,其它位置为0
  74. #print(labels)
  75. data = data.reshape([-1, img_channels, image_size, image_size])#-1缺省,函数自己计算,这里为train:50000 test:10000
  76. data = data.transpose([0, 2, 3, 1])#train:[50000,3,32,32]变成[50000,32,32,3] test:[10000,3,32,32]变成[10000,32,32,3]
  77. return data, labels
  78. def prepare_data():
  79. print("======Loading data======")
  80. download_data()
  81. data_dir = './cifar-10-batches-py'
  82. image_dim = image_size * image_size * img_channels #32x32x3
  83. meta = unpickle(data_dir + '/batches.meta')
  84. label_names = meta[b'label_names']#[b'airplane', b'automobile', b'bird', b'cat', b'deer', b'dog', b'frog', b'horse', b'ship', b'truck']
  85. label_count = len(label_names)#10
  86. train_files = ['data_batch_%d' % d for d in range(1, 6)]#['data_batch_1', 'data_batch_2', 'data_batch_3', 'data_batch_4', 'data_batch_5']
  87. train_data, train_labels = load_data(train_files, data_dir, label_count)#train_data[50000,32,32,3],train_labels[0,50000]
  88. test_data, test_labels = load_data(['test_batch'], data_dir, label_count)
  89. print("Train data:", np.shape(train_data), np.shape(train_labels))#Train data: (50000, 32, 32, 3) (50000, 10)
  90. print("Test data :", np.shape(test_data), np.shape(test_labels))#Test data : (10000, 32, 32, 3) (10000, 10)
  91. print("======Load finished======")#训练和测试数据读取完成
  92. print("======Shuffling data======")
  93. indices = np.random.permutation(len(train_data))#返回一个0-50000的随机排列
  94. train_data = train_data[indices]#train重新排列
  95. train_labels = train_labels[indices]#test重新排列
  96. print("======Prepare Finished======")
  97. return train_data, train_labels, test_data, test_labels
  98. def bias_variable(shape):
  99. initial = tf.constant(0.1, shape=shape, dtype=tf.float32)# <tf.Tensor 'Const:0' shape=(64,) dtype=float32>
  100. return tf.Variable(initial)
  101. def conv2d(x, W):
  102. #x:指需要做卷积的输入图像,它要求是一个Tensor,
  103. #具有[batch, in_height, in_width, in_channels]这样的shape,
  104. #具体含义是[训练时一个batch的图片数量, 图片高度, 图片宽度, 图像通道数],
  105. #注意这是一个4维的Tensor,要求类型为float32和float64其中之一
  106. #W:相当于CNN中的卷积核,它要求是一个Tensor,
  107. #具有[filter_height, filter_width, in_channels, out_channels]这样的shape,
  108. #具体含义是[卷积核的高度,卷积核的宽度,图像通道数,卷积核个数],
  109. #要求类型与参数input相同,
  110. #有一个地方需要注意,第三维in_channels,就是参数input的第四维
  111. #strides:卷积时在图像每一维的步长,这是一个一维的向量,长度4
  112. #padding:string类型的量,只能是"SAME","VALID"其中之一,这个值决定了不同的卷积方式
  113. #padding = 'SAME':补0,受到strides大小影响
  114. return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
  115. #
  116. def max_pool(input, k_size=1, stride=1, name=None):
  117. #input:需要池化的输入,一般池化层接在卷积层后面,所以输入通常是feature map,
  118. #依然是[batch, height, width, channels]这样的shape
  119. #ksize:池化窗口的大小,取一个四维向量,一般是[1, height, width, 1],
  120. #因为我们不想在batch和channels上做池化,所以这两个维度设为了1
  121. #strides:和卷积类似,窗口在每一个维度上滑动的步长,一般也是[1, stride,stride, 1]
  122. #padding:和卷积类似,可以取'VALID' 或者'SAME'
  123. #返回一个Tensor,类型不变,shape仍然是[batch, height, width, channels]这种形式
  124. return tf.nn.max_pool(input, ksize=[1, k_size, k_size, 1], strides=[1, stride, stride, 1],
  125. padding='SAME', name=name)
  126. #公式如下:
  127. #y=γ(x-μ)/σ+β
  128. #其中:
  129. #x是输入,
  130. #y是输出,
  131. #μ是均值,
  132. #σ是方差,
  133. #γ和β是缩放(scale)、偏移(offset)系数。
  134. #一般来讲,这些参数都是基于channel来做的,比如输入x是一个16*32*32*128(NWHC格式)的feature map,
  135. #那么上述参数都是128维的向量。其中γ和β是可有可无的,
  136. #有的话,就是一个可以学习的参数(参与前向后向),
  137. #没有的话,就简化成y=(x-μ)/σ。
  138. #而μ和σ,在训练的时候,使用的是batch内的统计值,
  139. #测试/预测的时候,采用的是训练时计算出的滑动平均值。
  140. def batch_norm(input):
  141. #decay:衰减系数。合适的衰减系数值接近1.0,特别是含多个9的值:0.999,0.99,0.9。
  142. #如果训练集表现很好而验证/测试集表现得不好,选择小的系数(推荐使用0.9)。
  143. #如果想要提高稳定性,zero_debias_moving_mean设为True
  144. #center:如果为True,有beta偏移量;如果为False,无beta偏移量
  145. #scale:如果为True,则乘以gamma。
  146. #如果为False,gamma则不使用。
  147. #当下一层是线性的时(例如nn.relu),由于缩放可以由下一层完成,所以可以禁用该层。
  148. #epsilon:ε,避免被零除
  149. #is_training:图层是否处于训练模式。
  150. #在训练模式下,它将积累转入的统计量moving_mean并 moving_variance使用给定的指数移动平均值 decay。
  151. #当它不是在训练模式,那么它将使用的数值moving_mean和moving_variance。
  152. #updates_collections :Collections来收集计算的更新操作。
  153. #updates_ops需要使用train_op来执行。
  154. #如果为None,则会添加控件依赖项以确保更新已计算到位。
  155. return tf.contrib.layers.batch_norm(input, decay=0.9, center=True, scale=True, epsilon=1e-3,
  156. is_training=train_flag, updates_collections=None)
  157. def _random_crop(batch, crop_shape, padding=None):
  158. oshape = np.shape(batch[0])#(32, 32, 3)
  159. if padding:
  160. oshape = (oshape[0] + 2*padding, oshape[1] + 2*padding)#(40, 40)元组
  161. new_batch = []
  162. npad = ((padding, padding), (padding, padding), (0, 0))#((4, 4), (4, 4), (0, 0))
  163. for i in range(len(batch)):#250
  164. new_batch.append(batch[i])
  165. if padding:
  166. #pad(array,pad_width,mode,**kwars)
  167. #其中array为要填补的数组(input)
  168. #pad_width是在各维度的各个方向上想要填补的长度,如((2,3),(4,5)),
  169. #如果直接输入一个整数,则说明各个维度和各个方向所填补的长度都一样。
  170. #mode为填补类型,即怎样去填补,有“constant”,“edge”等模式,
  171. #如果为constant模式,就得指定填补的值。
  172. new_batch[i] = np.lib.pad(batch[i], pad_width=npad,
  173. mode='constant', constant_values=0)#边缘填充,[0:32]变成[0,40]
  174. #temp = oshape[0] - crop_shape[0]
  175. nh = random.randint(0, oshape[0] - crop_shape[0])#返回[0,8]之间的整数
  176. nw = random.randint(0, oshape[1] - crop_shape[1])
  177. new_batch[i] = new_batch[i][nh:nh + crop_shape[0],
  178. nw:nw + crop_shape[1]]#长度为32
  179. return new_batch
  180. def _random_flip_leftright(batch):
  181. for i in range(len(batch)):
  182. if bool(random.getrandbits(1)):#返回一个1位随机的integer
  183. batch[i] = np.fliplr(batch[i])#左右翻转矩阵
  184. return batch
  185. def data_preprocessing(x_train,x_test):
  186. x_train = x_train.astype('float32')#train数据转换为float32
  187. x_test = x_test.astype('float32')#test数据转换为float32
  188. #Z-score标准化(0-1标准化)方法,这种方法给予原始数据的均值(mean)和标准差(standard deviation)进行数据的标准化。
  189. #经过处理的数据符合标准正态分布,即均值为0,标准差为1。
  190. x_train[:, :, :, 0] = (x_train[:, :, :, 0] - np.mean(x_train[:, :, :, 0])) / np.std(x_train[:, :, :, 0])
  191. x_train[:, :, :, 1] = (x_train[:, :, :, 1] - np.mean(x_train[:, :, :, 1])) / np.std(x_train[:, :, :, 1])
  192. x_train[:, :, :, 2] = (x_train[:, :, :, 2] - np.mean(x_train[:, :, :, 2])) / np.std(x_train[:, :, :, 2])
  193. x_test[:, :, :, 0] = (x_test[:, :, :, 0] - np.mean(x_test[:, :, :, 0])) / np.std(x_test[:, :, :, 0])
  194. x_test[:, :, :, 1] = (x_test[:, :, :, 1] - np.mean(x_test[:, :, :, 1])) / np.std(x_test[:, :, :, 1])
  195. x_test[:, :, :, 2] = (x_test[:, :, :, 2] - np.mean(x_test[:, :, :, 2])) / np.std(x_test[:, :, :, 2])
  196. return x_train, x_test
  197. def data_augmentation(batch):
  198. batch = _random_flip_leftright(batch)#[0:250]
  199. batch = _random_crop(batch, [32, 32], 4)#[250,32,32,3]
  200. return batch
  201. def learning_rate_schedule(epoch_num):
  202. if epoch_num < 81:
  203. return 0.1
  204. elif epoch_num < 121:
  205. return 0.01
  206. else:
  207. return 0.001
  208. def run_testing(sess, ep):
  209. acc = 0.0
  210. loss = 0.0
  211. pre_index = 0
  212. add = 1000
  213. for it in range(10):
  214. batch_x = test_x[pre_index:pre_index+add]
  215. batch_y = test_y[pre_index:pre_index+add]
  216. pre_index = pre_index + add
  217. loss_, acc_ = sess.run([cross_entropy, accuracy],
  218. feed_dict={x: batch_x, y_: batch_y, keep_prob: 1.0, train_flag: False})
  219. loss += loss_ / 10.0
  220. acc += acc_ / 10.0
  221. summary = tf.Summary(value=[tf.Summary.Value(tag="test_loss", simple_value=loss),
  222. tf.Summary.Value(tag="test_accuracy", simple_value=acc)])
  223. return acc, loss, summary
  224. if __name__ == '__main__':
  225. train_x, train_y, test_x, test_y = prepare_data()#准备数据,包括解压数据和打乱数据
  226. train_x, test_x = data_preprocessing(train_x, test_x)#数据预处理,使其符合标准正态分布
  227. # define placeholder x, y_ , keep_prob, learning_rate
  228. x = tf.placeholder(tf.float32,[None, image_size, image_size, 3])#<tf.Tensor 'Placeholder:0' shape=(?, 32, 32, 3) dtype=float32>
  229. y_ = tf.placeholder(tf.float32, [None, class_num])#<tf.Tensor 'Placeholder_1:0' shape=(?, 10) dtype=float32>
  230. keep_prob = tf.placeholder(tf.float32)#<tf.Tensor 'Placeholder_2:0' shape=<unknown> dtype=float32>
  231. learning_rate = tf.placeholder(tf.float32)#<tf.Tensor 'Placeholder_4:0' shape=<unknown> dtype=float32>
  232. train_flag = tf.placeholder(tf.bool)#<tf.Tensor 'Placeholder_5:0' shape=<unknown> dtype=bool>
  233. # build_network
  234. #He正态分布初始化方法,参数由0均值,标准差为sqrt(2 / fan_in) 的正态分布产生,其中fan_in权重张量的扇入
  235. #W是卷积核
  236. W_conv1_1 = tf.get_variable('conv1_1', shape=[3, 3, 3, 64], initializer=tf.contrib.keras.initializers.he_normal())#<tf.Variable 'conv1_1:0' shape=(3, 3, 3, 64) dtype=float32_ref>
  237. b_conv1_1 = bias_variable([64])#<tf.Variable 'Variable:0' shape=(64,) dtype=float32_ref>
  238. #这个函数的作用是计算激活函数 relu,即 max(features, 0)。即将矩阵中每行的非最大值置0。
  239. output = tf.nn.relu(batch_norm(conv2d(x, W_conv1_1) + b_conv1_1))#<tf.Tensor 'Relu:0' shape=(?, 32, 32, 64) dtype=float32>
  240. W_conv1_2 = tf.get_variable('conv1_2', shape=[3, 3, 64, 64], initializer=tf.contrib.keras.initializers.he_normal())#<tf.Variable 'conv1_2:0' shape=(3, 3, 64, 64) dtype=float32_ref>
  241. b_conv1_2 = bias_variable([64])#<tf.Variable 'Variable_1:0' shape=(64,) dtype=float32_ref>
  242. output = tf.nn.relu(batch_norm(conv2d(output, W_conv1_2) + b_conv1_2))#<tf.Tensor 'Relu_1:0' shape=(?, 32, 32, 64) dtype=float32>
  243. output = max_pool(output, 2, 2, "pool1")#<tf.Tensor 'pool1_1:0' shape=(?, 16, 16, 64) dtype=float32>
  244. W_conv2_1 = tf.get_variable('conv2_1', shape=[3, 3, 64, 128], initializer=tf.contrib.keras.initializers.he_normal())#<tf.Variable 'conv2_1:0' shape=(3, 3, 64, 128) dtype=float32_ref>
  245. b_conv2_1 = bias_variable([128])#<tf.Variable 'Variable_2:0' shape=(128,) dtype=float32_ref>
  246. output = tf.nn.relu(batch_norm(conv2d(output, W_conv2_1) + b_conv2_1))#<tf.Tensor 'Relu_2:0' shape=(?, 16, 16, 128) dtype=float32>
  247. W_conv2_2 = tf.get_variable('conv2_2', shape=[3, 3, 128, 128], initializer=tf.contrib.keras.initializers.he_normal())
  248. b_conv2_2 = bias_variable([128])
  249. output = tf.nn.relu(batch_norm(conv2d(output, W_conv2_2) + b_conv2_2))
  250. output = max_pool(output, 2, 2, "pool2")
  251. W_conv3_1 = tf.get_variable('conv3_1', shape=[3, 3, 128, 256], initializer=tf.contrib.keras.initializers.he_normal())
  252. b_conv3_1 = bias_variable([256])
  253. output = tf.nn.relu( batch_norm(conv2d(output,W_conv3_1) + b_conv3_1))
  254. W_conv3_2 = tf.get_variable('conv3_2', shape=[3, 3, 256, 256], initializer=tf.contrib.keras.initializers.he_normal())
  255. b_conv3_2 = bias_variable([256])
  256. output = tf.nn.relu(batch_norm(conv2d(output, W_conv3_2) + b_conv3_2))
  257. W_conv3_3 = tf.get_variable('conv3_3', shape=[3, 3, 256, 256], initializer=tf.contrib.keras.initializers.he_normal())
  258. b_conv3_3 = bias_variable([256])
  259. output = tf.nn.relu( batch_norm(conv2d(output, W_conv3_3) + b_conv3_3))
  260. W_conv3_4 = tf.get_variable('conv3_4', shape=[3, 3, 256, 256], initializer=tf.contrib.keras.initializers.he_normal())
  261. b_conv3_4 = bias_variable([256])
  262. output = tf.nn.relu(batch_norm(conv2d(output, W_conv3_4) + b_conv3_4))
  263. output = max_pool(output, 2, 2, "pool3")
  264. W_conv4_1 = tf.get_variable('conv4_1', shape=[3, 3, 256, 512], initializer=tf.contrib.keras.initializers.he_normal())
  265. b_conv4_1 = bias_variable([512])
  266. output = tf.nn.relu(batch_norm(conv2d(output, W_conv4_1) + b_conv4_1))
  267. W_conv4_2 = tf.get_variable('conv4_2', shape=[3, 3, 512, 512], initializer=tf.contrib.keras.initializers.he_normal())
  268. b_conv4_2 = bias_variable([512])
  269. output = tf.nn.relu(batch_norm(conv2d(output, W_conv4_2) + b_conv4_2))
  270. W_conv4_3 = tf.get_variable('conv4_3', shape=[3, 3, 512, 512], initializer=tf.contrib.keras.initializers.he_normal())
  271. b_conv4_3 = bias_variable([512])
  272. output = tf.nn.relu(batch_norm(conv2d(output, W_conv4_3) + b_conv4_3))
  273. W_conv4_4 = tf.get_variable('conv4_4', shape=[3, 3, 512, 512], initializer=tf.contrib.keras.initializers.he_normal())
  274. b_conv4_4 = bias_variable([512])
  275. output = tf.nn.relu(batch_norm(conv2d(output, W_conv4_4)) + b_conv4_4)
  276. output = max_pool(output, 2, 2)
  277. W_conv5_1 = tf.get_variable('conv5_1', shape=[3, 3, 512, 512], initializer=tf.contrib.keras.initializers.he_normal())
  278. b_conv5_1 = bias_variable([512])
  279. output = tf.nn.relu(batch_norm(conv2d(output, W_conv5_1) + b_conv5_1))
  280. W_conv5_2 = tf.get_variable('conv5_2', shape=[3, 3, 512, 512], initializer=tf.contrib.keras.initializers.he_normal())
  281. b_conv5_2 = bias_variable([512])
  282. output = tf.nn.relu(batch_norm(conv2d(output, W_conv5_2) + b_conv5_2))
  283. W_conv5_3 = tf.get_variable('conv5_3', shape=[3, 3, 512, 512], initializer=tf.contrib.keras.initializers.he_normal())
  284. b_conv5_3 = bias_variable([512])
  285. output = tf.nn.relu(batch_norm(conv2d(output, W_conv5_3) + b_conv5_3))
  286. W_conv5_4 = tf.get_variable('conv5_4', shape=[3, 3, 512, 512], initializer=tf.contrib.keras.initializers.he_normal())
  287. b_conv5_4 = bias_variable([512])
  288. output = tf.nn.relu(batch_norm(conv2d(output, W_conv5_4) + b_conv5_4))
  289. # output = tf.contrib.layers.flatten(output)
  290. output = tf.reshape(output, [-1, 2*2*512])#<tf.Tensor 'Reshape:0' shape=(?, 2048) dtype=float32>
  291. W_fc1 = tf.get_variable('fc1', shape=[2048, 4096], initializer=tf.contrib.keras.initializers.he_normal())
  292. b_fc1 = bias_variable([4096])
  293. output = tf.nn.relu(batch_norm(tf.matmul(output, W_fc1) + b_fc1) )
  294. #tf.nn.dropout是TensorFlow里面为了防止或减轻过拟合而使用的函数,它一般用在全连接层。
  295. #Dropout就是在不同的训练过程中随机扔掉一部分神经元。也就是让某个神经元的激活值以一定的概率p,让其停止工作,
  296. #这次训练过程中不更新权值,也不参加神经网络的计算。但是它的权重得保留下来(只是暂时不更新而已),因为下次样本输入时它可能又得工作了。
  297. #第一个参数output:指输入
  298. #第二个参数keep_prob: 设置神经元被选中的概率,在初始化时keep_prob是一个占位符, keep_prob = tf.placeholder(tf.float32)。
  299. #tensorflow在run时设置keep_prob具体的值,例如keep_prob: 0.5
  300. output = tf.nn.dropout(output, keep_prob)
  301. W_fc2 = tf.get_variable('fc7', shape=[4096, 4096], initializer=tf.contrib.keras.initializers.he_normal())
  302. b_fc2 = bias_variable([4096])
  303. output = tf.nn.relu(batch_norm(tf.matmul(output, W_fc2) + b_fc2))
  304. output = tf.nn.dropout(output, keep_prob)
  305. W_fc3 = tf.get_variable('fc3', shape=[4096, 10], initializer=tf.contrib.keras.initializers.he_normal())
  306. b_fc3 = bias_variable([10])
  307. output = tf.nn.relu(batch_norm(tf.matmul(output, W_fc3) + b_fc3))
  308. # output = tf.reshape(output,[-1,10])
  309. # loss function: cross_entropy
  310. # train_step: training operation
  311. #labels:一个分类标签,所不同的是,这个labels是分类的概率,
  312. #比如说[0.2,0.3,0.5],labels的每一行必须是一个概率分布(即概率之合加起来为1)。
  313. #logits:logit的值域范围[-inf,+inf](即正负无穷区间)。
  314. #我们可以把logist理解为原生态的、未经缩放的,可视为一种未归一化的l“概率替代物”,
  315. #如[4, 1, -2]。它可以是其他分类器(如逻辑回归等、SVM等)的输出。
  316. #Softmax把一个系列的概率替代物(logits)从[-inf, +inf] 映射到[0,1]。
  317. #经过softmax的加工,就变成“归一化”的概率(设为p1),这个新生成的概率p1,和labels所代表的概率分布(设为p2)一起作为参数,用来计算交叉熵。
  318. #这个差异信息,作为我们网络调参的依据,理想情况下,这两个分布尽量趋近最好。
  319. #如果有差异(也可以理解为误差信号),我们就调整参数,让其变得更小,这就是损失(误差)函数的作用。
  320. cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=output))#logit=log(odds)=log(P/(1-P))
  321. #l2_loss:1/2Σvar2或者output = sum(t ** 2) / 2
  322. #L1正则化是指权值向量w中各个元素的绝对值之和,通常表示为||w||1
  323. #L2正则化是指权值向量w中各个元素的平方和然后再求平方根(可以看到Ridge回归的L2正则化项有平方符号),通常表示为||w||2
  324. #也就是说Lx范数应用于优化的目标函数就叫做Lx正则化
  325. #l2_loss一般用于优化目标函数中的正则项,防止参数太多复杂容易过拟合(所谓的过拟合问题是指当一个模型很复杂时,
  326. #它可以很好的“记忆”每一个训练数据中的随机噪声的部分而忘记了要去“学习”训练数据中通用的趋势)
  327. #多个l2(var向量)对应元素相加变为1行var
  328. l2 = tf.add_n([tf.nn.l2_loss(var) for var in tf.trainable_variables()])
  329. #动量梯度下降算法
  330. #learning_rate: (学习率)张量或者浮点数
  331. #momentum: (动量)张量或者浮点数
  332. #use_locking: 为True时锁定更新
  333. #name:  梯度下降名称,默认为 "Momentum".
  334. #use_nesterov:  为True时,使用 Nesterov Momentum. 
  335. train_step = tf.train.MomentumOptimizer(learning_rate, momentum_rate, use_nesterov=True).\
  336. minimize(cross_entropy + l2 * weight_decay)
  337. #tf.argmax( , )中有两个参数,第一个参数是矩阵,第二个参数是0或者1。
  338. #0表示的是按列比较返回最大值的索引,
  339. #1表示按行比较返回最大值的索引。
  340. #tf.equal(A, B)是对比这两个矩阵或者向量的相等的元素,
  341. #如果是相等的那就返回True,否则返回False,
  342. #返回的值的矩阵维度和A是一样的
  343. correct_prediction = tf.equal(tf.argmax(output, 1), tf.argmax(y_, 1))
  344. #将x的数据格式转化成dtype.例如,原来x的数据格式是bool,那么将其转化成float以后,就能够将其转化成0和1的序列。反之也可以
  345. accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
  346. # initial an saver to save model
  347. saver = tf.train.Saver()
  348. with tf.Session() as sess:
  349. sess.run(tf.global_variables_initializer())#初始化全局变量
  350. summary_writer = tf.summary.FileWriter(log_save_path,sess.graph)#log是事件文件所在的目录,这里是工程目录下的log目录。第二个参数是事件文件要记录的图,也就是tensorflow默认的图。
  351. if os.path.exists(model_save_path):
  352. #模型的恢复用的是restore()函数,它需要两个参数restore(sess, save_path),
  353. #save_path指的是保存的模型路径。
  354. #我们可以使用tf.train.latest_checkpoint()来自动获取最后一次保存的模型。
  355. saver.restore(sess,model_save_path+"vgg19.ckpt")
  356. # epoch = 164
  357. # make sure [bath_size * iteration = data_set_number]
  358. for ep in range(1, total_epoch+1):#total_epoch = 164
  359. lr = learning_rate_schedule(ep)#学习率变化时间表
  360. pre_index = 0
  361. train_acc = 0.0
  362. train_loss = 0.0
  363. start_time = time.time()
  364. print("\n epoch %d/%d:" % (ep, total_epoch))
  365. for it in range(1, iterations+1):#iterations = 200
  366. batch_x = train_x[pre_index:pre_index+batch_size]#batch_size = 250
  367. batch_y = train_y[pre_index:pre_index+batch_size]
  368. batch_x = data_augmentation(batch_x)
  369. _, batch_loss = sess.run([train_step, cross_entropy],
  370. feed_dict={x: batch_x, y_: batch_y, keep_prob: dropout_rate,
  371. learning_rate: lr, train_flag: True})
  372. batch_acc = accuracy.eval(feed_dict={x: batch_x, y_: batch_y, keep_prob: 1.0, train_flag: True})
  373. train_loss += batch_loss
  374. train_acc += batch_acc
  375. pre_index += batch_size
  376. if it == iterations:
  377. train_loss /= iterations
  378. train_acc /= iterations
  379. #第一个参数是要求的结果
  380. #第二个参数feed_dict是给placeholder赋值
  381. loss_, acc_ = sess.run([cross_entropy, accuracy],
  382. feed_dict={x: batch_x, y_: batch_y, keep_prob: 1.0, train_flag: True})
  383. train_summary = tf.Summary(value=[tf.Summary.Value(tag="train_loss", simple_value=train_loss),
  384. tf.Summary.Value(tag="train_accuracy", simple_value=train_acc)])
  385. val_acc, val_loss, test_summary = run_testing(sess, ep)
  386. summary_writer.add_summary(train_summary, ep)
  387. summary_writer.add_summary(test_summary, ep)
  388. summary_writer.flush()
  389. print("iteration: %d/%d, cost_time: %ds, train_loss: %.4f, "
  390. "train_acc: %.4f, test_loss: %.4f, test_acc: %.4f"
  391. % (it, iterations, int(time.time()-start_time), train_loss, train_acc, val_loss, val_acc))
  392. else:
  393. print("iteration: %d/%d, train_loss: %.4f, train_acc: %.4f"
  394. % (it, iterations, train_loss / it, train_acc / it), end='\r')
  395. save_path = saver.save(sess, model_save_path+"vgg19.ckpt")
  396. print("Model saved in file: %s" % save_path)

二、项目结构

1.由于使用的是vs2017vs2019,因此需要新建一个项目,可参考博主的博客:vs2017 开始自己的第一个Python程序vs2019 开始自己的第一个Python程序

2.运行完该程序,你的项目结构应该是下图所示:

(1)vgg19.py就是你的代码文件

(2)项目名称cifar,因此解决方案是cifar.sln或者是cifar.pyproj

(3)cifar-10-batches-py是程序下载的数据集,一开始是没有的,打开它,内容如下:

(4)model是你训练完成的模型文件夹,一开始是没有的,打开它,内容如下:

(5)vgg_logs是你运行代码的日志文件,可以用tensorboard打开,内容如下:

 

打开cmd或者Anaconda Prompt,指令是(以博主路径为例):

tensorboard --logdir D:\vs2017_project\cifar\vgg_logs
 

然后打开浏览器,输出命令最后一行提示的网址,打开tensorboard:http://desktop-xxxxxx:6006

三、VGG简介

1.概要

VGG模型是2014年ILSVRC竞赛的第二名,第一名是GoogLeNet。但是VGG模型在多个迁移学习任务中的表现要优于googLeNet。而且,从图像中提取CNN特征,VGG模型是首选算法。它的缺点在于,参数量有140M之多,需要更大的存储空间。但是这个模型很有研究价值。

2.用途和准确率

VGG Net由牛津大学的视觉几何组(Visual Geometry Group)和 Google DeepMind公司的研究员一起研发的的深度卷积神经网络,在 ILSVRC 2014 上取得了第二名的成绩,将 Top-5错误率降到7.3%。 它主要的贡献是展示出网络的深度(depth)是算法优良性能的关键部分。目前使用比较多的网络结构主要有ResNet(152-1000层),GooleNet(22层),VGGNet(19层),大多数模型都是基于这几个模型上改进,采用新的优化算法,多模型融合等。 到目前为止,VGG Net 依然经常被用来提取图像特征。

3.网络结构图

                   

                                                                      

四、程序执行关键部分解析

1.数据预处理

Z-score标准化(0-1标准化)方法,这种方法给予原始数据的均值(mean)和标准差(standard deviation)进行数据的标准化。 经过处理的数据符合标准正态分布,即均值为0,标准差为1。

转化公式为:

                                                                    


  
  1. def data_preprocessing(x_train,x_test):
  2. x_train = x_train.astype('float32')#train数据转换为float32
  3. x_test = x_test.astype('float32')#test数据转换为float32
  4. #Z-score标准化(0-1标准化)方法,这种方法给予原始数据的均值(mean)和标准差(standard deviation)进行数据的标准化。
  5. #经过处理的数据符合标准正态分布,即均值为0,标准差为1。
  6. x_train[:, :, :, 0] = (x_train[:, :, :, 0] - np.mean(x_train[:, :, :, 0])) / np.std(x_train[:, :, :, 0])
  7. x_train[:, :, :, 1] = (x_train[:, :, :, 1] - np.mean(x_train[:, :, :, 1])) / np.std(x_train[:, :, :, 1])
  8. x_train[:, :, :, 2] = (x_train[:, :, :, 2] - np.mean(x_train[:, :, :, 2])) / np.std(x_train[:, :, :, 2])
  9. x_test[:, :, :, 0] = (x_test[:, :, :, 0] - np.mean(x_test[:, :, :, 0])) / np.std(x_test[:, :, :, 0])
  10. x_test[:, :, :, 1] = (x_test[:, :, :, 1] - np.mean(x_test[:, :, :, 1])) / np.std(x_test[:, :, :, 1])
  11. x_test[:, :, :, 2] = (x_test[:, :, :, 2] - np.mean(x_test[:, :, :, 2])) / np.std(x_test[:, :, :, 2])
  12. return x_train, x_test

2. 网络部分

(1)initializer=tf.contrib.keras.initializers.he_normal()

其中he_normal()指的是He正态分布初始化方法


  
  1. #He正态分布初始化方法,参数由0均值,标准差为sqrt(2 / fan_in) 的正态分布产生,其中fan_in权重张量的扇入
  2. #W是卷积核
  3. W_conv1_1 = tf.get_variable('conv1_1', shape=[3, 3, 3, 64], initializer=tf.contrib.keras.initializers.he_normal())b_conv1_1 = bias_variable([64])
  4. #这个函数的作用是计算激活函数 relu,即 max(features, 0)。即将矩阵中每行的非最大值置0。
  5. output = tf.nn.relu(batch_norm(conv2d(x, W_conv1_1) + b_conv1_1))

然后,我们分析一下tf.nn.relu(batch_norm(conv2d(x, W_conv1_1) + b_conv1_1))这句话

(2)tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')


  
  1. def conv2d(x, W):
  2. #x:指需要做卷积的输入图像,它要求是一个Tensor,
  3. #具有[batch, in_height, in_width, in_channels]这样的shape,
  4. #具体含义是[训练时一个batch的图片数量, 图片高度, 图片宽度, 图像通道数],
  5. #注意这是一个4维的Tensor,要求类型为float32和float64其中之一
  6. #W:相当于CNN中的卷积核,它要求是一个Tensor,
  7. #具有[filter_height, filter_width, in_channels, out_channels]这样的shape,
  8. #具体含义是[卷积核的高度,卷积核的宽度,图像通道数,卷积核个数],
  9. #要求类型与参数input相同,
  10. #有一个地方需要注意,第三维in_channels,就是参数x的第四维
  11. #strides:卷积时在图像每一维的步长,这是一个一维的向量,长度4
  12. #padding:string类型的量,只能是"SAME","VALID"其中之一,这个值决定了不同的卷积方式
  13. #padding = 'SAME':补0,受到strides大小影响
  14. return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

参数解析:

x:指需要做卷积的输入图像,它要求是一个Tensor,具有[batch, in_height, in_width, in_channels]这样的shape,具体含义是[训练时一个batch的图片数量, 图片高度, 图片宽度, 图像通道数],注意这是一个4维的Tensor,要求类型为float32和float64其中之一

W:相当于CNN中的卷积核,它要求是一个Tensor,具有[filter_height, filter_width, in_channels, out_channels]这样的shape,具体含义是[卷积核的高度,卷积核的宽度,图像通道数,卷积核个数],要求类型与参数input相同,有一个地方需要注意,第三维in_channels,就是参数x的第四维

strides:卷积时在图像每一维的步长,这是一个一维的向量,长度4

padding:string类型的量,只能是"SAME","VALID"其中之一,这个值决定了不同的卷积方式,padding = 'SAME':补0,受到strides大小影响

这里conv2d(x, W_conv1_1)指的是x卷积输入图像W_conv1_1卷积核,而且这个卷积核大小为3x3,输入通道为3,输出通道为64

(3)tf.contrib.layers.batch_norm()


  
  1. def batch_norm(input):
  2. #decay:衰减系数。合适的衰减系数值接近1.0,特别是含多个9的值:0.999,0.99,0.9。
  3. #如果训练集表现很好而验证/测试集表现得不好,选择小的系数(推荐使用0.9)。
  4. #如果想要提高稳定性,zero_debias_moving_mean设为True
  5. #center:如果为True,有beta偏移量;如果为False,无beta偏移量
  6. #scale:如果为True,则乘以gamma。
  7. #如果为False,gamma则不使用。
  8. #当下一层是线性的时(例如nn.relu),由于缩放可以由下一层完成,所以可以禁用该层。
  9. #epsilon:ε,避免被零除
  10. #is_training:图层是否处于训练模式。
  11. #在训练模式下,它将积累转入的统计量moving_mean并 moving_variance使用给定的指数移动平均值 decay。
  12. #当它不是在训练模式,那么它将使用的数值moving_mean和moving_variance。
  13. #updates_collections :Collections来收集计算的更新操作。
  14. #updates_ops需要使用train_op来执行。
  15. #如果为None,则会添加控件依赖项以确保更新已计算到位。
  16. return tf.contrib.layers.batch_norm(input, decay=0.9, center=True, scale=True, epsilon=1e-3,
  17. is_training=train_flag, updates_collections=None)

公式如下:

y=γ(x-μ)/σ+β

其中:x是输入,y是输出,μ是均值,σ是方差,γ和β是缩放(scale)、偏移(offset)系数。

一般来讲,这些参数都是基于channel来做的,比如输入x是一个16*32*32*128(NWHC格式)的feature map,那么上述参数都是128维的向量。

其中γ和β是可有可无的,有的话,就是一个可以学习的参数(参与前向后向),没有的话,就简化成y=(x-μ)/σ。

而μ和σ,在训练的时候,使用的是batch内的统计值,测试/预测的时候,采用的是训练时计算出的滑动平均值。

参数解析:

decay:衰减系数。合适的衰减系数值接近1.0,特别是含多个9的值:0.999,0.99,0.9。如果训练集表现很好而验证/测试集表现得不好,选择小的系数(推荐使用0.9)。如果想要提高稳定性,zero_debias_moving_mean设为True

center:如果为True,有beta偏移量;如果为False,无beta偏移量

scale:如果为True,则乘以gamma。如果为False,gamma则不使用。当下一层是线性的时(例如nn.relu),由于缩放可以由下一层完成,所以可以禁用该层。

epsilon:ε,避免被零除

is_training:图层是否处于训练模式。在训练模式下,它将积累转入的统计量moving_mean并 moving_variance使用给定的指数移动平均值 decay。当它不是在训练模式,那么它将使用的数值moving_mean和moving_variance。  

updates_collections :Collections来收集计算的更新操作。updates_ops需要使用train_op来执行。如果为None,则会添加控件依赖项以确保更新已计算到位。

(4)tf.nn.dropout(output, keep_prob)


  
  1. W_fc1 = tf.get_variable('fc1', shape=[2048, 4096], initializer=tf.contrib.keras.initializers.he_normal())
  2. b_fc1 = bias_variable([4096])
  3. output = tf.nn.relu(batch_norm(tf.matmul(output, W_fc1) + b_fc1) )
  4. #tf.nn.dropout是TensorFlow里面为了防止或减轻过拟合而使用的函数,它一般用在全连接层。
  5. #Dropout就是在不同的训练过程中随机扔掉一部分神经元。也就是让某个神经元的激活值以一定的概率p,让其停止工作,
  6. #这次训练过程中不更新权值,也不参加神经网络的计算。但是它的权重得保留下来(只是暂时不更新而已),因为下次样本输入时它可能又得工作了。
  7. #第一个参数output:指输入
  8. #第二个参数keep_prob: 设置神经元被选中的概率,在初始化时keep_prob是一个占位符, keep_prob = tf.placeholder(tf.float32)。
  9. #tensorflow在run时设置keep_prob具体的值,例如keep_prob: 0.5
  10. output = tf.nn.dropout(output, keep_prob)

该函数是TensorFlow里面为了防止或减轻过拟合而使用的函数,它一般用在全连接层

Dropout就是在不同的训练过程中随机扔掉一部分神经元。也就是让某个神经元的激活值以一定的概率p,让其停止工作,这次训练过程中不更新权值,也不参加神经网络的计算。

但是它的权重得保留下来(只是暂时不更新而已),因为下次样本输入时它可能又得工作了。

参数解析

output:指输入

keep_prob: 设置神经元被选中的概率,在初始化时keep_prob是一个占位符, keep_prob = tf.placeholder(tf.float32)。tensorflow在run时设置keep_prob具体的值,例如keep_prob: 0.5

FC层

左边的图为一个完全的全连接层,右边为应用dropout后的全连接层。

                      

3. 损失函数

(1)交叉熵

 cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=output))#logit=log(odds)=log(P/(1-P))
 

参数解析

labels:一个分类标签,所不同的是,这个labels是分类的概率,比如说[0.2,0.3,0.5],labels的每一行必须是一个概率分布(即概率之合加起来为1)。  

logits:logit的值域范围[-inf,+inf](即正负无穷区间)。我们可以把logist理解为原生态的、未经缩放的,可视为一种未归一化的l“概率替代物”,如[4, 1, -2]。它可以是其他分类器(如逻辑回归等、SVM等)的输出。

logit公式如下

                                                                                 

Odds(A)= 发生事件A次数 /  其他事件的次数(即不发生A的次数)

概率P(A)和Odds(A)的值域是不同的。前者被锁定在[0,1]之间,而后者则是[0,∞)

softmax对于logits的用处

Softmax把一个系列的概率替代物(logits)从[-inf, +inf] 映射到[0,1]

(2)L2损失

l2 = tf.add_n([tf.nn.l2_loss(var) for var in tf.trainable_variables()])
 

参数解析

l2_loss:1/2Σvar^2或者output = sum(t ** 2) / 2

L1正则化是指权值向量w中各个元素的绝对值之和,通常表示为||w||1

L2正则化是指权值向量w中各个元素的平方和然后再求平方根(可以看到Ridge回归的L2正则化项有平方符号),通常表示为||w||2  

也就是说Lx范数应用于优化的目标函数就叫做Lx正则化  

l2_loss一般用于优化目标函数中的正则项防止参数太多复杂容易过拟合(所谓的过拟合问题是指当一个模型很复杂时,它可以很好的“记忆”每一个训练数据中的随机噪声的部分而忘记了要去“学习”训练数据中通用的趋势)

tf.add_n:多个l2(var向量)对应元素相加变为1行var

(3)动量梯度下降算法


  
  1. train_step = tf.train.MomentumOptimizer(learning_rate, momentum_rate, use_nesterov=True).\
  2. minimize(cross_entropy + l2 * weight_decay)

参数解析

learning_rate: (学习率)张量或者浮点数  

momentum: (动量)张量或者浮点数

use_locking: 为True时锁定更新

name: 梯度下降名称,默认为 "Momentum".

use_nesterov:  为True时,使用 Nesterov Momentum

梯度下降法参数更新公式

W:=W−α∇W

b:=b−α∇b

可以看到,每次更新仅与当前梯度值相关,并不涉及之前的梯度。

而动量梯度下降法则对各个mini-batch求得的梯度∇W,∇b 使用指数加权平均得到 V∇w,V∇b 并使用新的参数更新之前的参数。

例如,在100次梯度下降中求得的梯度序列为:

{∇W1,∇W2,∇W3.........∇W99 ,∇W100}

则其对应的动量梯度分别为:

                                                                      

使用指数加权平均之后梯度代替原梯度进行参数更新。

因为每个指数加权平均后的梯度含有之前梯度的信息

4.准确率


  
  1. correct_prediction = tf.equal(tf.argmax(output, 1), tf.argmax(y_, 1))
  2. accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

tf.argmax( , )参数解析

第一个参数是矩阵,第二个参数是0或者1。 0表示的是按列比较返回最大值的索引, 1表示按行比较返回最大值的索引。 tf.equal(A, B)参数解析

对比这A和B两个矩阵或者向量的相等的元素, 如果是相等的那就返回True,否则返回False, 返回的值的矩阵维度和A是一样的。

tf.cast(x,dtype)参数解析

将x的数据格式转化成dtype

例如,原来x的数据格式是bool,那么将其转化成float以后,就能够将其转化成0和1的序列。反之也可以。

五、训练过程和结果

由于一个epoch耗时34s,因此164个epoch需要大约1.5小时训练完成

1.训练过程

2.结果

其中学习率在epoch为82122时会有转变,代码部分如下:


  
  1. def learning_rate_schedule(epoch_num):
  2. if epoch_num < 81:
  3. return 0.1
  4. elif epoch_num < 121:
  5. return 0.01
  6. else:
  7. return 0.001

tensorboard结果

我们也可以从图表中看到在82和121的剧烈变化

最终训练准确率89%左右,测试准确率84.5%左右~

六、参考博客和文献

1.2014-VGG-《Very deep convolutional networks for large-scale image recognition》

2.VGG结构的基本理解

3.数据规范化(归一化)、及Z-score标准化

4.tf.nn.softmax_cross_entropy_with_logits中的“logits”到底是个什么意思?

5.tf.nn.l2_loss和 tf.nn.l2_normalize

6.TensorFlow三种常用的优化器

7.动量梯度下降法(gradient descent with momentum)


返回至原博客:

vs2017安装和使用教程(详细)

vs2019安装和使用教程(详细)

文章来源: nickhuang1996.blog.csdn.net,作者:悲恋花丶无心之人,版权归原作者所有,如需转载,请联系作者。

原文链接:nickhuang1996.blog.csdn.net/article/details/85046067

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。