最近学习了TensorFlow,发现一个模型叫vgg16,而后搭建环境跑了一下,以为十分神奇,并且准确率十分的高。又上了一节选修课,关于人工智能,老师让作一个关于人工智能的试验,因而以为vgg16很不错,能够直接用。python
但发现vgg16是训练好的模型,拿来直接用太没水平,因而网上发现说能够用vgg16进行迁移学习。git
我理解的迁移学习:windows
迁移学习符合人们学习的过程,若是要学习同样新东西,咱们确定会运用或是借鉴以前的学习经验,这样可以快速的把握要点,可以快速的学习。迁移学习也是如此。数组
vgg16模型是前人训练出的可以识别1000种物品的模型,并且识别率很高,它的模型如图:网络
能够数出绿色的模块一共有16层,经过多层的卷积和池化,会提取图片特征值,而后把图片压缩成一个一维数组输入到全链接层中,图中有三层全链接层fc1,fc2,fc3,再通过softmax输出几率分布的预测结果。app
进过试验,这个模型可以很好的识别花,可是若是要在此基础上识别多种花,还须要在此基础上进行训练,可是训练的过程将简化不少。接下来经过代码来说解函数
项目:学习
Transfer_learning:测试
--checkpoint #用来保存模型优化
-- #自动生成的四个文件
--flower_photos #图片
--daisy
--dandeline
--roses
--sunflowers
--tulips
#vgg16模型
--utils.py
--vgg16.npy
--vgg16.py
--app.py
--ftrain.py
--get_features.py
--transfer_test.py
--transfer_train.py
--codes.npy #存储图片特征值
--labels #存储图片的标签
--1.jpg #检测图片
--.......
第一步获取图片的特征,对于vgg16模型,fc1层以前所作的就是提取图片特征,咱们无需再费事去训练模型去提取图片的特征,而是直接使用它提供的完善的模型去提取特征,而且它可以很好的把握图片的特征,而后它这些特征存储起来,用于下面的训练。
get_features.py
#coding=utf-8 import os import numpy as np import tensorflow as tf import vgg16 import utils #接下来咱们将 flower_photos 文件夹中的花朵图片都载入到进来,而且用图片所在的子文件夹做为标签值。 data_dir = 'flower_photos/' contents = os.listdir(data_dir) classes = [each for each in contents if os.path.isdir(data_dir + each)] #利用vgg16计算获得特征值 # 首先设置计算batch的值,若是运算平台的内存越大,这个值能够设置得越高 batch_size = 10 # 用codes_list来存储特征值 codes_list = [] # 用labels来存储花的类别 labels = [] # batch数组用来临时存储图片数据 batch = [] codes = None with tf.Session() as sess: # 构建VGG16模型对象 vgg = vgg16.Vgg16() input_ = tf.placeholder(tf.float32, [None, 224, 224, 3]) with tf.name_scope("content_vgg"): # 载入VGG16模型 vgg.build(input_) # 对每一个不一样种类的花分别用VGG16计算特征值 for each in classes: print ("Starting {} images".format(each)) class_path = data_dir + each files = os.listdir(class_path) for ii, file in enumerate(files, 1): # 载入图片并放入batch数组中 img = utils.load_image(os.path.join(class_path, file)) batch.append(img.reshape((1, 224, 224, 3))) labels.append(each) # 若是图片数量到了batch_size则开始具体的运算 if ii % batch_size == 0 or ii == len(files): images = np.concatenate(batch) feed_dict = {input_: images} # 计算特征值 codes_batch = sess.run(vgg.relu6, feed_dict=feed_dict) # 将结果放入到codes数组中 if codes is None: codes = codes_batch else: codes = np.concatenate((codes, codes_batch)) # 清空数组准备下一个batch的计算 batch = [] print ('{} images processed'.format(ii)) #code is a two-dimensional array including features of all pictures #这样咱们就能够获得一个 codes 数组,和一个 labels 数组,分别存储了全部花朵的特征值和类别。 #能够用以下的代码将这两个数组保存到硬盘上: #with open('codes', 'w') as f: np.save("codes.npy",codes) #codes.tofile(f) #not good size of file is too big #pickle.dump(codes,f) import csv with open('labels', 'w') as f: writer = csv.writer(f, delimiter='\n') writer.writerow(labels) #pickle.dump(labels,f)
进过上面代码咱们已经获得了图片的特征值和标签,接下来,咱们须要设置一个全链接层来训练,下面代码中增长了一层256个节点和5个节点的全链接层。
ftrain.py
#coding=utf-8 import os import numpy as np import tensorflow as tf import matplotlib.pyplot as plt from sklearn.preprocessing import LabelBinarizer import vgg16 import utils from sklearn.model_selection import StratifiedShuffleSplit #模型保存的路径和名称 MODEL_SAVE_PATH="./checkpoints/" MODEL_NAME="flowers.ckpt" LABELS = "labels" CODES = "codes.npy" codes = None label = [] labels = [] if CODES: codes = np.load(CODES) else: print ("No such file,please run get_feature.py first") if LABELS: with open(LABELS,"r") as f: label = f.readlines() for line in label: line = line.strip() labels.append(line) else: print ("No such file,please run get_feature.py first") #准备训练集,验证集和测试集 #首先我把 labels 数组中的分类标签用 One Hot Encode 的方式替换 lb = LabelBinarizer() lb.fit(labels) labels_vecs = lb.transform(labels) #return codes,labels,labels_vecs ''' 接下来就是抽取数据,由于不一样类型的花的数据数量并非彻底同样的, 并且 labels 数组中的数据也尚未被打乱, 因此最合适的方法是使用 StratifiedShuffleSplit 方法来进行分层随机划分。 假设咱们使用训练集:验证集:测试集 = 8:1:1,那么代码以下: ''' ss = StratifiedShuffleSplit(n_splits=1, test_size=0.2) train_idx, val_idx = next(ss.split(codes, labels)) half_val_len = int(len(val_idx)/2) val_idx, test_idx = val_idx[:half_val_len], val_idx[half_val_len:] train_x, train_y = codes[train_idx], labels_vecs[train_idx] val_x, val_y = codes[val_idx], labels_vecs[val_idx] test_x, test_y = codes[test_idx], labels_vecs[test_idx] print ("Train shapes (x, y):", train_x.shape, train_y.shape) print ("Validation shapes (x, y):", val_x.shape, val_y.shape) print ("Test shapes (x, y):", test_x.shape, test_y.shape) #训练网络 ''' 分好了数据集以后,就能够开始对数据集进行训练了, 假设咱们使用一个 256 维的全链接层, 一个 5 维的全链接层(由于咱们要分类五种不一样类的花朵), 和一个 softmax 层。固然,这里的网络结构能够任意修改, 你能够不断尝试其余的结构以找到合适的结构。 ''' # 输入数据的维度 # 标签数据的维度 labels_ = tf.placeholder(tf.int64, shape=[None, labels_vecs.shape[1]]) inputs_ = tf.placeholder(tf.float32, shape=[None, codes.shape[1]]) # 加入一个256维的全链接的层 fc = tf.contrib.layers.fully_connected(inputs_, 256) # 加入一个5维的全链接层 logits = tf.contrib.layers.fully_connected(fc, labels_vecs.shape[1], activation_fn=None) # 获得最后的预测分布 predicted = tf.nn.softmax(logits) # 计算cross entropy值 cross_entropy = tf.nn.softmax_cross_entropy_with_logits(labels=labels_, logits=logits) # 计算损失函数 cost = tf.reduce_mean(cross_entropy) # 采用用得最普遍的AdamOptimizer优化器 optimizer = tf.train.AdamOptimizer().minimize(cost) correct_pred = tf.equal(tf.argmax(predicted, 1), tf.argmax(labels_, 1)) accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) #为了方便把数据分红一个个 batch 以下降内存的使用,还能够再用一个函数专门用来生成 batch。 def get_batches(x, y, n_batches=10): """ 这是一个生成器函数,按照n_batches的大小将数据划分了小块 """ batch_size = len(x)//n_batches for ii in range(0, n_batches*batch_size, batch_size): # 若是不是最后一个batch,那么这个batch中应该有batch_size个数据 if ii != (n_batches-1)*batch_size: X, Y = x[ii: ii+batch_size], y[ii: ii+batch_size] # 不然的话,那剩余的不够batch_size的数据都凑入到一个batch中 else: X, Y = x[ii:], y[ii:] # 生成器语法,返回X和Y yield X, Y
通过上面的代码,已经把图片集分红三部分,并且也设置好全链接成,接下来须要把图片特征喂入,开始训练模型。
transfer_train.py
import os import numpy as np import tensorflow as tf import matplotlib.pyplot as plt import vgg16 import utils import ftrain #运行 # 运行多少轮次 epochs = 20 # 统计训练效果的频率 iteration = 0 # 保存模型的保存器 saver = tf.train.Saver() with tf.Session() as sess: sess.run(tf.global_variables_initializer()) coord = tf.train.Coordinator()#4 threads = tf.train.start_queue_runners(sess=sess, coord=coord)#5 for e in range(epochs): for x, y in ftrain.get_batches(ftrain.train_x, ftrain.train_y): feed = {ftrain.inputs_: x, ftrain.labels_: y} # 训练模型 loss, _ = sess.run([ftrain.cost, ftrain.optimizer], feed_dict=feed) print ("Epoch: {}/{}".format(e+1, epochs), "Iteration: {}".format(iteration), "Training loss: {:.5f}".format(loss)) iteration += 1 if iteration % 5 == 0: feed = {ftrain.inputs_: ftrain.val_x, ftrain.labels_: ftrain.val_y} val_acc = sess.run(ftrain.accuracy, feed_dict=feed) # 输出用验证机验证训练进度 print ("Epoch: {}/{}".format(e, epochs), "Iteration: {}".format(iteration), "Validation Acc: {:.4f}".format(val_acc)) # 保存模型 saver.save(sess, os.path.join(ftrain.MODEL_SAVE_PATH, ftrain.MODEL_NAME))
在控制台的输出结果中,咱们能够看到随着迭代次数的增长,损失值在不断的下降,精确性也在提升。把训练好的模型保存起来,接下来用测试集来测试模型。
transfer_test.py
#coding=utf-8 import os import numpy as np import tensorflow as tf import ftrain import vgg16 import utils #用测试集来测试模型效果 saver = tf.train.Saver() with tf.Session() as sess: saver.restore(sess,tf.train.latest_checkpoint(ftrain.MODEL_SAVE_PATH)) feed = {ftrain.inputs_: ftrain.test_x, ftrain.labels_: ftrain.test_y} test_acc = sess.run(ftrain.accuracy,feed_dict=feed) print ("Test accuracy: {:.4f}".format(test_acc))
测试代码,加载训练好的模型,而后把测试集代码喂入,计算精确度,控制台能够看到结果,个人结果达到90%以上
而后能够应用这个模型,来识别图片了,这个模型能够识别5中话,能够本身增长。
app.py
#coding=utf-8 import numpy as np import tensorflow as tf import matplotlib.pyplot as plt import vgg16 import utils import ftrain def per_picture(): #deal with picture testPicArr = [] img_path = input('Input the path and image name:') img_ready = utils.load_image(img_path) testPicArr.append(img_ready.reshape((1,224,224,3))) images = np.concatenate(testPicArr) return images labels_vecs = ['daisy','dandelion','roses','sunflower','tulips'] labels_vecs = np.array(labels_vecs) fig=plt.figure(u"Top-5 预测结果") saver = tf.train.Saver() with tf.Session() as sess: #图片预处理 images = per_picture() #输入vgg16中计算特征值 vgg = vgg16.Vgg16() input_ = tf.placeholder(tf.float32, [None, 224, 224, 3]) with tf.name_scope("content_vgg"): # 载入VGG16模型 vgg.build(input_) feed_dict = {input_: images} # 计算特征值 codes_batch = sess.run(vgg.relu6, feed_dict=feed_dict) #返回y矩阵中最大值的下标,若是是二维的加1 preValue = tf.argmax(ftrain.predicted, 1) #加载训练好的新模型 saver.restore(sess, tf.train.latest_checkpoint(ftrain.MODEL_SAVE_PATH)) #计算预测值 preValue = sess.run(preValue, feed_dict={ftrain.inputs_:codes_batch}) print ("The prediction flower is:", labels_vecs[preValue]) probability = sess.run(ftrain.predicted, feed_dict={ftrain.inputs_:codes_batch}) top5 = np.argsort(probability[0]) print ("top5:",top5) values = [] bar_label = [] for n, i in enumerate(top5): print ("n:",n) print ("i:",i) values.append(probability[0][i]) bar_label.append(labels_vecs[i]) print (i, ":", labels_vecs[i], "----", utils.percent(probability[0][i])) ax = fig.add_subplot(111) ax.bar(range(len(values)), values, tick_label=bar_label, width=0.5, fc='g') ax.set_ylabel(u'probabilityit') ax.set_title(u'Top-5') for a,b in zip(range(len(values)), values): ax.text(a, b+0.0005, utils.percent(b), ha='center', va = 'bottom', fontsize=7) plt.show()
上面代码过程是,首先要经过vgg16提取该图片的特征值,而后进行图片预处理,加载训练好的模型,输入进去,得到结果。
结果展现以下:
能够看到准确率很高,我每种图片只训练了144张,数据集有800多张。
以上代码能够在windows 下python3的环境运行。
代码和数据集参考自:https://cosx.org/2017/10/transfer-learning/
对于实现以上过程遇到的问题:
(1)首先对迁移学习的理解
对于这个简单的试验,迁移学习主要体如今使用训练好的vgg16模型(存储在vgg16.npy中)提取图片的特征值,而后再对这些特征值训练。
(2)模型的保存和加载
为了不反复的去提取图片特征值(这个很耗时间),把特征值保存在codes.npy,把图片的标签存储在labels中。还有就是存储训练模型,这里由于对TensorFlow模型不太了解,因此出一个问题
存储模型后,在另外一个文件中加载模型发现训练的模型和没训练的模型同样
缘由是由于在另外一个文件中使用的不是同一个saver,我又对saver进行了初始化,致使使用了一个崭新的模型。
(3)模型保存优化部分:
若是你不给tf.train.Saver()
传入任何参数,那么saver将处理graph中的全部变量。其中每个变量都以变量建立时传入的名称被保存。
有时候在检查点文件中明肯定义变量的名称颇有用。举个例子,你也许已经训练获得了一个模型,其中有个变量命名为"weights"
,你想把它的值恢复到一个新的变量"params"
中。
有时候仅保存和恢复模型的一部分变量颇有用。再举个例子,你也许训练获得了一个5层神经网络,如今想训练一个6层的新模型,能够将以前5层模型的参数导入到新模型的前5层中。
你能够经过给tf.train.Saver()
构造函数传入Python字典,很容易地定义须要保持的变量及对应名称:键对应使用的名称,值对应被管理的变量。