真锋
永远保持一颗学习和专注的心
嵌入式视觉

【Kaggle竞赛】数据准备

这篇文章的标题纠结了半天,因为在做深度学习的工作时,数据是非常重要的,第一步的工作也是准备数据,这中间我们需要做很多工作包括数据输入、数据预处理、数据增强等,我个人把这一步的工作命名为数据准备,当然也可以有其他命名。

前言:在我们做图像识别的问题时,碰到的数据集可能有多种多样的形式,常见的文件如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)、数据预处理、数据增强等功能函数。

赞赏
更多干货文章,欢迎关注我的微信公众号-嵌入式视觉。

发表回复

textsms
account_circle
email

  • kingskr

    楼主 训练数据集哪里下载的啊

    4年前 回复
    • harley博主

      @kingskr: 我这里是以kaggle癌症检测比赛为例,下载地址在这https://www.kaggle.com/c/histopathologic-cancer-detection/data,如果是自己的项目就需要自己准备数据集了

      4年前 回复
  • harley博主

    后续测试证明,程序训练没有问题,有问题的是我使用的极算云服务器。

    4年前 回复

嵌入式视觉

【Kaggle竞赛】数据准备
这篇文章的标题纠结了半天,因为在做深度学习的工作时,数据是非常重要的,第一步的工作也是准备数据,这中间我们需要做很多工作包括数据输入、数据预处理、数据增强等,我个人把这一步的…
扫描二维码继续阅读
2018-11-30