Contents
- 1 环境准备
- 2 处理流程
- 3 程序设计
- 3.1 第一个版本程序
- 3.2 输出结果
- 3.3 第二个版本程序
- 4 总结
这篇文章的标题纠结了半天,因为在做深度学习的工作时,数据是非常重要的,第一步的工作也是准备数据,这中间我们需要做很多工作包括数据输入、数据预处理、数据增强等,我个人把这一步的工作命名为数据准备,当然也可以有其他命名。
前言:在我们做图像识别的问题时,碰到的数据集可能有多种多样的形式,常见的文件如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函数如下:
代码语言:javascript复制# ----------------------------获取文件名函数,从训练集标签获取-----------------------------------
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()函数如下:
代码语言:javascript复制#------------------------------读取图像数据,划分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)
全部程序如下:
代码语言:javascript复制# 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的文件名地址集和标签)
程序如下:
代码语言:javascript复制# 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 imagesnThere 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)、数据预处理、数据增强等功能函数。