在各种神经网络结构中,前面很多层(卷积、池化)可以看做是特征提取的过程,比如提取图像的各种纹理、颜色、线条等等。最后几层(全连接层),一般将提取好的特征进行压缩,用于分类。
训练大型网络,需要很好的设备,也需要很长的时间。迁移学习,就是将一个问题上训练好的模型参数,通过简单的调整使其适用于一个新的问题,一般保留前面N层(瓶颈层)的参数,重新训练最后的全连接层,这样做,可以节省大量时间,得到的效果也不赖。
以下代码,利用Google的Inception-v3模型,对花数据集进行分类。
花数据集有以下几个目录,每个目录就是一个种类的花:
import glob
import os.path
import random
import numpy as np
import tensorflow as tf
from tensorflow.python.platform import gfile
#Icpt v3 瓶颈层节点个数和节点名字,
#可用tensor.name来获取该张量
BOTTLENECK_TENSOR_SIZE=2048
BOTTLENECK_TENSOR_NAME='pool_3/_reshape:0'
#图像输入张量的名称
JPEG_DATA_TENSOR_NAME='DecodeJpeg/contents:0'
#模型路径和模型文件名
MODEL_DIR='E:/程序/Jupyter/data'
MODEL_FILE='tensorflow_inception_graph.pb'
#因为原始数据会经过模型多次,
#瓶颈层之前的参数不会变,输出的结果也不会变,
#所以存起来,避免重复计算,节省时间
CACHE_DIR='E:/程序/Jupyter/data/tmp/bottleneck'
INPUT_DATA='E:/程序/Jupyter/data/flower_photos'
#验证、测试集占比
VALIDATION_PERCENTAGE=10
TEST_PERCENTAGE=10
LEARNING_RATE=0.01
STEPS=4000
BATCH=100
这个函数,用来把图片数据集随机分成训练集、验证集和测试集。
def create_image_lists(test_pct,val_pct):
#得到的所有图片都会存放在这个字典里,key是花的类别,
#value也是一个字典,其key是训练、验证、测试集
#其value是图片的名称
result={}
#得到根目录下的子目录
sub_dirs=[x[0] for x in os.walk(INPUT_DATA)]
#得到的第一个目录是当前目录,不需要考虑
for sub_dir in sub_dirs[1:]:
#获取当前目录下所有有效的图片文件
extensions=['jpg','jpeg','JPG','JPEG']
file_list=[]
#sub_dir是一个/data/sub_dir,用basename可以获取最后的目录名,
#也就是类别的名字
dir_name=os.path.basename(sub_dir)
for extension in extensions:
#用glob来匹配该类别下,所有jpg/jpeg/JPG/JPEG图片
file_glob=os.path.join(INPUT_DATA,dir_name,'*.'+extension)
file_list.extend(glob.glob(file_glob))
if not file_list:continue
#file_list是该类别下,所有的图片
label_name=dir_name.lower()
training_images=[]
testing_images=[]
validation_images=[]
#将该类别下的图片,随机添加到训练、测试、验证集
for file_name in file_list:
base_name=os.path.basename(file