1.数据集
jpg图像数据格式的MNIST数据集:(放在database1文件夹下面)
2.利用tensorflow-V2的数据集tf.data.Dataset去训练keras模型(MobileNet)
训练过程如下:
- 利用pathlib和python自带的list,将原始图像转为list(string,int)格式
- 利用tf.data.Dataset.from_tensor_slices将list数据转为tf.data.Dataset中的BatchDataset,然后用tf.data.Dataset.zip合并两个BatchDataset为一个ZipDataset
- 构建keras网络模型(MobileNet):from tensorflow_core.python.keras.applications.mobilenet import MobileNet
- 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_bmp
、decode_png
、decode_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方法:
这里说到,x可以是:
- numpy array格式(可以用将图像转为np.array([float,int])直接输入到fi方法中,如同【Tensorflow】训练keras模型+keras的数据生成器ImageDataGenerator+jpg图像数据格式的MNIST数据集+对比flow和flow_from_directory)
- tf.Tensor(应该可以用tensorflow-V1版本的tf.Tensor,有待验证)
- 一个字典dict:能够映射名字到相关的numpy array或者tf.Tensor(这个不是了解)
- tf.data.Dataset(这个就是本文的方法,将list([string,int])转tf.data.Dataset)
-
generator 或者 keras.utils.Sequence(类似于tf.keras.preprocessing.image.ImageDataGenerator,如同【Tensorflow】训练keras模型+keras的数据生成器ImageDataGenerator+jpg图像数据格式的MNIST数据集+对比flow和flow_from_directory)
同时,y也是可以不用被明确,如果x中有包含标签的话。
-
fit_generator方法:已经被放弃了!因为fit方法也可以支持generator
虽然被已经准备被放弃了,但是还是会有人说(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.html;https://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()