这篇文章的标题纠结了半天,因为在做深度学习的工作时,数据是非常重要的,第一步的工作也是准备数据,这中间我们需要做很多工作包括数据输入、数据预处理、数据增强等,我个人把这一步的工作命名为数据准备,当然也可以有其他命名。
前言:在我们做图像识别的问题时,碰到的数据集可能有多种多样的形式,常见的文件如jpg、png等还好,它可以和tensorflow框架无缝对接,但是如果图像文件是tif等tensorflow不支持解码的文件格式,这就给程序的编写带来一定麻烦。
环境准备
系统:Windows10/Linux系统
软件:Python3、TensorFlow框架、和常用的Python库,数据准备阶段主要是os、cv2、numpy、skimage、csv等。
处理流程
不同的数据集有着不同的程序设计流程,但大致都遵循以下处理流程:
- 文件名获取(主要是获取文件地址集)
- 读取文件数据(采用Opencv3或者skimage库读取图像文件,返回ndarray格式,或者TensorFlow读取图像,返回Tensor格式)
- 打乱数据(随机打乱数据)
- 划分batch(根据硬件规格,可设置相应较大的batch)
程序设计
我这里以Histopathologic Cancer Detection比赛为例,编写数据准备的程序,这个程序,我写了两个版本,前期的获取文件名函数都差不多,后面的打乱数据和划分batch部分,一个版本是采用numpy+python自带的功能完成的,后面一个版本是用TensorFlow的数据集Dataset框架完成打乱图像数据和划分batch的功能(也可采用队列形式)。
这部分,我描述的不是很好,有经验的下面的程序大致就能理解了。
数据集形式如下图所示:
第一个版本程序
纯python编写,借助了cv2、os、numpy、csv等库
数据准备程序被我命名为input_data.py,里面主要是两个函数:
- get_files(获取文件名函数,从训练集标签获取)
- get_batch(读取图像数据,划分batch)
get_files函数如下:
# ----------------------------获取文件名函数,从训练集标签获取----------------------------------- def get_files(label_file): file_list = [] label_list = [] image_list = [] i = 0 with open(label_file,'r') as train: # 返回一个生成器对象,reader是可迭代的 reader = csv.reader(train) for row in reader: i += 1 if i > 1: # print(row) file_path = os.path.join(train_dir,row[0]+'.tif') # 获取图像文件路径 file_list.append(file_path) label_list.append(row[1]) # image = cv2.imread(file_path) # ndarray # print(img) # image = cv2.resize(image,(IMG_W,IMG_H,IMG_C)) # ndarray # image_list.append(image) # print(str(file_path)) num_files = len(file_list) # 获取图像名列表长度 num_labels = len(label_list) if num_files == num_labels: print('num of files identify to num of labels and the num is %d' % num_files) # 打乱文件顺序 temp = np.array([file_list,label_list]) # ndarray,把图像名序列和标签名序列做成一个二维数组 temp = temp.transpose() # ndarray,对二维数组进行转置操作,(2,220025)-->(220026,5) np.random.shuffle(temp) # 打乱数组各行顺序 file_list = list(temp[:,0]) # list,获取打乱顺序后的图像名序列 label_list = list(temp[:,1]) # list,获取打乱后的标签序列 label_list = [int(i) for i in label_list] # 把字符标签转化为整数型标签 return i,file_list,label_list # 返回文件名和文件标签列表list
get_batch()函数如下:
#------------------------------读取图像数据,划分batch----------------------------------------- # 生成相同大小的批次 def get_batch(files,labels,start,batch_size): images = [] start = (start+batch_size) % len(labels) end = start + batch_size # start = start+batch_size files = files[start:end] for i,file in enumerate(files): #print(file) image = cv2.imread(file) image = cv2.resize(image,(IMG_W,IMG_H)) images.append(image) labels = labels[start:end] # 打乱一个batch的图像数据和对应标签 temp = np.array([images,labels]) # ndarray,把图像名序列和标签名序列做成一个二维数组 temp = temp.transpose() # ndarray,对二维数组进行转置操作,(2,220025)-->(220026,5) np.random.shuffle(temp) # 打乱数组各行顺序 images = list(temp[:,0]) # list,获取打乱顺序后的图像名序列 labels = list(temp[:,1]) # list,获取打乱后的标签序列 labels = [int(i) for i in labels] # 把字符标签转化为整数型标签 # 返回一个batch的images和labels return np.array(images),np.array(labels)
全部程序如下:
# coding:utf-8 # filename:input_data.py # Environment:windows10,python3,numpy,TensorFlow1.9,glob,matplotlib,time # Function:负责实现读取数据,生成批次(batch) import os import numpy as np from skimage import io,transform import csv import cv2 IMG_H = 96 # 图像高度 IMG_W = 96 # 图像宽度 IMG_C = 3 # 图像通道 batch_size = 20 # 批次大小 label_file = 'F:/Software/Python_Project/Histopathologic-Cancer-Detection2.0/train_labels.csv' train_dir = 'F:/Software/Python_Project/Histopathologic-Cancer-Detection/train/' # ----------------------------获取文件名函数,从训练集标签获取----------------------------------- def get_files(label_file): file_list = [] label_list = [] image_list = [] i = 0 with open(label_file,'r') as train: # 返回一个生成器对象,reader是可迭代的 reader = csv.reader(train) for row in reader: i += 1 if i > 1: # print(row) file_path = os.path.join(train_dir,row[0]+'.tif') # 获取图像文件路径 file_list.append(file_path) label_list.append(row[1]) # image = cv2.imread(file_path) # ndarray # print(img) # image = cv2.resize(image,(IMG_W,IMG_H,IMG_C)) # ndarray # image_list.append(image) # print(str(file_path)) num_files = len(file_list) # 获取图像名列表长度 num_labels = len(label_list) if num_files == num_labels: print('num of files identify to num of labels and the num is %d' % num_files) # 打乱文件顺序 temp = np.array([file_list,label_list]) # ndarray,把图像名序列和标签名序列做成一个二维数组 temp = temp.transpose() # ndarray,对二维数组进行转置操作,(2,220025)-->(220026,5) np.random.shuffle(temp) # 打乱数组各行顺序 file_list = list(temp[:,0]) # list,获取打乱顺序后的图像名序列 label_list = list(temp[:,1]) # list,获取打乱后的标签序列 label_list = [int(i) for i in label_list] # 把字符标签转化为整数型标签 return i,file_list,label_list # 返回文件名和文件标签列表list #------------------------------读取图像数据,划分batch----------------------------------------- # 生成相同大小的批次 def get_batch(files,labels,start,batch_size): images = [] start = (start+batch_size) % len(labels) end = start + batch_size # start = start+batch_size files = files[start:end] for i,file in enumerate(files): # print(file) # 仅供测试程序时用,迭代训练模型时建议注释掉 image = cv2.imread(file) image = cv2.resize(image,(IMG_W,IMG_H)) images.append(image) labels = labels[start:end] # 打乱一个batch的图像数据和对应标签 temp = np.array([images,labels]) # ndarray,把图像名序列和标签名序列做成一个二维数组 temp = temp.transpose() # ndarray,对二维数组进行转置操作,(2,220025)-->(220026,5) np.random.shuffle(temp) # 打乱数组各行顺序 images = list(temp[:,0]) # list,获取打乱顺序后的图像名序列 labels = list(temp[:,1]) # list,获取打乱后的标签序列 labels = [int(i) for i in labels] # 把字符标签转化为整数型标签 # 返回一个batch的images和labels return np.array(images),np.array(labels) #-----------------------------测试:迭代输出一个batch数据---------------------------- steps = 10 #循环迭代输出batch数据 for i in range(steps): data,label = get_batch(file_list, label_list, i, batch_size) print(data,label) print(type(data)) print(label.dtype) print(len(data),len(label))
本程序获取文件名(文件地址集)函数,不需要列出通过训练目录下的文件,而是借助训练集标签,直接构造文件路径,实测这样速度快了很多,如果是通过os.listdir()+os.path.join的方式获取文件路径,还需要和训练集标签去一一对应相应文件名和标签,这样速度非常慢!训练集少还好点,训练集大的话,光获取文件名路径这个部分,就花很多时间了。
为了加快程序的速度,本程序的读取图像数据是按照一个批次来读取的,先随机打乱文件名数据之后,然后划分文件名batch,再开始读取图像数据,这样就得到了一个batch的图像数据,shape为(batch,img_w,img_h,img_c)。一个batch一个batch的去读取图像,比一次性读取所有图像数据再划分batch要快很多。
输出结果
无图无真相,我这里设置batch_size的是20。输出data的shape为(20,96,96,3),label的shape为(20,)
第二个版本程序
这个版本使用的是TensorFlow的Dataset框架读取处理数据,我在网上没找到使用的程序,在参考了些资料和查阅api之后,自己写了这个实用的程序,但是在训练的时候,出现了训练到1000左右epoch时,程序突然报错了,这让我很懵逼,目前没有找到问题。
其实正常测试读取训练集图像是没问题,主要是在训练模型的时候出了问题,还不清楚是模型训练程序还是数据准备程序的问题,所以这个版本程序仅供参考。
纯python编写,借助了cv2、os、numpy、csv、TensorFlow等库。
数据准备程序被我命名为input_data.py,里面主要是两个函数:
- get_files(获取文件名函数,从训练集标签获取)
- read_batch_image(读取一个batch图像,返回图像和标签数据ndarray)
- get_batch(生成一个batch的文件名地址集和标签)
程序如下:
# coding:utf-8 # filename:input_data.py # Environment:windows10,python3,numpy,TensorFlow1.9,glob,matplotlib,time # Function:负责实现读取数据,生成批次(batch) from skimage import io,transform import tensorflow as tf import numpy as np import os import matplotlib.pyplot as plt import csv import cv2 # 本地电脑训练数据读取对应地址 train_dir = "F:/Software/Python_Project/Histopathologic-Cancer-Detection/train/" label_file = 'F:/Software/Python_Project/Histopathologic-Cancer-Detection/train_labels.csv' # 云服务器训练数据读取对应地址 # train_dir = '/data/Histopathologic-Cancer-Detection/train/' # label_file = '/data/Histopathologic-Cancer-Detection/train_labels.csv' #-------------------------------------图像数据及标签获取并打乱---------------------------------- # 获取数据集图像文件路径和标签 def get_files(file_dir,label_file): # file_dir: 文件夹路径 # label_file: 训练数据标签文件 # return: 乱序后的图片和标签 # 定义存放图像数据和标签列表 image_list = [] label_list = [] len_file = len(os.listdir(file_dir)) #统计指定文件夹中图像文件个数 len_label = len(open(label_file).readlines())-1 # 统计label_file文件有多少行 if len_file == len_label: print('num of images identify to num of labels.') print('The number of images is %d.' % len_file) # csv_file = open(label_file,'r') train_files = [file_dir+i for i in os.listdir(file_dir)] # use this for full datas train_files = train_files[0:300] # 循环列出文件夹下的所有图像文件,获取文件路径和文件标签列表 # for file in os.listdir(file_dir): for i,file in enumerate(train_files): file_name = file.split('/')[-1].split(sep='.')[0] # i += 1 print(i,'files load success','filename is',file_name) # print(file,'load success') # image_list.append(file_dir+file) image_list.append(file) with open(label_file,'r') as train: # 返回一个生成器对象,reader是可迭代的 reader = csv.reader(train) for row in reader: if row[0] == file_name: label_list.append(row[1]) print('The label is',row[1]) print('There are %d images\nThere are %d labels' % (len(image_list),len(label_list))) # 打乱文件顺序 temp = np.array([image_list,label_list]) # ndarray,把图像名序列和标签名序列做成一个二维数组 temp = temp.transpose() # ndarray,对二维数组进行转置操作,(2,220025)-->(220026,5) np.random.shuffle(temp) # 打乱数组各行顺序 image_list = list(temp[:,0]) # list,获取打乱顺序后的图像名序列 label_list = list(temp[:,1]) # list,获取打乱后的标签序列 label_list = [int(i) for i in label_list] # 把字符标签转化为整数型标签 return image_list,label_list # list,shape:(220025,) (220025,) #-----------------------读取一个batch图像,返回图像和标签数据ndarray------------------------------------ # 读取image file name,裁剪图像尺寸后,返回ndarray格式的数据(3*D) def read_batch_image(file_batch,label_batch): # file_batch = file_batch.eval() # label_batch = label_batch.eval() image_batch = [] # batch_label = [] for i,file in enumerate(file_batch): file = str(file) file = file.strip('b') # print(file) image = io.imread(eval(file)) image = transform.resize(image, (96,96)) image_batch.append(image) # image = tf.image.resize_images(image, [96, 96]) # 返回一个batch图像数据,shape:(batch_size,96,96,3) return np.array(image_batch),np.array(label_batch) #----------------------------生成一个batch的文件名地址集和标签----------------------------------- # 生成相同大小的批次 def get_batch(filenames, labels, batch_size): # 此时dataset中的一个元素是(filename, label) dataset = tf.data.Dataset.from_tensor_slices((filenames,labels)) # 此时dataset中的一个元素是(file_batch, label_batch) dataset = dataset.shuffle(buffer_size=1000).batch(batch_size).repeat() # 从dataset中实例化了一个Iterator,只能从头到尾读取一次元素 iterator = dataset.make_one_shot_iterator() # file_batch,label_batch是返回的一维张量 file_batch,label_batch = iterator.get_next() # 返回一个batch的file和label return file_batch,label_batch #------------------------开始从数据集读取文件名和图像数据--------------------------------------- filenames,labels = get_files(train_dir,label_file) #------------------------创建会话,开始测试迭代读取图像----------------------------------------- with tf.Session() as sess: for i in range(10): print('Start Iterator') file_batch,label_batch = get_batch(filenames,labels,20) # 打印一个人batch的图像数据和标签 file_batch = sess.run(file_batch) label_batch = sess.run(label_batch) print(file_batch,label_batch) data,label = read_batch_image(file_batch,label_batch) print(data) print(label)
总结
数据准备的程序真的没有模板去套,需要我们再下载分析好数据之后,设计相应的文件名获取、数据读取(打乱、划分batch)、数据预处理、数据增强等功能函数。
发表评论