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


目录

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

二、项目结构

三、VGG简介

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

五、训练过程和结果

六、参考博客和文献


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

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

2.代码如下:

vgg19.py

import tensorflow as tf
import numpy as np
import time
import os
import sys
import pickle
import randomclass_num = 10
image_size = 32
img_channels = 3
iterations = 200
batch_size = 250
total_epoch = 164
weight_decay = 0.0003
dropout_rate = 0.5
momentum_rate = 0.9
log_save_path = './vgg_logs'
model_save_path = './model/'def download_data():dirname = 'cifar-10-batches-py'origin = 'http://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz'fname = './CAFIR-10_data/cifar-10-python.tar.gz'fpath = './' + dirnamedownload = Falseif os.path.exists(fpath) or os.path.isfile(fname):download = Falseprint("DataSet already exist!")else:download = Trueif download:print('Downloading data from', origin)import urllib.requestimport tarfiledef reporthook(count, block_size, total_size):global start_timeif count == 0:start_time = time.time()returnduration = time.time() - start_timeprogress_size = int(count * block_size)speed = int(progress_size / (1024 * duration))percent = min(int(count*block_size*100/total_size),100)sys.stdout.write("\r...%d%%, %d MB, %d KB/s, %d seconds passed" %(percent, progress_size / (1024 * 1024), speed, duration))sys.stdout.flush()urllib.request.urlretrieve(origin, fname, reporthook)print('Download finished. Start extract!', origin)if fname.endswith("tar.gz"):tar = tarfile.open(fname, "r:gz")tar.extractall()tar.close()elif fname.endswith("tar"):tar = tarfile.open(fname, "r:")tar.extractall()tar.close()def unpickle(file):with open(file, 'rb') as fo:dict = pickle.load(fo, encoding='bytes')return dictdef load_data_one(file):batch = unpickle(file)#./cifar-10-batches-py/data_batch_1 ./cifar-10-batches-py/test_batch'data = batch[b'data']#数据labels = batch[b'labels']#标签print("Loading %s : %d." % (file, len(data)))return data, labelsdef load_data(files, data_dir, label_count):global image_size, img_channelsdata, labels = load_data_one(data_dir + '/' + files[0])#./cifar-10-batches-py/data_batch_1 [0:10000]for f in files[1:]:#test_batch时不经历该循环data_n, labels_n = load_data_one(data_dir + '/' + f)#从./cifar-10-batches-py/data_batch_2data = np.append(data, data_n, axis=0)#在行末尾追加,第一次循环变为[0:20000]labels = np.append(labels, labels_n, axis=0)#最终[0:50000]labels = np.array([[float(i == label) for i in range(label_count)] for label in labels])#labels重组,原数组第i个数字为k则第i行第k个位置位1,其它位置为0#print(labels)data = data.reshape([-1, img_channels, image_size, image_size])#-1缺省,函数自己计算,这里为train:50000 test:10000data = data.transpose([0, 2, 3, 1])#train:[50000,3,32,32]变成[50000,32,32,3] test:[10000,3,32,32]变成[10000,32,32,3]return data, labelsdef prepare_data():print("======Loading data======")download_data()data_dir = './cifar-10-batches-py'image_dim = image_size * image_size * img_channels #32x32x3meta = unpickle(data_dir + '/batches.meta')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']label_count = len(label_names)#10train_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']train_data, train_labels = load_data(train_files, data_dir, label_count)#train_data[50000,32,32,3],train_labels[0,50000]test_data, test_labels = load_data(['test_batch'], data_dir, label_count)print("Train data:", np.shape(train_data), np.shape(train_labels))#Train data: (50000, 32, 32, 3) (50000, 10)print("Test data :", np.shape(test_data), np.shape(test_labels))#Test data : (10000, 32, 32, 3) (10000, 10)print("======Load finished======")#训练和测试数据读取完成print("======Shuffling data======")indices = np.random.permutation(len(train_data))#返回一个0-50000的随机排列train_data = train_data[indices]#train重新排列train_labels = train_labels[indices]#test重新排列print("======Prepare Finished======")return train_data, train_labels, test_data, test_labelsdef bias_variable(shape):initial = tf.constant(0.1, shape=shape, dtype=tf.float32)# <tf.Tensor 'Const:0' shape=(64,) dtype=float32>return tf.Variable(initial)def conv2d(x, W):#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,就是参数input的第四维#strides:卷积时在图像每一维的步长,这是一个一维的向量,长度4#padding:string类型的量,只能是"SAME","VALID"其中之一,这个值决定了不同的卷积方式#padding = 'SAME':补0,受到strides大小影响return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')#
def max_pool(input, k_size=1, stride=1, name=None):#input:需要池化的输入,一般池化层接在卷积层后面,所以输入通常是feature map,#依然是[batch, height, width, channels]这样的shape#ksize:池化窗口的大小,取一个四维向量,一般是[1, height, width, 1],#因为我们不想在batch和channels上做池化,所以这两个维度设为了1#strides:和卷积类似,窗口在每一个维度上滑动的步长,一般也是[1, stride,stride, 1]#padding:和卷积类似,可以取'VALID' 或者'SAME'#返回一个Tensor,类型不变,shape仍然是[batch, height, width, channels]这种形式return tf.nn.max_pool(input, ksize=[1, k_size, k_size, 1], strides=[1, stride, stride, 1],padding='SAME', name=name)#公式如下:
#y=γ(x-μ)/σ+β
#其中:
#x是输入,
#y是输出,
#μ是均值,
#σ是方差,
#γ和β是缩放(scale)、偏移(offset)系数。
#一般来讲,这些参数都是基于channel来做的,比如输入x是一个16*32*32*128(NWHC格式)的feature map,
#那么上述参数都是128维的向量。其中γ和β是可有可无的,
#有的话,就是一个可以学习的参数(参与前向后向),
#没有的话,就简化成y=(x-μ)/σ。
#而μ和σ,在训练的时候,使用的是batch内的统计值,
#测试/预测的时候,采用的是训练时计算出的滑动平均值。
def batch_norm(input):#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,则会添加控件依赖项以确保更新已计算到位。return tf.contrib.layers.batch_norm(input, decay=0.9, center=True, scale=True, epsilon=1e-3,is_training=train_flag, updates_collections=None)def _random_crop(batch, crop_shape, padding=None):oshape = np.shape(batch[0])#(32, 32, 3)if padding:oshape = (oshape[0] + 2*padding, oshape[1] + 2*padding)#(40, 40)元组new_batch = []npad = ((padding, padding), (padding, padding), (0, 0))#((4, 4), (4, 4), (0, 0))for i in range(len(batch)):#250new_batch.append(batch[i])if padding:#pad(array,pad_width,mode,**kwars)#其中array为要填补的数组(input)#pad_width是在各维度的各个方向上想要填补的长度,如((2,3),(4,5)),#如果直接输入一个整数,则说明各个维度和各个方向所填补的长度都一样。#mode为填补类型,即怎样去填补,有“constant”,“edge”等模式,#如果为constant模式,就得指定填补的值。new_batch[i] = np.lib.pad(batch[i], pad_width=npad,mode='constant', constant_values=0)#边缘填充,[0:32]变成[0,40]#temp = oshape[0] - crop_shape[0]nh = random.randint(0, oshape[0] - crop_shape[0])#返回[0,8]之间的整数nw = random.randint(0, oshape[1] - crop_shape[1])new_batch[i] = new_batch[i][nh:nh + crop_shape[0],nw:nw + crop_shape[1]]#长度为32return new_batchdef _random_flip_leftright(batch):for i in range(len(batch)):if bool(random.getrandbits(1)):#返回一个1位随机的integerbatch[i] = np.fliplr(batch[i])#左右翻转矩阵return batchdef data_preprocessing(x_train,x_test):x_train = x_train.astype('float32')#train数据转换为float32x_test = x_test.astype('float32')#test数据转换为float32#Z-score标准化(0-1标准化)方法,这种方法给予原始数据的均值(mean)和标准差(standard deviation)进行数据的标准化。#经过处理的数据符合标准正态分布,即均值为0,标准差为1。x_train[:, :, :, 0] = (x_train[:, :, :, 0] - np.mean(x_train[:, :, :, 0])) / np.std(x_train[:, :, :, 0])x_train[:, :, :, 1] = (x_train[:, :, :, 1] - np.mean(x_train[:, :, :, 1])) / np.std(x_train[:, :, :, 1])x_train[:, :, :, 2] = (x_train[:, :, :, 2] - np.mean(x_train[:, :, :, 2])) / np.std(x_train[:, :, :, 2])x_test[:, :, :, 0] = (x_test[:, :, :, 0] - np.mean(x_test[:, :, :, 0])) / np.std(x_test[:, :, :, 0])x_test[:, :, :, 1] = (x_test[:, :, :, 1] - np.mean(x_test[:, :, :, 1])) / np.std(x_test[:, :, :, 1])x_test[:, :, :, 2] = (x_test[:, :, :, 2] - np.mean(x_test[:, :, :, 2])) / np.std(x_test[:, :, :, 2])return x_train, x_testdef data_augmentation(batch):batch = _random_flip_leftright(batch)#[0:250]batch = _random_crop(batch, [32, 32], 4)#[250,32,32,3]return batchdef learning_rate_schedule(epoch_num):if epoch_num < 81:return 0.1elif epoch_num < 121:return 0.01else:return 0.001def run_testing(sess, ep):acc = 0.0loss = 0.0pre_index = 0add = 1000for it in range(10):batch_x = test_x[pre_index:pre_index+add]batch_y = test_y[pre_index:pre_index+add]pre_index = pre_index + addloss_, acc_  = sess.run([cross_entropy, accuracy],feed_dict={x: batch_x, y_: batch_y, keep_prob: 1.0, train_flag: False})loss += loss_ / 10.0acc += acc_ / 10.0summary = tf.Summary(value=[tf.Summary.Value(tag="test_loss", simple_value=loss),tf.Summary.Value(tag="test_accuracy", simple_value=acc)])return acc, loss, summaryif __name__ == '__main__':train_x, train_y, test_x, test_y = prepare_data()#准备数据,包括解压数据和打乱数据train_x, test_x = data_preprocessing(train_x, test_x)#数据预处理,使其符合标准正态分布# define placeholder x, y_ , keep_prob, learning_ratex = tf.placeholder(tf.float32,[None, image_size, image_size, 3])#<tf.Tensor 'Placeholder:0' shape=(?, 32, 32, 3) dtype=float32>y_ = tf.placeholder(tf.float32, [None, class_num])#<tf.Tensor 'Placeholder_1:0' shape=(?, 10) dtype=float32>keep_prob = tf.placeholder(tf.float32)#<tf.Tensor 'Placeholder_2:0' shape=<unknown> dtype=float32>learning_rate = tf.placeholder(tf.float32)#<tf.Tensor 'Placeholder_4:0' shape=<unknown> dtype=float32>train_flag = tf.placeholder(tf.bool)#<tf.Tensor 'Placeholder_5:0' shape=<unknown> dtype=bool># build_network#He正态分布初始化方法,参数由0均值,标准差为sqrt(2 / fan_in) 的正态分布产生,其中fan_in权重张量的扇入#W是卷积核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>b_conv1_1 = bias_variable([64])#<tf.Variable 'Variable:0' shape=(64,) dtype=float32_ref>#这个函数的作用是计算激活函数 relu,即 max(features, 0)。即将矩阵中每行的非最大值置0。output = tf.nn.relu(batch_norm(conv2d(x, W_conv1_1) + b_conv1_1))#<tf.Tensor 'Relu:0' shape=(?, 32, 32, 64) dtype=float32>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>b_conv1_2 = bias_variable([64])#<tf.Variable 'Variable_1:0' shape=(64,) dtype=float32_ref>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>output = max_pool(output, 2, 2, "pool1")#<tf.Tensor 'pool1_1:0' shape=(?, 16, 16, 64) dtype=float32>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>b_conv2_1 = bias_variable([128])#<tf.Variable 'Variable_2:0' shape=(128,) dtype=float32_ref>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>W_conv2_2 = tf.get_variable('conv2_2', shape=[3, 3, 128, 128], initializer=tf.contrib.keras.initializers.he_normal())b_conv2_2 = bias_variable([128])output = tf.nn.relu(batch_norm(conv2d(output, W_conv2_2) + b_conv2_2))output = max_pool(output, 2, 2, "pool2")W_conv3_1 = tf.get_variable('conv3_1', shape=[3, 3, 128, 256], initializer=tf.contrib.keras.initializers.he_normal())b_conv3_1 = bias_variable([256])output = tf.nn.relu( batch_norm(conv2d(output,W_conv3_1) + b_conv3_1))W_conv3_2 = tf.get_variable('conv3_2', shape=[3, 3, 256, 256], initializer=tf.contrib.keras.initializers.he_normal())b_conv3_2 = bias_variable([256])output = tf.nn.relu(batch_norm(conv2d(output, W_conv3_2) + b_conv3_2))W_conv3_3 = tf.get_variable('conv3_3', shape=[3, 3, 256, 256], initializer=tf.contrib.keras.initializers.he_normal())b_conv3_3 = bias_variable([256])output = tf.nn.relu( batch_norm(conv2d(output, W_conv3_3) + b_conv3_3))W_conv3_4 = tf.get_variable('conv3_4', shape=[3, 3, 256, 256], initializer=tf.contrib.keras.initializers.he_normal())b_conv3_4 = bias_variable([256])output = tf.nn.relu(batch_norm(conv2d(output, W_conv3_4) + b_conv3_4))output = max_pool(output, 2, 2, "pool3")W_conv4_1 = tf.get_variable('conv4_1', shape=[3, 3, 256, 512], initializer=tf.contrib.keras.initializers.he_normal())b_conv4_1 = bias_variable([512])output = tf.nn.relu(batch_norm(conv2d(output, W_conv4_1) + b_conv4_1))W_conv4_2 = tf.get_variable('conv4_2', shape=[3, 3, 512, 512], initializer=tf.contrib.keras.initializers.he_normal())b_conv4_2 = bias_variable([512])output = tf.nn.relu(batch_norm(conv2d(output, W_conv4_2) + b_conv4_2))W_conv4_3 = tf.get_variable('conv4_3', shape=[3, 3, 512, 512], initializer=tf.contrib.keras.initializers.he_normal())b_conv4_3 = bias_variable([512])output = tf.nn.relu(batch_norm(conv2d(output, W_conv4_3) + b_conv4_3))W_conv4_4 = tf.get_variable('conv4_4', shape=[3, 3, 512, 512], initializer=tf.contrib.keras.initializers.he_normal())b_conv4_4 = bias_variable([512])output = tf.nn.relu(batch_norm(conv2d(output, W_conv4_4)) + b_conv4_4)output = max_pool(output, 2, 2)W_conv5_1 = tf.get_variable('conv5_1', shape=[3, 3, 512, 512], initializer=tf.contrib.keras.initializers.he_normal())b_conv5_1 = bias_variable([512])output = tf.nn.relu(batch_norm(conv2d(output, W_conv5_1) + b_conv5_1))W_conv5_2 = tf.get_variable('conv5_2', shape=[3, 3, 512, 512], initializer=tf.contrib.keras.initializers.he_normal())b_conv5_2 = bias_variable([512])output = tf.nn.relu(batch_norm(conv2d(output, W_conv5_2) + b_conv5_2))W_conv5_3 = tf.get_variable('conv5_3', shape=[3, 3, 512, 512], initializer=tf.contrib.keras.initializers.he_normal())b_conv5_3 = bias_variable([512])output = tf.nn.relu(batch_norm(conv2d(output, W_conv5_3) + b_conv5_3))W_conv5_4 = tf.get_variable('conv5_4', shape=[3, 3, 512, 512], initializer=tf.contrib.keras.initializers.he_normal())b_conv5_4 = bias_variable([512])output = tf.nn.relu(batch_norm(conv2d(output, W_conv5_4) + b_conv5_4))# output = tf.contrib.layers.flatten(output)output = tf.reshape(output, [-1, 2*2*512])#<tf.Tensor 'Reshape:0' shape=(?, 2048) dtype=float32>W_fc1 = tf.get_variable('fc1', shape=[2048, 4096], initializer=tf.contrib.keras.initializers.he_normal())b_fc1 = bias_variable([4096])output = tf.nn.relu(batch_norm(tf.matmul(output, W_fc1) + b_fc1) )#tf.nn.dropout是TensorFlow里面为了防止或减轻过拟合而使用的函数,它一般用在全连接层。#Dropout就是在不同的训练过程中随机扔掉一部分神经元。也就是让某个神经元的激活值以一定的概率p,让其停止工作,#这次训练过程中不更新权值,也不参加神经网络的计算。但是它的权重得保留下来(只是暂时不更新而已),因为下次样本输入时它可能又得工作了。#第一个参数output:指输入#第二个参数keep_prob: 设置神经元被选中的概率,在初始化时keep_prob是一个占位符, keep_prob = tf.placeholder(tf.float32)。#tensorflow在run时设置keep_prob具体的值,例如keep_prob: 0.5output = tf.nn.dropout(output, keep_prob)W_fc2 = tf.get_variable('fc7', shape=[4096, 4096], initializer=tf.contrib.keras.initializers.he_normal())b_fc2 = bias_variable([4096])output = tf.nn.relu(batch_norm(tf.matmul(output, W_fc2) + b_fc2))output = tf.nn.dropout(output, keep_prob)W_fc3 = tf.get_variable('fc3', shape=[4096, 10], initializer=tf.contrib.keras.initializers.he_normal())b_fc3 = bias_variable([10])output = tf.nn.relu(batch_norm(tf.matmul(output, W_fc3) + b_fc3))# output  = tf.reshape(output,[-1,10])# loss function: cross_entropy# train_step: training operation#labels:一个分类标签,所不同的是,这个labels是分类的概率,#比如说[0.2,0.3,0.5],labels的每一行必须是一个概率分布(即概率之合加起来为1)。#logits:logit的值域范围[-inf,+inf](即正负无穷区间)。#我们可以把logist理解为原生态的、未经缩放的,可视为一种未归一化的l“概率替代物”,#如[4, 1, -2]。它可以是其他分类器(如逻辑回归等、SVM等)的输出。#Softmax把一个系列的概率替代物(logits)从[-inf, +inf] 映射到[0,1]。#经过softmax的加工,就变成“归一化”的概率(设为p1),这个新生成的概率p1,和labels所代表的概率分布(设为p2)一起作为参数,用来计算交叉熵。#这个差异信息,作为我们网络调参的依据,理想情况下,这两个分布尽量趋近最好。#如果有差异(也可以理解为误差信号),我们就调整参数,让其变得更小,这就是损失(误差)函数的作用。cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=output))#logit=log(odds)=log(P/(1-P))#l2_loss:1/2Σvar2或者output = sum(t ** 2) / 2#L1正则化是指权值向量w中各个元素的绝对值之和,通常表示为||w||1#L2正则化是指权值向量w中各个元素的平方和然后再求平方根(可以看到Ridge回归的L2正则化项有平方符号),通常表示为||w||2 #也就是说Lx范数应用于优化的目标函数就叫做Lx正则化#l2_loss一般用于优化目标函数中的正则项,防止参数太多复杂容易过拟合(所谓的过拟合问题是指当一个模型很复杂时,#它可以很好的“记忆”每一个训练数据中的随机噪声的部分而忘记了要去“学习”训练数据中通用的趋势)#多个l2(var向量)对应元素相加变为1行varl2 = tf.add_n([tf.nn.l2_loss(var) for var in tf.trainable_variables()])#动量梯度下降算法#learning_rate: (学习率)张量或者浮点数#momentum: (动量)张量或者浮点数#use_locking: 为True时锁定更新#name:  梯度下降名称,默认为 "Momentum".#use_nesterov:  为True时,使用 Nesterov Momentum. train_step = tf.train.MomentumOptimizer(learning_rate, momentum_rate, use_nesterov=True).\minimize(cross_entropy + l2 * weight_decay)#tf.argmax( , )中有两个参数,第一个参数是矩阵,第二个参数是0或者1。#0表示的是按列比较返回最大值的索引,#1表示按行比较返回最大值的索引。#tf.equal(A, B)是对比这两个矩阵或者向量的相等的元素,#如果是相等的那就返回True,否则返回False,#返回的值的矩阵维度和A是一样的correct_prediction = tf.equal(tf.argmax(output, 1), tf.argmax(y_, 1))#将x的数据格式转化成dtype.例如,原来x的数据格式是bool,那么将其转化成float以后,就能够将其转化成0和1的序列。反之也可以accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))# initial an saver to save modelsaver = tf.train.Saver()with tf.Session() as sess:sess.run(tf.global_variables_initializer())#初始化全局变量summary_writer = tf.summary.FileWriter(log_save_path,sess.graph)#log是事件文件所在的目录,这里是工程目录下的log目录。第二个参数是事件文件要记录的图,也就是tensorflow默认的图。if os.path.exists(model_save_path):#模型的恢复用的是restore()函数,它需要两个参数restore(sess, save_path),#save_path指的是保存的模型路径。#我们可以使用tf.train.latest_checkpoint()来自动获取最后一次保存的模型。saver.restore(sess,model_save_path+"vgg19.ckpt")# epoch = 164# make sure [bath_size * iteration = data_set_number]for ep in range(1, total_epoch+1):#total_epoch = 164lr = learning_rate_schedule(ep)#学习率变化时间表pre_index = 0train_acc = 0.0train_loss = 0.0start_time = time.time()print("\n epoch %d/%d:" % (ep, total_epoch))for it in range(1, iterations+1):#iterations = 200batch_x = train_x[pre_index:pre_index+batch_size]#batch_size = 250batch_y = train_y[pre_index:pre_index+batch_size]batch_x = data_augmentation(batch_x)_, batch_loss = sess.run([train_step, cross_entropy],feed_dict={x: batch_x, y_: batch_y, keep_prob: dropout_rate,learning_rate: lr, train_flag: True})batch_acc = accuracy.eval(feed_dict={x: batch_x, y_: batch_y, keep_prob: 1.0, train_flag: True})train_loss += batch_losstrain_acc += batch_accpre_index += batch_sizeif it == iterations:train_loss /= iterationstrain_acc /= iterations#第一个参数是要求的结果#第二个参数feed_dict是给placeholder赋值loss_, acc_ = sess.run([cross_entropy, accuracy],feed_dict={x: batch_x, y_: batch_y, keep_prob: 1.0, train_flag: True})train_summary = tf.Summary(value=[tf.Summary.Value(tag="train_loss", simple_value=train_loss),tf.Summary.Value(tag="train_accuracy", simple_value=train_acc)])val_acc, val_loss, test_summary = run_testing(sess, ep)summary_writer.add_summary(train_summary, ep)summary_writer.add_summary(test_summary, ep)summary_writer.flush()print("iteration: %d/%d, cost_time: %ds, train_loss: %.4f, ""train_acc: %.4f, test_loss: %.4f, test_acc: %.4f"% (it, iterations, int(time.time()-start_time), train_loss, train_acc, val_loss, val_acc))else:print("iteration: %d/%d, train_loss: %.4f, train_acc: %.4f"% (it, iterations, train_loss / it, train_acc / it), end='\r')save_path = saver.save(sess, model_save_path+"vgg19.ckpt")print("Model saved in file: %s" % save_path)

二、项目结构

1.由于使用的是vs2017或vs2019,因此需要新建一个项目,可参考博主的博客: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。

转化公式为:

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

2. 网络部分

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

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

#He正态分布初始化方法,参数由0均值,标准差为sqrt(2 / fan_in) 的正态分布产生,其中fan_in权重张量的扇入
#W是卷积核
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])
#这个函数的作用是计算激活函数 relu,即 max(features, 0)。即将矩阵中每行的非最大值置0。
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')

def conv2d(x, W):#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大小影响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()

def batch_norm(input):#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,则会添加控件依赖项以确保更新已计算到位。return tf.contrib.layers.batch_norm(input, decay=0.9, center=True, scale=True, epsilon=1e-3,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)

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

train_step = tf.train.MomentumOptimizer(learning_rate, momentum_rate, use_nesterov=True).\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.准确率

    correct_prediction = tf.equal(tf.argmax(output, 1), tf.argmax(y_, 1))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时会有转变,代码部分如下:

def learning_rate_schedule(epoch_num):if epoch_num < 81:return 0.1elif epoch_num < 121:return 0.01else: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安装和使用教程(详细)

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

  1. 深度学习入门——利用卷积神经网络训练CIFAR—10数据集

    CIFAR-10数据集简介 CIFAR-10是由Hinton的学生Alex Krizhevsky和Ilya Sutskever整理的一个用于普适物体的小型数据集.它一共包含10个类别的RGB彩色图片: ...

  2. vs2019新建android生成app,VS2017 VS2019创建离线安装包

    VS2017&VS2019创建离线安装包 微软教程 下载安装引导程序 以下链接均来自微软官网 创建安装包命令 以VS2017专业版为例,VS2019自带.NET 4.7.2相关内容,可去除相关 ...

  3. 如何制作自己的图片数据集-基于tensorflow

    写在开始 自己最开始接触python的时候,第一个学会使用的库就是tensorflow,在经历了everyone 都会经历的mnist数据集训练后,就开始想自己做一个图片分类的深度学习,期间也是一波三 ...

  4. 2020-08-21 Windows 10安装 Anaconda+tensorflow 配置GPU

    Windows 10安装 Anaconda+tensorflow 配置GPU 参考链接 https://blog.csdn.net/hitzijiyingcai/article/details/833 ...

  5. vs2017\vs2019 动态规划算法实现0-1背包问题 C

    这是针对于博客vs2017安装和使用教程(详细)和vs2019安装和使用教程(详细)的动态规划算法实现0-1背包问题的示例 目录 一.问题描述

  6. VS2013/VS2015/VS2017下使用ArcEngine10.1/10.2

    背景 ArcObjects SDK for Microsoft .Net Framework 10.1或10.2在安装时,会检测VS2010或VS2012的安装路径,而有时因为其他项目使用限制而且不愿 ...

  7. 基于SVM的思想做CIFAR 10图像分类

    #SVM 回顾一下之前的SVM,找到一个间隔最大的函数,使得正负样本离该函数是最远的,是否最远不是看哪个点离函数最远,而是找到一个离函数最近的点看他是不是和该分割函数离的最近的. 使用large ma ...

  8. cifar 10 最高正确率

    http://rodrigob.github.io/are_we_there_yet/build/classification_datasets_results.html 这个网站里有MNIST数据集 ...

  9. 从微软官网下载VS离线安装包的方法VS2017,VS2019

    这里描述是包括所有版本,截图以下载VS2017社区版为例: ①登入VS官网下载页面,选择需要的版本点击下载,下载页点此进入. ②下载完成后,打开下载文件所在文件夹,Windows 8.1及以上版本用户 ...

最新文章

  1. 【UNIX网络编程(二)】基本TCP套接字编程函数
  2. Linux下的一些问题收集及解决方法(一)
  3. ubuntu20.04安装讯飞输入法(失败经历)
  4. cordova监听事件中调用其他方法_Laravel模型事件的实现原理详解
  5. 计算机网络之物理层:4、编码和调制
  6. java高并发临时表_不适用临时表进行分页,筛选,查询,避免高并发的方法。...
  7. Spring4.x学习(1)
  8. shell学习之常用bash内置变量
  9. Linux入门:PuTTY实现Linux和Windows文件互传
  10. 正则表达式 学习笔记2.2
  11. 关于wince/mobile 同步软件XP activesync ,WindowsMobile设备中心的连接问题分析
  12. 哀悼日-全网变灰效果实现
  13. 介绍身份证号姓名实名认证 身份证号姓名校验 身份证号验人API
  14. 计算机社团活动照片,电脑绘画兴趣小组活动记录表Word版
  15. DOSBox+MASM,汇编语言环境搭建
  16. 你不知道的原生js广播频道接口
  17. c语言计算机图形来画八分画圆,计算机图形学:中点画圆算法
  18. lv官网编码查询_老太太拿LV买菜装大葱!结果闺女被抓了......
  19. Axure RP9基本操作
  20. 计算机操作系统原理--Linux实例分析

热门文章

  1. 【幻灯片动画效果制作】Focusky教程 | 设置录音和动画同步
  2. MySQL中的各种查询
  3. 【商业画布】魏朱画布
  4. 比较全面的安全测试用例设计思路
  5. jpg转bmpbmp转jpg
  6. 学习笔记5(类和对象)
  7. 计算机电缆参数,计算机电缆的几个参数要求
  8. 小米米家投影仪青春版怎么样,和当贝d3x怎么选全面解析让你知道
  9. 一对情侣用计算机电影,一个剧情电影,讲述一对情侣谈恋爱的故事
  10. Java经典算法题目(兔子题)及题目分析