Tensorflow实现图像分类

Tensorflow实现图像分类

利用tensorflow做图像分类的训练,主要分为读取图像,训练批次,定义模型,训练,保存模型,模型调用几步。
首先是读取图像,利用python中os模块的listdir方法来读取图像。在这里可以把每一类的图像放到同一文件夹中,文件夹的名字可以直接作为图像的标签,最好是用数字代替,图像的名字最好也是数字,这样写代码比较方便,如图:
Tensorflow实现图像分类
Tensorflow实现图像分类
Tensorflow实现图像分类

读取图像的代码如下:
Tensorflow实现图像分类
接下来要做的就是生成训练批次了,因为tensorflow训练的数据必须是Tensor类型,下面的tf.train.slice_input_producer()函数,就是一个Tensor生成器,介绍一下这个函数。
tf.train.slice_input_producer(tensor_list, num_epochs, shuffle=True, seed=None, capacity=32, shared_name=None, name=none)
参数tensor_list:包含一个tensor列表,表中tensor的第一维度的值必须相等,即个数必须相等,有多少个图像,就应该有多少个对应的标签。
第二个参数num_epochs: 可选参数,是一个整数值,代表迭代的次数,如果设置 num_epochs=None,生成器可以无限次遍历tensor列表,如果设置为 num_epochs=N,生成器只能遍历tensor列表N次。
第三个参数shuffle: bool类型,设置是否打乱样本的顺序。一般情况下,如果shuffle=True,生成的样本顺序就被打乱了,在批处理的时候不需要再次打乱样本,使用 tf.train.batch函数就可以了;如果shuffle=False,就需要在批处理时候使用 tf.train.shuffle_batch函数打乱样本。
第四个参数seed: 可选的整数,是生成随机数的种子,在第三个参数设置为shuffle=True的情况下才有用。
第五个参数capacity:设置tensor列表的容量。
第六个参数shared_name:可选参数,如果设置一个‘shared_name’,则在不同的上下文环境(Session)中可以通过这个名字共享生成的tensor。
第七个参数name:可选,设置操作的名称。
设置好输入队列之后,读取图像数据,这里的label暂不处理,然后利用tf.read_file读取图像,读取后的图像是string类型的Tensor,所以接下来需要用tf.image.decode_jpeg进行解码,当然这里是jpg类型的图片,如果是其他类型的图片,还有其他的解码函数,在这里的channels = 3表示的是返回的是三通道的RGB图像,函数的返回值是uint8类型的Tensor。
tf.image.resize_image_with_crop_or_pad(image, target_height, target_width)这个函数的作用是将图像剪切为需要的大小,并且保持横纵比不变,这是因为tensorflow在训练时要求所有图像的大小都是一样的,为了防止图像的大小影响训练,所有必须要处理图像的大小。处理之后,对图像进行标准化。然后就是tf.train.batch函数,这个函数的功能是利用一个tensor的列表或字典来获取一个batch数据,
tf.train.batch(
tensors, # 一个列表或字典的tensor用来进行入队
batch_size, #设置每次从队列中获取出队数据的数量
num_threads=1, #用来控制入队tensors线程的数量,如果num_threads大于1,则batch操作将是非确定性的,输出的batch可能会乱序
capacity=32, #一个整数,用来设置队列中元素的最大数量
enqueue_many=False, #在tensors中的tensor是否是单个样本
shapes=None, #可选,每个样本的shape,默认是tensors的shape
dynamic_pad=False, #Boolean值.允许输入变量的shape,出队后会自动填补维度,来保持与batch内的shapes相同
allow_smaller_final_batch=False, #可选,Boolean值,如果为True队列中的样本数量小于batch_size时,出队的数量会以最终遗留下来的样本进行出队,如果为Flalse,小于batch_size的样本不会做出队处理
shared_name=None, #可选,通过设置该参数,可以对多个会话共享队列
name=None #可选,操作的名字
)
最后,还要将数据的数据类型转换为可用于训练的类型
Tensorflow实现图像分类

接下来就要定义训练是的卷积层以及每层的参数,首先要知道,cnn的包括卷积层,池化层和正则化层,卷积层的方法tf.nn.conv2d函数
tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, name=None)

input:指卷积需要输入的参数,具有这样的shape[batch, in_height, in_width, in_channels],分别是[batch张图片, 每张图片高度为in_height, 每张图片宽度为in_width, 图像通道为in_channels]。
filter:指用来做卷积的滤波器,当然滤波器也需要有相应参数,滤波器的shape为[filter_height, filter_width, in_channels, out_channels],分别对应[滤波器高度, 滤波器宽度, 接受图像的通道数, 卷积后通道数],其中第三个参数 in_channels需要与input中的第四个参数 in_channels一致,out_channels第一看的话有些不好理解,如rgb输入三通道图,我们的滤波器的out_channels设为1的话,就是三通道对应值相加,最后输出一个卷积核。
strides:代表步长,其值可以直接默认一个数,也可以是一个四维数如[1,2,1,1],则其意思是水平方向卷积步长为第二个参数2,垂直方向步长为1.其中第一和第四个参数我还不是很明白,请大佬指点,貌似和通道有关系。
padding:代表填充方式,参数只有两种,SAME和VALID,SAME比VALID的填充方式多了一列,比如一个33图像用22的滤波器进行卷积,当步长设为2的时候,会缺少一列,则进行第二次卷积的时候,VALID发现余下的窗口不足2*2会直接把第三列去掉,SAME则会填充一列,填充值为0。
use_cudnn_on_gpu:bool类型,是否使用cudnn加速,默认为true。大概意思是是否使用gpu加速,还没搞太懂。
name:给返回的tensor命名。给输出feature map起名字。

池化函数:tf.nn.max_pool(value, ksize, strides, padding, name=None)

value:池化的输入,一般池化层接在卷积层的后面,所以输出通常为feature map。feature map依旧是[batch, in_height, in_width, in_channels]这样的参数。
ksize:池化窗口的大小,参数为四维向量,通常取[1, height, width, 1],因为我们不想在batch和channels上做池化,所以这两个维度设为了1。ps:估计面tf.nn.conv2d中stries的四个取值也有相同的意思。
stries:步长,同样是一个四维向量。
padding:填充方式同样只有两种不重复了。

在这里要提一下加权重和偏置这个问题,可以理解为原本的训练是y = x,这里权重是w,偏置是b的话,那么训练的结果就变成了y = wx + b,这里需要自己设置权重和偏置的值。这里的权重可能不好理解,其实就是用来生成卷积核的shape。
tf.Variable(tf.random_normal([3,3,3,16],stddev = 0.01))
这里的卷积核就是由这个函数生成的,其中的shape中的几个数据的含义为,卷积核的高度,滤波器的宽度,图像通道数,滤波器个数。
Tensorflow实现图像分类
Tensorflow实现图像分类
如图,三次卷积池化之后,用**函数relu**。然后定义损失函数以及评价分类准确率的量,训练时,需要loss值减小,准确率增加,这样的训练才是收敛的。
损失函数
Tensorflow实现图像分类
评估量
Tensorflow实现图像分类
训练
Tensorflow实现图像分类

最后,还有附上主函数代码

Tensorflow实现图像分类