【TensorFlow-windows】keras接口——利用tensorflow的方法加载数据

前言

之前使用tensorflowkeras的时候,都各自有一套数据读取方法,但是遇到一个问题就是,在训练的时候,GPU的利用率忽高忽低,极大可能是由于训练过程中读取每个batch数据造成的,所以又看了tensorflow官方的加载数据方法。主要是利用了tf.data.Dataset这里面的一系列操作。

国际惯例,参考博客:

tensorflow官方加载数据集方法

官方文档对应的代码images.ipynb

官方文档对应的代码tf_records.ipynb

Tensorflow中创建自己的TFRecord格式数据集

TensorFlow全新的数据读取方式:Dataset API入门教程

以tf.data优化训练数据 Google开发者大会2018

Tensorflow数据预处理之tf.data.TFRecordDataset—TFRecords详解\TFRecords图像预处理

buffer_size的含义——Dataset.map , Dataset.prefetch and Dataset.shuffle

tensorflow 数据读取总结—(直接供给数据(feeding) 从文件中以管线形式读取数据 预加载数据)

复习

先复习一下之前博客中tensorflowkeras加载数据的方法

之前采用的tensorflow加载数据方法

详细查看之前的这篇博客:

IMG_HEIGHT = 28 # 高
IMG_WIDTH = 28 # 宽
CHANNELS = 3 # 通道数
def read_images(dataset_path, batch_size):
    imagepaths, labels = list(), list()
    data = open(dataset_path, 'r').read().splitlines()
    for d in data:
        imagepaths.append(d.split(' ')[0])
        labels.append(int(d.split(' ')[1]))   

    # 转换为张量
    imagepaths = tf.convert_to_tensor(imagepaths, dtype=tf.string)
    labels = tf.convert_to_tensor(labels, dtype=tf.int32)
    # 建立TF队列,打乱数据
    image, label = tf.train.slice_input_producer([imagepaths, labels],
                                                 shuffle=True)
    # 读取数据
    image = tf.read_file(image)
    image = tf.image.decode_jpeg(image, channels=CHANNELS)

    # 将图像resize成规定大小
    image = tf.image.resize_images(image, [IMG_HEIGHT, IMG_WIDTH])

    # 手动归一化
    image = image * 1.0/127.5 - 1.0

    # 创建batch
    inputX, inputY = tf.train.batch([image, label], batch_size=batch_size,
                          capacity=batch_size * 8,
                          num_threads=4)

    return inputX, inputY

主要使用tf.train中的一系列操作

keras中自带的数据加载方法

直接看官方文档即可,我比较喜欢用下面这一系列方法从文件夹中读取数据:

train_datagen = ImageDataGenerator(
        rescale=1./255,
        shear_range=0.2,
        zoom_range=0.2,
        horizontal_flip=True)
test_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
        'data/train',
        target_size=(150, 150),
        batch_size=32,
        class_mode='binary')

validation_generator = test_datagen.flow_from_directory(
        'data/validation',
        target_size=(150, 150),
        batch_size=32,
        class_mode='binary')

只要使用flow_from_directory自动从文件夹中读取数据。

利用tf.data直接读取数据

数据集准备

下列所有实验的数据都基于tensorflow提供的flower_photos数据集,才220M左右,下载地址戳这里

我也上传到网盘了:

链接:https://pan.baidu.com/s/13esPlx-fkKlXaegJNROPyw
提取码:nv64

解压后,得到五个文件夹,每个文件夹一类花朵。

代码

首先引入必要的包:

import os 
import tensorflow as tf
import pathlib
import random
import numpy as np

读取图片数据

  • 首先找到所有图片和对应的路径:

    data_root = pathlib.Path('./dataset/flower_photos/')
    all_image_paths = list(data_root.glob('*/*'))
    all_image_paths = [str(path) for path in all_image_paths]
    random.shuffle(all_image_paths)
    image_count = len(all_image_paths)
    print('total image num:',image_count)#total image num: 3670
    
  • 数据预处理:

    class Process_img:
        def __init__(self,img_size):
            self.img_size = img_size
            
        def load_and_preprocess_image(self,img_path):
            image = tf.read_file(img_path)
            image = tf.image.decode_jpeg(image,channels=3)
            #进行各种图像处理:裁剪、缩放、旋转、亮度调整等
            image=tf.image.resize_images(image,self.img_size) #此处严格按照API文档调用,tensorflow 版本不同使用的方法不同
            image /= 255.0
            return image
    
  • 数据预处理必须使用如下流程,先from_tensor_slice转换成Dataset格式,然后使用map将数据丢到预处理函数中:

    path_ds = tf.data.Dataset.from_tensor_slices(all_image_paths)
    
    a=Process_img(img_size=[192,192])
    image_ds = path_ds.map(a.load_and_preprocess_image,num_parallel_calls=tf.contrib.data.AUTOTUNE)
    

    这里有一个小技巧就是:本人不清楚map函数如何接受预处理函数所需传递的方法,有一个方法就是将预处理参数,比如image_size也丢到Dataset里面去,但是有点麻烦,这样做from_tensor_slice里面的参数有点长,还不如初始化一个对象,存储预处理所需参数了,清晰易懂。

读取标签数据

  • 先获取标签,因为路径中文件名的上级文件夹就是标签,所以可以:

    label_names = sorted(item.name for item in data_root.glob('*/') if item.is_dir())
    print('label names:',label_names)
    #label names: ['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']
    
  • 再将标签转换为int型索引:

    #将标签转换为索引值
    label_to_index = dict((name,index) for index,name in enumerate(label_names))
    print('label corresponding index:',label_to_index)
    
  • 然后获取到所有图像对应的标签:

    all_image_labels = [label_to_index[pathlib.Path(path).parent.name] for path in all_image_paths]
    

    验证一下看看:

    print('first 10 sample path and labels:')
    for i in range(0,10):
        print('{0}\t {1}'.format(all_image_paths[i],all_image_labels[i]))
    '''
    first 10 sample path and labels:
    dataset\flower_photos\roses\5799616059_0ffda02e54.jpg	 2
    dataset\flower_photos\roses\22385375599_1faf334f5d_n.jpg	 2
    dataset\flower_photos\sunflowers\6627521877_6e43fb3c49_m.jpg	 3
    dataset\flower_photos\dandelion\3465599902_14729e2b1b_n.jpg	 1
    dataset\flower_photos\roses\4267024012_295e7141a3_n.jpg	 2
    dataset\flower_photos\dandelion\23414449869_ee849a80d4.jpg	 1
    dataset\flower_photos\tulips\4418204816_018375acd0_m.jpg	 4
    dataset\flower_photos\dandelion\9517326597_5d116a0166.jpg	 1
    dataset\flower_photos\dandelion\7197581386_8a51f1bb12_n.jpg	 1
    dataset\flower_photos\dandelion\425800274_27dba84fac_n.jpg	 1
    '''
    
  • 同样将标签也转换成Dataset格式:

    label_ds = tf.data.Dataset.from_tensor_slices(tf.cast(all_image_labels,tf.int64))
    

组合数据

因为后续需要打乱和分批,所以需要将图像与标签对应打包对应好,后面一起变换

img_label_ds = tf.data.Dataset.zip((image_ds,label_ds))

打乱、重复数据、分批,详细解释可以查看这里,我们只看如何使用:

batch_size = 32
ds = ds.cache()
ds=img_label_ds.shuffle(buffer_size=image_count)
ds=ds.repeat()
ds=ds.batch(batch_size)
ds = ds.prefetch(buffer_size=tf.contrib.data.AUTOTUNE)

【注】不加cache也行,但是官方文档说加了能提高数据喂进内存的性能。prefetch在官方文档中说的是在训练时将数据喂到batch里面。

训练

使用tf.keras里面的mobileNetV2模型微调

预处理

因为mobileNetV2要求输入数据范围在 ( − 1 , − 1 ) (-1,-1) (1,1),所以我们还要做一次预处理:

# 把数据由(0,1)转换为(-1,1)
def change_range(image,label):
    return 2*image-1,label
keras_ds=ds.map(change_range)

【注】可以发现,数据变成Dataset格式以后,各种预处理都得用map映射到处理函数。

载入模型并训练

去掉mobileNet的尾巴,是否使用imagenet的权重与训练,取决于weights是否None

mobile_net = tf.keras.applications.MobileNetV2(input_shape=(192,192,3),include_top=False,weights=None)

接个全连接做分类:

model = tf.keras.Sequential([
    mobile_net,
    tf.keras.layers.GlobalAveragePooling2D(),
    tf.keras.layers.Dense(5,activation='softmax')
])

看看网络结构

model.summary()
'''
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
mobilenetv2_1.00_192 (Model) (None, 6, 6, 1280)        2257984   
_________________________________________________________________
global_average_pooling2d (Gl (None, 1280)              0         
_________________________________________________________________
dense (Dense)                (None, 5)                 6405      
=================================================================
Total params: 2,264,389
Trainable params: 2,230,277
Non-trainable params: 34,112
_________________________________________________________________
'''

编译模型:

model.compile(optimizer=tf.train.AdamOptimizer(),
              loss=tf.keras.losses.sparse_categorical_crossentropy,
              metrics=["accuracy"])

开始训练

steps_per_epoch = int(np.ceil(len(all_image_paths)/batch_size))
model.fit(keras_ds,epochs=1000,steps_per_epoch=steps_per_epoch)
'''
Epoch 1/1000
115/115 [==============================] - 172s 1s/step - loss: 1.3721 - acc: 0.4427
Epoch 2/1000
115/115 [==============================] - 155s 1s/step - loss: 1.1061 - acc: 0.5582
Epoch 3/1000
115/115 [==============================] - 150s 1s/step - loss: 0.9562 - acc: 0.6190
Epoch 4/1000
115/115 [==============================] - 148s 1s/step - loss: 0.8750 - acc: 0.6617
Epoch 5/1000
115/115 [==============================] - 223s 2s/step - loss: 0.8136 - acc: 0.6927
Epoch 6/1000
115/115 [==============================] - 148s 1s/step - loss: 0.7368 - acc: 0.7201
Epoch 7/1000
115/115 [==============================] - 148s 1s/step - loss: 0.6718 - acc: 0.7582
Epoch 8/1000
115/115 [==============================] - 148s 1s/step - loss: 0.6206 - acc: 0.7682
Epoch 9/1000
115/115 [==============================] - 148s 1s/step - loss: 0.5699 - acc: 0.7905
Epoch 10/1000
115/115 [==============================] - 147s 1s/step - loss: 0.5368 - acc: 0.8041
Epoch 11/1000
115/115 [==============================] - 147s 1s/step - loss: 0.4938 - acc: 0.8190
Epoch 12/1000
115/115 [==============================] - 148s 1s/step - loss: 0.4456 - acc: 0.8372
Epoch 13/1000
115/115 [==============================] - 147s 1s/step - loss: 0.4257 - acc: 0.8429
Epoch 14/1000
115/115 [==============================] - 149s 1s/step - loss: 0.3856 - acc: 0.8573
.......
'''

利用tf.data转成tfrecord再载入

比较喜欢的方法就是跟caffe一样,先做数据集,训练的时候读取,tensorflow中建议的存储格式就是tfrecord

制作数据集

导入对应包:

import os 
import tensorflow as tf
import pathlib
import random
import numpy as np

获取图像数据路径:

data_root = pathlib.Path('./dataset/flower_photos/')
all_image_paths = list(data_root.glob('*/*'))
all_image_paths = [str(path) for path in all_image_paths]
random.shuffle(all_image_paths)
image_count = len(all_image_paths)
print('total image num:',image_count)

获取图像对应标签:

label_names = sorted(item.name for item in data_root.glob('*/') if item.is_dir())
label_to_index = dict((name,index) for index,name in enumerate(label_names))
all_image_labels = [label_to_index[pathlib.Path(path).parent.name] for path in all_image_paths]

把图像与标签打包:

image_labels = zip(all_image_paths,all_image_labels)

按照tensorflow的方法将图像和标签做成tfrecord格式数据集:

def _bytes_feature(value):
    """Returns a bytes_list from a string / byte."""
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))

def _float_feature(value):
    """Returns a float_list from a float / double."""
    return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))

def _int64_feature(value):
    """Returns an int64_list from a bool / enum / int / uint."""
    return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))
def image_example(image_string, label):
    feature = {
        'label': _int64_feature(label),
        'image_raw': _bytes_feature(image_string)
    }
    return tf.train.Example(features=tf.train.Features(feature=feature))
with tf.python_io.TFRecordWriter('images.tfrecords') as writer:
    for filename, label in image_labels:
        image_string = open(filename, 'rb').read()
        tf_example = image_example(image_string, label)
        writer.write(tf_example.SerializeToString())

上面需要注意的就是image_example里面的feature里面存的内容,你可以自己定义一些其它的,比如图像宽高之类的,后续读取的时候可以通过键值获取对应值,这里只存了必须的图像字节和标签。其余的函数干啥的,别问,用之就对了。

读取数据

读取必要包:

import os 
import tensorflow as tf
import pathlib
import random
import numpy as np

读取tfrecord对应的数据:

raw_image_dataset = tf.data.TFRecordDataset('images.tfrecords')

# Create a dictionary describing the features.
image_feature_description = {
    'label': tf.FixedLenFeature([], tf.int64),
    'image_raw': tf.FixedLenFeature([], tf.string),
}

def _parse_image_function(example_proto):
    # Parse the input tf.Example proto using the dictionary above.
    example = tf.parse_single_example(example_proto, image_feature_description)
    image = tf.image.resize_images(tf.image.decode_jpeg(example['image_raw'],channels=3),[192,192])
    image/=255.0
    label = example['label']
    return image,label

parsed_image_dataset = raw_image_dataset.map(_parse_image_function)
parsed_image_dataset

流程基本就是使用tf.data.TFRecordDataset载入tfrecord数据,然后取对应存储的信息,如图像与标签。还可以来一波预处理,当然还是利用map将数据丢到预处理函数中。

接着就是打乱、分批、重复

train_data = parsed_image_dataset.shuffle(buffer_size=100)
train_data = train_data.batch(8)
train_data = train_data.repeat()
train_data = train_data.prefetch(buffer_size=tf.contrib.data.AUTOTUNE)
print(train_data)

关于buffer_size的说明,戳这里

训练

跟前面没啥区别:

mobile_net = tf.keras.applications.MobileNetV2(input_shape=(192,192,3),include_top=False,weights=None)

model = tf.keras.Sequential([
    mobile_net,
    tf.keras.layers.GlobalAveragePooling2D(),
    tf.keras.layers.Dense(5,activation='softmax')
])

model.compile(optimizer=tf.train.AdamOptimizer(),
              loss=tf.keras.losses.sparse_categorical_crossentropy,
              metrics=["accuracy"])
              
model.fit(train_data,epochs=1000,steps_per_epoch=1000)
'''
Epoch 1/1000
1000/1000 [==============================] - 356s 356ms/step - loss: 1.2820 - acc: 0.4885
Epoch 2/1000
1000/1000 [==============================] - 358s 358ms/step - loss: 1.0426 - acc: 0.5928
Epoch 3/1000
1000/1000 [==============================] - 355s 355ms/step - loss: 0.9630 - acc: 0.6330
Epoch 4/1000
1000/1000 [==============================] - 354s 354ms/step - loss: 0.9110 - acc: 0.6524
Epoch 5/1000
1000/1000 [==============================] - 354s 354ms/step - loss: 0.8589 - acc: 0.6771
Epoch 6/1000
1000/1000 [==============================] - 355s 355ms/step - loss: 0.7635 - acc: 0.7152
Epoch 7/1000
1000/1000 [==============================] - 355s 355ms/step - loss: 0.6983 - acc: 0.7406
Epoch 8/1000
1000/1000 [==============================] - 354s 354ms/step - loss: 0.6482 - acc: 0.7632
Epoch 9/1000
1000/1000 [==============================] - 354s 354ms/step - loss: 0.5834 - acc: 0.7769
Epoch 10/1000
1000/1000 [==============================] - 357s 357ms/step - loss: 0.5439 - acc: 0.7995
'''

训练结果和上面直接从文件夹读取的结果差不多,说明流程没问题。

有个坑

读取tfrecord数据集的这句话:

raw_image_dataset = tf.data.TFRecordDataset('images.tfrecords')

丫的竟然不核对这个tfrecords文件是否存在,或者是否为空数据,不信你随便改个名,这句话还能运行,真的是醉了。程序model.fit会直接进入死机状态,你也不知道它是在读数据,还是崩了。

所以我们在进行下列一顿操作以后:

raw_image_dataset = tf.data.TFRecordDataset('images11.tfrecords')

# Create a dictionary describing the features.
image_feature_description = {
    'label': tf.FixedLenFeature([], tf.int64),
    'image_raw': tf.FixedLenFeature([], tf.string),
}

def _parse_image_function(example_proto):
    # Parse the input tf.Example proto using the dictionary above.
    example = tf.parse_single_example(example_proto, image_feature_description)
    image = tf.image.resize_images(tf.image.decode_jpeg(example['image_raw'],channels=3),[192,192])
    image/=255.0
    label = example['label']
    return image,label

parsed_image_dataset = raw_image_dataset.map(_parse_image_function)
parsed_image_dataset

train_data = parsed_image_dataset.shuffle(buffer_size=100)
train_data = train_data.batch(8)
train_data = train_data.repeat()
train_data = train_data.prefetch(buffer_size=tf.contrib.data.AUTOTUNE)
print(train_data)

必须得验证一下这个train_data里面是不是有数据,图片与标签是否对应。

验证方法,是迭代输出

iterator = train_data.make_one_shot_iterator()
one_element = iterator.get_next()
with tf.Session() as sess:
    try:
        while True:
            a=sess.run(one_element)
            print(a[0].shape)#(8, 192, 192, 3)
            print(a[1].shape)#(8,)
            break
    except tf.errors.OutOfRangeError:
        print('end!')

我们把数据保存在a里面,同时从ashape可以看出来,存了图片和标签,而且存储的是一个batch_size大小的数据。接下来显示一下:

label_name=['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']
print((a[0][0]).shape)
show_idx = 0
plt.imshow(a[0][show_idx])
plt.title(label_name[a[1][show_idx]])

在这里插入图片描述

有图片输出就说明没问题了。

后记

可以发现这一系列的数据读取操作是可以封装在一起的,这里先将实验验证用的ipynb放出来:

  • 直接使用tf.data遍历文件夹训练:
    链接:https://pan.baidu.com/s/1YSWLVfmfU2brnLI0uRyljg
    提取码:n2ht
  • 制作tfrecord数据集:
    链接:https://pan.baidu.com/s/1HGH66klAl5zECEznRPhV7g
    提取码:w6ss
  • 读取tfrecord数据集并训练:
    链接:https://pan.baidu.com/s/1Jyyu2u96xLkomJhKT-AgIA
    提取码:mnkj

为了方便后续使用,直接写一个现成的Python脚本,以后直接传入路径,输出可以直接训练的数据参数。

  • 直接使用tf.data遍历文件夹训练:
    链接:https://pan.baidu.com/s/1yb0EoBXzhyQEA-BO3i1Fcg
    提取码:2706

  • 制作tfrecord数据集:

    链接:https://pan.baidu.com/s/1Jw2LDKGeTrMKaItDHqe3dA
    提取码:znfy

  • 读取tfrecord数据集训练:链接:https://pan.baidu.com/s/1rRKx9tP8jrAZhzXjIZNNTQ
    提取码:sa2t

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风翼冰舟

额~~~CSDN还能打赏了

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值