1. 程式人生 > >貓狗大戰2.0 使用tensorflow和tfrecord

貓狗大戰2.0 使用tensorflow和tfrecord

      距離上次的部落格已經過去了半個月兩週左右的時間,自己在b站和部落格上學習了很多相關的知識,自我感覺自己的tensorflow的水平已經算是到了入門的水平,在部落格上有相關的非tensorboard匯入資料,實測有效(傳送門由於時間久了,暫時找不到了,自己找一下吧)。不過通過tensorboard的可執行資料卻十分少,所以自己在此記錄一下自己的程式,也算是自己整理一下。

如果感覺有用,可以點一下關注喲!我會不定期更新一些自己學習的東西。

使用的IDE是vscode(python3.5),資料下載可以在其他部落格中找一下。

 首先工程圖如下:

Data資料夾下有train和test兩個資料夾,就是下載的資料集。Logs存放咱們所有的程式。

                                          

首先我們要建立建立tfrecord的檔案:

首先得到資料夾train下的所有檔名稱,貼出程式碼如下:

import tensorflow as tf
import numpy as np
import os

NumClass = 2
ImageWidth = 208
ImageHeight = 208
ImageChannel = 3

def get_files(file_dic):

    cats = []
    dogs = []
    cats_labels = []
    dogs_labels = []

    for file in os.listdir(file_dic):
        #得到檔案下所有的檔名稱,也就是cat.0jpg....

        name = file.split(sep='.')
        if name[0] == 'cat':
            cats.append(file_dic+'/'+file)
            cats_labels.append(0)
        if name[0] == 'dog':
            dogs.append(file_dic+'/'+file)
            dogs_labels.append(1)

    return cats, cats_labels, dogs, dogs_labels

cats, cats_labels, dogs, dogs_labels = get_files('G:/CatsAndDogs/data/train')
print(cats)

得到的輸出為:

['G:/CatsAndDogs/data/train/cat.0.jpg', 'G:/CatsAndDogs/data/train/cat.1.jpg'...

可以看到檔名稱都已匯入cats等列表當中,那麼接下來的操作就是把image和label的列表連線起來並打亂資料。程式碼貼出如下。

    #將檔案打亂順序
    image_list = np.hstack((cats, dogs)) #將兩個列表連線起來
    labels_list = np.hstack((cats_labels, dogs_labels))
    temp = np.array([image_list, labels_list])
    temp = temp.transpose() #轉置矩陣
    np.random.shuffle(temp) #打亂資料,下面的圖片是temp現在的資料


    ##從打亂的temp中再取出list,相當於洗牌之後的重新摸牌
    image_list = list(temp[:, 0])
    label_list = list(temp[:, 1])
    label_list = [int(i) for i in label_list]  # 字串型別轉換為int型別

現在temp程式跑到這的資料結果如下圖所示:

嗯,到現在為止也很成功,那我們接下來繼續操作,這個時候又變成了需要對整個列表的操作,所以我們需要整個列表的長度,再把相片的解碼出來。

from scipy.misc import imread,imresize #注意是得重新載入一個model

    #首先要確定整個列表的長度並且初始化相片的格式
    num_file = len(cats_labels) + len(dogs_labels)
    images = np.zeros((num_file, ImageHeight, ImageWidth, ImageChannel), dtype = np.uint8)

    for index in range(num_file):
        img = imread(image_list[index])
        img = imresize(img, (ImageWidth, ImageHeight))
        images[index] = img
        

接下來我們可以把這些相片,labels等資訊傳入一個類中,用來建立接下來的tdrecord檔案。

    class ImgData(object):
        pass
    
    result = ImgData()
    result.images = images
    result.labels = label_list
    result.num = num_file

到此我們就完成了第一個函式。此時得到了資料夾中亂序的所有照片及相應的標籤,暫時完成了第一步的工作。接下來我們就開始第二部的工作,完成tfrecord檔案的完成。

def convert(data, destination, destination1):
    """將圖片儲存為.tfrecords檔案
    引數:
        data: 上述函式返回的ImageData物件
        destination: 目標檔名
    """
 
    images = data.images
    labels = data.labels
    num_examples = data.num - 3000
                #使用上面使用的類進行下面的tfreord建立,照片標籤以及資料
 
    # 儲存的檔名
    filename = destination
                #儲存時的檔名(帶路徑的)
    
    # 使用TFRecordWriter來寫入資料
    writer = tf.python_io.TFRecordWriter(filename)
                #使用tfrecord進行寫入
    # 遍歷圖片
    for index in range(num_examples):
        # 轉為二進位制
        image = images[index].tostring()
        label = labels[index]
                #直接使用上面所建立的類進行輸入
                
        # tf.train下有Feature和Features,需要注意其區別
        # 層級關係為Example->Features->Feature(很重要)
        #注意圖片一般的型別為ByteList,其他的有Int64List和FloatList型
        example = tf.train.Example(features=tf.train.Features(feature={
            'image': tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])),
            'label': tf.train.Feature(int64_list=tf.train.Int64List(value=[label]))
        }))
        # 寫入建立的example
        writer.write(example.SerializeToString())
    writer.close()
            
    filename1 = destination1
    writer = tf.python_io.TFRecordWriter(filename1)
    for index in range(3000):
        # 轉為二進位制
        image = images[index+num_examples].tostring()
        label = labels[index+num_examples]
        example = tf.train.Example(features=tf.train.Features(feature={
            'image': tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])),
            'label': tf.train.Feature(int64_list=tf.train.Int64List(value=[label]))
        }))
        writer.write(example.SerializeToString())
    writer.close()    

convert(result, 'G:/CatsAndDogs/logs/traintfrecord', 'G:/CatsAndDogs/logs/testtfrecord')

上面的註釋很清楚,應該可以看懂,主要是有train和test兩個部分,不太懂的也可以百度查一下,應該可以找到相關的資料。接下來的就是解碼一下資料。

def read_and_decode(filename_queue, batch_size, capacity):
    """讀取.tfrecords檔案
    引數:
        filename_queue: 檔名, 一個列表
    返回:
        img, label: **單張圖片和對應標籤**
    """
    # 建立一個圖節點,該節點負責資料輸入
    filename_queue = tf.train.string_input_producer([filename_queue])
                # tf.train.string_input_producer函式把我們需要的全部檔案打包為一個tf內部的queue型別
                # 之後tf開檔案就從這個queue中取目錄了,要注意一點的是這個函式的shuffle引數預設是True
                # 所以讀取的順序可能不一樣
    reader = tf.TFRecordReader()
    _, serialized_example = reader.read(filename_queue)
                # 讀取前面所建立的資料目錄
    
    # 解析單個example
            # 暫時不大清楚下面的操作,不過感覺應該是得到各個features
    features = tf.parse_single_example(serialized_example, features={
        'image': tf.FixedLenFeature([], tf.string),
        'label': tf.FixedLenFeature([], tf.int64)
    })
            # tf.decode_raw函式的意思是將原來編碼為字串型別的變數重新變回來
            # 這個方法在資料集dataset中很常用,因為製作圖片源資料一般寫進tfrecord裡用to_bytes的形式,也就是字串
            # 這裡將原始資料取出來,必須制定原始資料的格式,原始資料是什麼格式這裡解析必須是什麼格式!!
            # tf.cast這個函式主要用於資料型別的轉變,不會改變原始資料的值還有形狀的
    image = tf.decode_raw(features['image'], tf.uint8)
    image = tf.reshape(image, [ImageHeight, ImageWidth, ImageChannel])
    image = tf.cast(image, tf.float32)
    label = tf.cast(features['label'], tf.int32)
    
    image_batch, label_batch = tf.train.batch([image, label],
                                              batch_size=batch_size,
                                              num_threads=64,  # 執行緒
                                              capacity=capacity)
    
    return image, label

上面是解析tfrecord的函式,那麼可以利用下面的程式進行train和test資料的獲得。

image, label = read_and_decode('G:/CatsAndDogs/logs/traintfrecord', 16, 2000) 
image1, label1 = read_and_decode('G:/CatsAndDogs/logs/testtfrecord', 16, 2000)  

整體的函式如下:

import tensorflow as tf
import numpy as np
import os
from scipy.misc import imread,imresize

NumClass = 2
ImageWidth = 208
ImageHeight = 208
ImageChannel = 3

def get_files(file_dic):

    cats = []
    dogs = []
    cats_labels = []
    dogs_labels = []

    #得到檔案下所有的檔名稱,也就是cat.0jpg....
    for file in os.listdir(file_dic):   

        name = file.split(sep='.')
        if name[0] == 'cat':
            cats.append(file_dic+'/'+file)
            cats_labels.append(0)
        if name[0] == 'dog':
            dogs.append(file_dic+'/'+file)
            dogs_labels.append(1)
            
    num_file = len(cats_labels) + len(dogs_labels)
    images = np.zeros((num_file, ImageHeight, ImageWidth, ImageChannel), dtype = np.uint8)
    print("There are %d cats\nThere are %d dogs" % (len(cats), len(dogs)))

    #將檔案打亂順序
    image_list = np.hstack((cats, dogs))
    labels_list = np.hstack((cats_labels, dogs_labels))
    temp = np.array([image_list, labels_list])
    temp = temp.transpose()
    np.random.shuffle(temp)

    ##從打亂的temp中再取出list(img和lab)
    image_list = list(temp[:, 0])
    label_list = list(temp[:, 1])
    label_list = [int(i) for i in label_list]  # 字串型別轉換為int型別
    
    for index in range(num_file):
        img = imread(image_list[index])
        img = imresize(img, (ImageWidth, ImageHeight))
        images[index] = img
        
    class ImgData(object):
        pass
    
    result = ImgData()
    result.images = images
    result.labels = label_list
    result.num = num_file
    
    return result

def convert(data, destination, destination1):
    """將圖片儲存為.tfrecords檔案
    引數:
        data: 上述函式返回的ImageData物件
        destination: 目標檔名
    """
 
    images = data.images
    labels = data.labels
    num_examples = data.num - 3000
                #使用上面使用的類進行下面的tfreord建立,照片標籤以及資料
 
    # 儲存的檔名
    filename = destination
                #儲存時的檔名(帶路徑的)
    
    # 使用TFRecordWriter來寫入資料
    writer = tf.python_io.TFRecordWriter(filename)
                #使用tfrecord進行寫入
    # 遍歷圖片
    for index in range(num_examples):
        # 轉為二進位制
        image = images[index].tostring()
        label = labels[index]
                #直接使用上面所建立的類進行輸入
                
        # tf.train下有Feature和Features,需要注意其區別
        # 層級關係為Example->Features->Feature(很重要)
        #注意圖片一般的型別為ByteList,其他的有Int64List和FloatList型
        example = tf.train.Example(features=tf.train.Features(feature={
            'image': tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])),
            'label': tf.train.Feature(int64_list=tf.train.Int64List(value=[label]))
        }))
        # 寫入建立的example
        writer.write(example.SerializeToString())
    writer.close()
            
    filename1 = destination1
    writer = tf.python_io.TFRecordWriter(filename1)
    for index in range(3000):
        # 轉為二進位制
        image = images[index+num_examples].tostring()
        label = labels[index+num_examples]
        example = tf.train.Example(features=tf.train.Features(feature={
            'image': tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])),
            'label': tf.train.Feature(int64_list=tf.train.Int64List(value=[label]))
        }))
        writer.write(example.SerializeToString())
    writer.close()    

convert(result, 'G:/CatsAndDogs/logs/traintfrecord', 'G:/CatsAndDogs/logs/testtfrecord')


def read_and_decode(filename_queue, batch_size, capacity):
    """讀取.tfrecords檔案
    引數:
        filename_queue: 檔名, 一個列表
    返回:
        img, label: **單張圖片和對應標籤**
    """
    # 建立一個圖節點,該節點負責資料輸入
    filename_queue = tf.train.string_input_producer([filename_queue])
                # tf.train.string_input_producer函式把我們需要的全部檔案打包為一個tf內部的queue型別
                # 之後tf開檔案就從這個queue中取目錄了,要注意一點的是這個函式的shuffle引數預設是True
                # 所以讀取的順序可能不一樣
    reader = tf.TFRecordReader()
    _, serialized_example = reader.read(filename_queue)
                # 讀取前面所建立的資料目錄
    
    # 解析單個example
            # 暫時不大清楚下面的操作,不過感覺應該是得到各個features
    features = tf.parse_single_example(serialized_example, features={
        'image': tf.FixedLenFeature([], tf.string),
        'label': tf.FixedLenFeature([], tf.int64)
    })
            # tf.decode_raw函式的意思是將原來編碼為字串型別的變數重新變回來
            # 這個方法在資料集dataset中很常用,因為製作圖片源資料一般寫進tfrecord裡用to_bytes的形式,也就是字串
            # 這裡將原始資料取出來,必須制定原始資料的格式,原始資料是什麼格式這裡解析必須是什麼格式!!
            # tf.cast這個函式主要用於資料型別的轉變,不會改變原始資料的值還有形狀的
    image = tf.decode_raw(features['image'], tf.uint8)
    image = tf.reshape(image, [ImageHeight, ImageWidth, ImageChannel])
    image = tf.cast(image, tf.float32)
    label = tf.cast(features['label'], tf.int32)
    
    image_batch, label_batch = tf.train.batch([image, label],
                                              batch_size=batch_size,
                                              num_threads=64,  # 執行緒
                                              capacity=capacity)
    
    return image, label

image, label = read_and_decode('G:/CatsAndDogs/logs/traintfrecord', 16, 2000) 
image1, label1 = read_and_decode('G:/CatsAndDogs/logs/testtfrecord', 16, 2000)  

不過要注意先用前兩個函式生成所需的tfrecord檔案才能跑第三個函式。

明天繼續剩下的部分。