本人上一篇博客介绍了vgg16模型的finetuning(网址:点击打开链接),在cpu上跑没问题,但是我在全部复制到gpu(gtx750TI)上跑却发生爆内存的情况,下面介绍一下我的解决办法。
不得不承认爆内存跟我的渣渣gpu有关,为了尽量的减少gpu内存的使用率,在加载vgg16模型的时候并没有直接调用命令,而是把网络的权重跟偏置都设为常值,直接赋值,减少了以后sess.run(tf.global_variables_initializer())这句明令时因为run所有的变量而带来的内存消耗。
关于vgg16模型下载上面的链接中有,自行查看。而查看vgg16模型里面的具体代码如下,我们可以看见每一部分的数值:
weight_file = 'vgg/vgg16_weights.npz' #自己改文件的地址
weights = np.load(weight_file)
keys = sorted(weights.keys())
print(weights[keys[29]])#此处查看的是第30个部分的数值
其次关于网上说的batch_size太大也有关系,我电脑这次设置的batch_size为16,实验成功。
整个vgg16在gpu上训练的代码如下:
import tensorflow as tf
import numpy as np
import os
N_CLASSES = 2 # 2个输出神经元,[1,0] 或者 [0,1]猫和狗的概率
IMG_W = 224 # 重新定义图片的大小,图片如果过大则训练比较慢
IMG_H = 224
BATCH_SIZE = 16 #每批数据的大小
CAPACITY = 256 #队列容量,队列中最多容纳图片的个数
MAX_STEP = 15000 # 训练的步数,应当 >= 10000
learning_rate = 0.0001 # 学习率,建议刚开始的 learning_rate <= 0.0001
file_dir = 'catdog/train/'
weight_file = 'vgg/vgg16_weights.npz'
weights = np.load(weight_file)
keys = sorted(weights.keys())
#print(weights[keys[0]])
def network(imgs, batch_size, n_classes):
# zero-mean input
with tf.name_scope('preprocess') as scope:
mean = tf.constant([123.68, 116.779, 103.939], dtype=tf.float32, shape=[1, 1, 1, 3], name='img_mean')
images = imgs-mean
# conv1_1
with tf.name_scope('conv1_1') as scope:
kernel = weights[keys[0]]
conv = tf.nn.conv2d(images, kernel, [1, 1, 1, 1], padding='SAME')
biases = weights[keys[1]]
out = tf.nn.bias_add(conv, biases)
conv1_1 = tf.nn.relu(out, name=scope)
# conv1_2
with tf.name_scope('conv1_2') as scope:
kernel = weights[keys[2]]
conv = tf.nn.conv2d(conv1_1, kernel, [1, 1, 1, 1], padding='SAME')
biases = weights[keys[3]]
out = tf.nn.bias_add(conv, biases)
conv1_2 = tf.nn.relu(out, name=scope)
# pool1
pool1 = tf.nn.max_pool(conv1_2,
ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1],
padding='SAME',
name='pool1')
# conv2_1
with tf.name_scope('conv2_1') as scope:
kernel = weights[keys[4]]
conv = tf.nn.conv2d(pool1, kernel, [1, 1, 1, 1], padding='SAME')
biases = weights[keys[5]]
out = tf.nn.bias_add(conv, biases)
conv2_1 = tf.nn.relu(out, name=scope)
# conv2_2
with tf.name_scope('conv2_2') as scope:
kernel = weights[keys[6]]
conv = tf.nn.conv2d(conv2_1, kernel, [1, 1, 1, 1], padding='SAME')
biases = weights[keys[7]]
out = tf.nn.bias_add(conv, biases)
conv2_2 = tf.nn.relu(out, name=scope)
# pool2
pool2 = tf.nn.max_pool(conv2_2,
ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1],
padding='SAME',
name='pool2')
# conv3_1
with tf.name_scope('conv3_1') as scope:
kernel = weights[keys[8]]
conv = tf.nn.conv2d(pool2, kernel, [1, 1, 1, 1], padding='SAME')
biases = weights[keys[9]]
out = tf.nn.bias_add(conv, biases)
conv3_1 = tf.nn.relu(out, name=scope)
# conv3_2
with tf.name_scope('conv3_2') as scope:
kernel = weights[keys[10]]
conv = tf.nn.conv2d(conv3_1, kernel, [1, 1, 1, 1], padding='SAME')
biases = weights[keys[11]]
out = tf.nn.bias_add(conv, biases)
conv3_2 = tf.nn.relu(out, name=scope)
# conv3_3
with tf.name_scope('conv3_3') as scope:
kernel = weights[keys[12]]
conv = tf.nn.conv2d(conv3_2, kernel, [1, 1, 1, 1], padding='SAME')
biases = weights[keys[13]]
out = tf.nn.bias_add(conv, biases)
conv3_3 = tf.nn.relu(out, name=scope)
# pool3
pool3 = tf.nn.max_pool(conv3_3,
ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1],
padding='SAME',
name='pool3')
# conv4_1
with tf.name_scope('conv4_1') as scope:
kernel = weights[keys[14]]
conv = tf.nn.conv2d(pool3, kernel, [1, 1, 1, 1], padding='SAME')
biases = weights[keys[15]]
out = tf.nn.bias_add(conv, biases)
conv4_1 = tf.nn.relu(out, name=scope)
# conv4_2
with tf.name_scope('conv4_2') as scope:
kernel = weights[keys[16]]
conv = tf.nn.conv2d(conv4_1, kernel, [1, 1, 1, 1], padding='SAME')
biases = weights[keys[17]]
out = tf.nn.bias_add(conv, biases)
conv4_2 = tf.nn.relu(out, name=scope)
# conv4_3
with tf.name_scope('conv4_3') as scope:
kernel = weights[keys[18]]
conv = tf.nn.conv2d(conv4_2, kernel, [1, 1, 1, 1], padding='SAME')
biases = weights[keys[19]]
out = tf.nn.bias_add(conv, biases)
conv4_3 = tf.nn.relu(out, name=scope)
# pool4
pool4 = tf.nn.max_pool(conv4_3,
ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1],
padding='SAME',
name='pool4')
# conv5_1
with tf.name_scope('conv5_1') as scope:
kernel = weights[keys[20]]
conv = tf.nn.conv2d(pool4, kernel, [1, 1, 1, 1], padding='SAME')
biases = weights[keys[21]]
out = tf.nn.bias_add(conv, biases)
conv5_1 = tf.nn.relu(out, name=scope)
# conv5_2
with tf.name_scope('conv5_2') as scope:
kernel = weights[keys[22]]
conv = tf.nn.conv2d(conv5_1, kernel, [1, 1, 1, 1], padding='SAME')
biases = weights[keys[23]]
out = tf.nn.bias_add(conv, biases)
conv5_2 = tf.nn.relu(out, name=scope)
# conv5_3
with tf.name_scope('conv5_3') as scope:
kernel = weights[keys[24]]
conv = tf.nn.conv2d(conv5_2, kernel, [1, 1, 1, 1], padding='SAME')
biases = weights[keys[25]]
out = tf.nn.bias_add(conv, biases)
conv5_3 = tf.nn.relu(out, name=scope)
# pool5
pool5 = tf.nn.max_pool(conv5_3,
ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1],
padding='SAME',
name='pool4')
# fc1
with tf.name_scope('fc1') as scope:
reshape = tf.reshape(pool5, shape=[batch_size, -1])
fc1w = weights[keys[26]]
fc1b = weights[keys[27]]
#pool5_flat = tf.reshape(pool5, [-1, shape])
fc1l = tf.nn.bias_add(tf.matmul(reshape, fc1w), fc1b)
fc1 = tf.nn.relu(fc1l)
# fc2
with tf.name_scope('fc2') as scope:
fc2w = weights[keys[28]]
fc2b = weights[keys[29]]
fc2l = tf.nn.bias_add(tf.matmul(fc1, fc2w), fc2b)
fc2 = tf.nn.relu(fc2l)
# fc3
with tf.name_scope('fc3') as scope:
fc3w = tf.Variable(tf.truncated_normal([4096, n_classes],
dtype=tf.float32,
stddev=1e-1), trainable=True,name='weights')
fc3b = tf.Variable(tf.constant(1.0, shape=[n_classes], dtype=tf.float32),
trainable=True, name='biases')
fc3l = tf.nn.bias_add(tf.matmul(fc2, fc3w), fc3b)
fc3l = tf.nn.softmax(fc3l)
print(fc3l)
return fc3l
# def load_weights(self, weight_file, sess):
# weights = np.load(weight_file)
# keys = sorted(weights.keys())
# for i, k in enumerate(keys):
# if i not in [30,31]:
# print (i, k, np.shape(weights[k]))
# sess.run(self.parameters[i].assign(weights[k]))
# print("-------------all done-------------")
def get_files(file_dir):
# file_dir: 文件夹路径
# return: 乱序后的图片和标签
cats = []
label_cats = []
dogs = []
label_dogs = []
# 载入数据路径并写入标签值
for file in os.listdir(file_dir):
name = file.split('.')
if name[0] == 'cat':
cats.append(file_dir + file) #修改原来的列表,增加对象
label_cats.append(0)
else:
dogs.append(file_dir + file)
label_dogs.append(1)
print("There are %d cats\nThere are %d dogs" % (len(cats), len(dogs)))
# 打乱文件顺序
image_list = np.hstack((cats, dogs)) #把猫放在前面,狗在后面组成一个文件
label_list = np.hstack((label_cats, label_dogs))
temp = np.array([image_list, label_list]) #生成序列
temp = temp.transpose() # 转置
np.random.shuffle(temp)
image_list = list(temp[:, 0]) #下标索引,访问列表值
label_list = list(temp[:, 1])
label_list = [int(i) for i in label_list]
return image_list, label_list
def get_batch(image,label,image_W,image_H,batch_size,capacity):
# 转换数据为 ts 能识别的格式
image = tf.cast(image,tf.string) #将image格式转化为字符串
label = tf.cast(label, tf.int32)
# 将image 和 label 放倒队列里
input_queue = tf.train.slice_input_producer([image,label])
label = input_queue[1]
# 读取图片的全部信息
image_contents = tf.read_file(input_queue[0])
# 把图片解码,channels =3 为彩色图片, r,g ,b 黑白图片为 1 ,也可以理解为图片的厚度
image = tf.image.decode_jpeg(image_contents,channels =3)
# 将图片以图片中心进行裁剪或者扩充为 指定的image_W,image_H
image = tf.image.resize_image_with_crop_or_pad(image, image_W, image_H)
# 对数据进行标准化,标准化,就是减去它的均值,除以他的方差
image = tf.image.per_image_standardization(image)
# 生成批次 num_threads 有多少个线程根据电脑配置设置 capacity 队列中 最多容纳图片的个数 tf.train.shuffle_batch 打乱顺序,
image_batch, label_batch = tf.train.batch([image, label],batch_size = batch_size, num_threads = 64, capacity = capacity)
# 重新定义下 label_batch 的形状
label_batch = tf.reshape(label_batch , [batch_size])
# 转化图片格式
image_batch = tf.cast(image_batch,tf.float32)
return image_batch, label_batch
def onehot(labels):
'''''one-hot'''
n_sample = len(labels)
n_class = max(labels)+1
onehot_labels = np.zeros((n_sample ,n_class))
onehot_labels[np.arange(n_sample),labels] = 1
return onehot_labels
def losses(logits, labels):
with tf.variable_scope('loss') as scope:
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits \
(logits=logits, labels=labels, name='xentropy_per_example')
loss = tf.reduce_mean(cross_entropy, name='loss')
return loss
def trainning(loss, learning_rate):
with tf.name_scope('optimizer'):
optimizer = tf.train.AdamOptimizer(learning_rate= learning_rate) #自适应优化器
global_step = tf.Variable(0, name='global_step', trainable=False) #记录全局训练步骤的单值
train_op = optimizer.minimize(loss, global_step= global_step)
return train_op
def evaluation(logits, labels):
with tf.variable_scope('accuracy') as scope:
# k:每个样本的预测结果的前k个最大的数里面是否包含targets预测中的标签,一般都是取1,即取预测最大概率的索引与标签对比。
correct = tf.nn.in_top_k(logits, labels, 1)
correct = tf.cast(correct, tf.float16)
accuracy = tf.reduce_mean(correct)
return accuracy
def run_training():
# 获取图片和标签集
train, train_label = get_files(file_dir)
# 生成批次
train_batch, train_label_batch = get_batch(train,
train_label,
IMG_W,
IMG_H,
BATCH_SIZE,
CAPACITY)
# 进入模型
train_logits = network(train_batch, BATCH_SIZE, N_CLASSES)
# 获取 loss
train_loss = losses(train_logits, train_label_batch)
# 训练
train_op = trainning(train_loss, learning_rate)
# 获取准确率
train__acc = evaluation(train_logits, train_label_batch)
# 合并 summary
sess = tf.Session()
sess.run(tf.global_variables_initializer())
###TensorFlow提供了两个类来帮助多线程的实现:tf.Coordinator和 tf.QueueRunner。
#从设计上这两个类必须被一起使用。Coordinator类可以用来同时停止多个工作线程并且向那个在等待所有工作线程终止的程序报告异常。
#QueueRunner类用来协调多个工作线程同时将多个张量推入同一个队列中
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
try:
for step in np.arange(MAX_STEP):
if coord.should_stop():
break
_, tra_loss, tra_acc = sess.run([train_op, train_loss, train__acc])
if step % 50== 0:
print('Step %d, train loss = %.2f, train accuracy = %.2f%%' %(step, tra_loss, tra_acc*100.0))
except tf.errors.OutOfRangeError:
print('Done training -- epoch limit reached')
finally:
coord.request_stop()
coord.join(threads)
sess.close()
# train
run_training()