【Tensorflow】训练keras模型+tensorflow-V2的数据集tf.data.Dataset+jpg图像数据格式的MNIST数据集+MobileNet

1.数据集

jpg图像数据格式的MNIST数据集:(放在database1文件夹下面)

 

2.利用tensorflow-V2的数据集tf.data.Dataset去训练keras模型(MobileNet)

训练过程如下:

  1. 利用pathlib和python自带的list,将原始图像转为list(string,int)格式
  2. 利用tf.data.Dataset.from_tensor_slices将list数据转为tf.data.Dataset中的BatchDataset,然后用tf.data.Dataset.zip合并两个BatchDataset为一个ZipDataset
  3. 构建keras网络模型(MobileNet):from tensorflow_core.python.keras.applications.mobilenet import MobileNet
  4. keras训练:model.fit或者model.fit_generator

1)利用pathlib和python自带的list,将原始图像转为list(string,int)格式

原始数据是这样的

如果直接把图像数据读取进来会占用非常多的内存,所以采用pathlib加载文件绝对路径和对应的label,并保存为list

    data_path = pathlib.Path(file_dir)
    print(type(data_path))#<class 'pathlib.WindowsPath'>
    all_image_paths = list(data_path.glob('*/*'))  
    print(type(data_path.glob('*/*')))#<class 'generator'>
    # print(all_image_paths)
    
    # all_image_paths = [str(path) for path in all_image_paths]  # 所有图片的相对路径的列表
    all_image_paths = [os.path.abspath(path) for path in all_image_paths]  # 所有图片的绝对路径的列表
    random.shuffle(all_image_paths)  # 打散
    # print(all_image_paths[0:3])
    
    image_count = len(all_image_paths)
    print('image_count: ',image_count)
    
    
    label_names = sorted(item.name for item in data_path.glob('*/') if item.is_dir())
    # print('label_names: ',label_names)
    label_to_index = dict((name, index) for index, name in enumerate(label_names))
    # print('label_to_index: ',label_to_index)
    all_image_labels = [label_to_index[pathlib.Path(path).parent.name] for path in all_image_paths]

这里面的pathlib对象的成员方法glob结合dict,可以将文件名("0","1"等等)提取出来作为lebel_names,并且建立了索引label_to_index(类名对应索引编号,顺序是从小到大排序),

对于每个jpg文件的parent.name就是其对应类名,因此在label_to_index能够找到对应类名的索引编号。

 

2)利用tf.data.Dataset.from_tensor_slices将list数据转为tf.data.Dataset中的BatchDataset,然后用tf.data.Dataset.zip合并两个BatchDataset为一个ZipDataset

tensorflow-V2版本放弃了tf.train系列数据集,而是改用tf.data.Dataset来存放数据集(比如BatchDataset和ZipDataset)

https://tensorflow.google.cn/versions/r2.1/api_docs/python/tf/data/Dataset

我们可以利用tf.data.Dataset.from_tensor_slices()将前面得到的 list(string,int) 转为tf.data.Dataset(比如,BatchDataset和ZipDataset)

train_images = tf.data.Dataset.from_tensor_slices(train_image_list).map(train_preprocess_image).batch(batch_size)
train_labels = tf.data.Dataset.from_tensor_slices(train_label_list).map(preprocess_label).batch(batch_size)
train_ds=tf.data.Dataset.zip((train_images, train_labels)).repeat()
print(train_ds)#<ZipDataset shapes: ((28, 28, 3), ()), types: (tf.float32, tf.int32)>
print(type(train_ds))#<class 'tensorflow.python.data.ops.dataset_ops.ZipDataset'>
print(train_images)#<BatchDataset shapes: (None, 100, 100, 3), types: tf.float32>
print(type(train_images))#<class 'tensorflow.python.data.ops.dataset_ops.BatchDataset'>

tensorflow官网中关于tf.data.Dataset及其相关方法:https://tensorflow.google.cn/versions/r2.1/api_docs/python/tf/data/Dataset

其中,tf.data.Dataset有很多成员方法:

  • map方法:可以在流程中对图像做预处理,但是好像是不太能够通过map传递参数(比如bool)

不过我们可以在train_preprocess_image函数中对图像进行预处理操作或者数据增强操作,比如

def train_preprocess_image(path):
    image = tf.io.read_file(path)  # 读取图片
    image = tf.image.decode_jpeg(image, channels=3)
    # image = tf.image.grayscale_to_rgb(image)
    
    image = tf.image.resize(image, [im_w, im_h])  # 原始图片大小为(100, 100, 3),重设为(192, 192)
    
    #随机调整图像的亮度
    image = tf.image.random_brightness(image,max_delta=30)
    
    #随机设置图片的对比度
    image = tf.image.random_contrast(image,lower=0.2,upper=1.8)
    
    #随机设置图片的色度
    image = tf.image.random_hue(image,max_delta=0.3)
    
    #随机设置图片的饱和度
    image = tf.image.random_saturation(image,lower=0.2,upper=1.8)
    
    image = tf.cast(image, dtype=tf.float32) / 255.0
    return image

train_preprocess_image这里面用到了tf.io.read_file读取文件数据,

然后用到了tf.image类的各种图像处理方法,可以在官网找到具体使用规则:https://tensorflow.google.cn/versions/r2.1/api_docs/python/tf/image

比如,文件格式转换为图像格式jpeg:tf.image.decode_jpeg(https://tensorflow.google.cn/versions/r2.1/api_docs/python/tf/io/decode_jpeg),

(这里说明decode_jpegh函数还有ratio参数可以直接对图像进行倍数缩小。)

当然,还有其他的图像格式转换方法,比如 decode_bmpdecode_pngdecode_and_crop_jpeg

训练和测试的图像数据最好都要归一化处理(tf.cast(image, dtype=tf.float32) / 255.0),这样训练的效率会比较高。

 

  • batch方法:定义了数据集的单次生成的批量大小

keras.model.fit训练的时候必须要有,因为要求输入是4维变量,不然会报错提示尺寸对不上

 

  • repeat方法:重复数据

我们在官网github上查到repeat的解释:https://github.com/tensorflow/tensorflow/blob/v2.1.0/tensorflow/python/data/ops/dataset_ops.py#L1081-L1100

如果没有repeat就不会重复数据;如果repeat()不指定count大小,则默认数据可以重复;如果指定了count大小,则数据重复count次

  def repeat(self, count=None):
    """Repeats this dataset so each original value is seen `count` times.
    >>> dataset = tf.data.Dataset.from_tensor_slices([1, 2, 3])
    >>> dataset = dataset.repeat(3)
    >>> list(dataset.as_numpy_iterator())
    [1, 2, 3, 1, 2, 3, 1, 2, 3]
    NOTE: If this dataset is a function of global state (e.g. a random number
    generator), then different repetitions may produce different elements.
    Args:
      count: (Optional.) A `tf.int64` scalar `tf.Tensor`, representing the
        number of times the dataset should be repeated. The default behavior (if
        `count` is `None` or `-1`) is for the dataset be repeated indefinitely.
    Returns:
      Dataset: A `Dataset`.
    """
    return RepeatDataset(self, count)

 

  • shuffle方法:可以打乱数据集

但是因为我们在生成 list(string,int)数据集的时候就已经使用random.shuffle对list进行打乱,

因此就不需要使用,当然也可以对ZipDataset再打乱一次,具体使用规则看https://github.com/tensorflow/tensorflow/blob/v2.1.0/tensorflow/python/data/ops/dataset_ops.py#L1135-L1193

 

 

3)构建keras网络模型(MobileNet):from tensorflow_core.python.keras.applications.mobilenet import MobileNet

这一步就很简单了,调用语句如下就好:D:\Users\Leon_PC\Anaconda3\envs\tf_nightly\lib\site-packages\tensorflow_core\python\keras\applications\mobilenet.py

from tensorflow_core.python.keras.applications.mobilenet import MobileNet
model=MobileNet(input_shape=(im_w, im_h,3),weights=None,include_top=True,classes=classes)

这里有两点要注意一下:

1.如果你虚拟环境中也有keras,那么你可能会在D:\Users\Leon_PC\Anaconda3\envs\tf_nightly\lib\site-packages\tensorflow_core\python\keras\applications\mobilenet.py找到模型,然后用下面的语句import

from keras_applications.mobilenet import MobileNet

这样是会报错的,因为你没有给这个模型配置backend,而且好像也没有直接传参的方法,所以还是老老实实地用上面的语句就好了,

而且上面的语句其实也是调用了D:\Users\Leon_PC\Anaconda3\envs\tf_nightly\lib\site-packages\keras_applications\mobilenet.py,但是是把backend配置好了,所以能用!

2.weight=‘imagenet’就是可以选择imagenet训练过的网络模型参数,include_top=True就是可以使用我们自己设计顶层的输出类别数量(classes=自己定义的数量),

如果weight=‘imagenet’同时include_top=True,那么classes就必须等于1000,这是源码的规定:(D:\Users\Leon_PC\Anaconda3\envs\tf_nightly\lib\site-packages\keras_applications\mobilenet.py 161行)

    if weights == 'imagenet' and include_top and classes != 1000:
        raise ValueError('If using `weights` as `"imagenet"` with `include_top` '
                         'as true, `classes` should be 1000')

可是我如果只要网络模型的一部分的权重呢?这个mobilenet就不行了,只能再找其他的网络模型吧,

里面的网络模型不是很多,肯定还是不够的

可以去tensorflow官方github:https://github.com/tensorflow/tensorflow/tree/master/tensorflow/python/keras/applications,找一下

还有一些benchmarks方法可以参考一下:https://github.com/tensorflow/tensorflow/tree/master/tensorflow/python/keras/benchmarks

 

4)keras训练:model.fit或者model.fit_generator

虽然fit_generator或者fit是keras.Model的方法,但是不要去keras官网查api(https://keras.io/api/ 或者 https://keras.io/zh/),因为体验不是很好,而且不能确定keras的版本,信息也给的不全面!

直接去tensorflow的官网查:keras.Model  https://tensorflow.google.cn/versions/r2.1/api_docs/python/tf/keras/Model

 

  • fit方法:

https://github.com/tensorflow/tensorflow/blob/v2.1.0/tensorflow/python/keras/engine/training.py#L596-L819

这里说到,x可以是:

同时,y也是可以不用被明确,如果x中有包含标签的话。

 

 

  • fit_generator方法:已经被放弃了!因为fit方法也可以支持generator

https://github.com/tensorflow/tensorflow/blob/v2.1.0/tensorflow/python/keras/engine/training.py#L1268-L1306

虽然被已经准备被放弃了,但是还是会有人说(https://www.jb51.net/article/188905.htm

如果我们直接用keras的fit函数来训练模型的话,是需要传入全部训练数据,但是好在提供了fit_generator,可以分批次的读取数据,节省了我们的内存,我们唯一要做的就是实现一个生成器(generator)。

这明显就不对了哈,官网都说了fit可以兼容fit_generator,所以就不再需要继续使用这个endpoint。(感觉endpoint这个词有点。。。。)

 

而且,tensorflow运行时的临时文件是默认放在c盘中的,c盘红红的看着真难受!

不过经过对比,不管是fit或者是fit_generator都会占用我c盘大约5G的空间,应该还是把数据全部加载进来了。。。

一开始我还以为是放在appdata或者是某个cache中,但是找了一下好像都不是,最后确定是放在了挂载在c盘的虚拟内存中,但是我的c盘空间这么小还要挂虚拟内存(8G左右的样子),后面直接把虚拟内存挂到D盘,世界终于清静了!

虚拟内存转移到D盘可以参考这两篇博文:http://www.dngswin10.com/newdngs/47225.htmlhttps://zhidao.baidu.com/question/2269430125151142228.html

 

后面可以考虑一下这样的keras写法:https://blog.csdn.net/feifeiyechuan/article/details/109777137

就是用gc回收变量,用tf.keras.backend.clear_session()和tf.compat.v1.reset_default_graph()清理session和graph,

感觉效果有限,而且不太方便用到一些callbacks函数,当然这样肯定更加灵活~

import gc
import tensorflow as tf
epochs = 100
for  i in range(epochs):
	model.fit([encoder_input_data, decoder_input_data], decoder_target_data,
           batch_size=batch_size,
           epochs=1,
           validation_split=0.3)
    model.save('models.h5')
    gc.collect()
    tf.keras.backend.clear_session()
    tf.compat.v1.reset_default_graph()

 

 

完整代码如下:

input_mnist_numpy2tfv2data.py

# -*- coding: utf-8 -*-
"""
Created on Sat Jan 30 15:08:38 2021

@author: Leon_PC
"""
import random
import pathlib
import os
def input_data_list(file_dir,train_ratio=4/5):
    # data_path = pathlib.Path('./database1/')
    data_path = pathlib.Path(file_dir)
    print(type(data_path))#<class 'pathlib.WindowsPath'>
    all_image_paths = list(data_path.glob('*/*'))  
    print(type(data_path.glob('*/*')))#<class 'generator'>
    # print(all_image_paths)
    
    # all_image_paths = [str(path) for path in all_image_paths]  # 所有图片的相对路径的列表
    all_image_paths = [os.path.abspath(path) for path in all_image_paths]  # 所有图片的绝对路径的列表
    random.shuffle(all_image_paths)  # 打散
    # print(all_image_paths[0:3])
    
    image_count = len(all_image_paths)
    print('image_count: ',image_count)
    
    
    label_names = sorted(item.name for item in data_path.glob('*/') if item.is_dir())
    # print('label_names: ',label_names)
    label_to_index = dict((name, index) for index, name in enumerate(label_names))
    # print('label_to_index: ',label_to_index)
    all_image_labels = [label_to_index[pathlib.Path(path).parent.name] for path in all_image_paths]
    
    # classes=len(label_names)
    print(label_names)
    
    # train_ratio=4/5
    nums_for_training=int(len(all_image_paths)*train_ratio)
    
    train_image_list = list(all_image_paths[0:nums_for_training])
    train_label_list = list(all_image_labels[0:nums_for_training])
    test_image_list = list(all_image_paths[nums_for_training:len(all_image_paths)])
    test_label_list = list(all_image_labels[nums_for_training:len(all_image_paths)])  
    return train_image_list,train_label_list,test_image_list,test_label_list,label_names



if __name__=='__main__':
    input_data('../database1/')

 

train_keras_from_tfv2data.py 

# -*- coding: utf-8 -*-
"""
Created on Fri Jan 29 20:36:16 2021

@author: Leon_PC
"""
import tensorflow as tf
import random
import pathlib
from tensorflow import keras
import os
import numpy as np
from my_input_data import input_data_list




im_w=128
im_h=128
# im_channels=3

train_image_list,train_label_list,test_image_list,test_label_list,label_names=input_data_list('../database1/')

classes=len(label_names)
nums_for_training=len(train_image_list)
batch_size=32
steps_per_epoch=int(nums_for_training/batch_size)
epochs=2
print(nums_for_training)
print(steps_per_epoch)


def train_preprocess_image(path):
    image = tf.io.read_file(path)  # 读取图片
    image = tf.image.decode_jpeg(image, channels=3)
    # image = tf.image.grayscale_to_rgb(image)
    
    image = tf.image.resize(image, [im_w, im_h])  # 原始图片大小为(100, 100, 3),重设为(192, 192)
    
    #随机调整图像的亮度
    image = tf.image.random_brightness(image,max_delta=30)
    
    #随机设置图片的对比度
    image = tf.image.random_contrast(image,lower=0.2,upper=1.8)
    
    #随机设置图片的色度
    image = tf.image.random_hue(image,max_delta=0.3)
    
    #随机设置图片的饱和度
    image = tf.image.random_saturation(image,lower=0.2,upper=1.8)
    
    image = tf.cast(image, dtype=tf.float32) / 255.0
    return image

def test_preprocess_image(path):
    image = tf.io.read_file(path)  # 读取图片
    image = tf.image.decode_jpeg(image, channels=3)
    # image = tf.image.grayscale_to_rgb(image)
    image = tf.image.resize(image, [im_w, im_h])  # 原始图片大小为(100, 100, 3),重设为(192, 192)
    image = tf.cast(image, dtype=tf.float32) / 255.0
    return image

def preprocess_label(label):
    label = tf.cast(label, dtype=tf.int32)
    label = tf.one_hot(label, depth=classes)
    return label



train_images = tf.data.Dataset.from_tensor_slices(train_image_list).map(train_preprocess_image).batch(batch_size)
train_labels = tf.data.Dataset.from_tensor_slices(train_label_list).map(preprocess_label).batch(batch_size)
train_ds=tf.data.Dataset.zip((train_images, train_labels)).repeat()
print(train_ds)#<ZipDataset shapes: ((28, 28, 3), ()), types: (tf.float32, tf.int32)>
print(type(train_ds))#<class 'tensorflow.python.data.ops.dataset_ops.ZipDataset'>
print(train_images)#<BatchDataset shapes: (None, 100, 100, 3), types: tf.float32>
print(type(train_images))#<class 'tensorflow.python.data.ops.dataset_ops.BatchDataset'>

test_images = tf.data.Dataset.from_tensor_slices(test_image_list).map(test_preprocess_image).batch(batch_size)
test_labels = tf.data.Dataset.from_tensor_slices(test_label_list).map(preprocess_label).batch(batch_size)
test_ds=tf.data.Dataset.zip((test_images, test_labels))
print(test_ds)


# # for index, name in enumerate(test_ds):
#     # print(type(name),name.shape, index)
#     # print(name,index)
    

from tensorflow_core.python.keras.applications.mobilenet import MobileNet
model=MobileNet(input_shape=(im_w, im_h,3),weights=None,include_top=True,classes=classes)


model.compile(loss='categorical_crossentropy',optimizer='sgd',metrics=['accuracy'])
model.fit(train_ds, steps_per_epoch=steps_per_epoch,epochs=epochs, verbose=1)
# model.fit_generator(train_ds, steps_per_epoch=steps_per_epoch,epochs=epochs, verbose=1)
# # model.fit(train_ds, epochs=epochs)

# cost = model.test_on_batch(test_ds)
cost = model.evaluate(test_ds)
print('test loss: ', cost)

# from keras import backend as K
 
# K.clear_session()
# tf.compat.v1.reset_default_graph()


 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值