【TensorFlow】笔记5:图像数据处理
文章目錄
- 一、TFRecords輸入數(shù)據(jù)格式
- 1、TFRecord格式介紹
- 2、TFRecord樣例程序
- (1)生成TFRecords文件
- (2)讀取TFRecord文件
- 二、圖像數(shù)據(jù)處理
- 1、TF圖像處理函數(shù)
- (1)圖像編碼處理
- (2)圖像大小處理
- a、通過算法使得新的圖像盡量保存原始圖像上所有的信息。
- b、對圖像裁剪或填充——`tf.image.resize_image_with_crop_or_pad`函數(shù)
- c、比例調(diào)整圖像大小——`tf.image.entral_crop`函數(shù)
- d、剪裁或填充給定區(qū)域的圖像——`tf.image.crop_to_bounding_box`函數(shù)和`tf.image.pad_to_bounding_box`函數(shù)
- (3)圖像翻轉(zhuǎn)
- (4)圖像色彩調(diào)整
- a、調(diào)整亮度
- b、調(diào)整對比度
- c、調(diào)整色相
- d、調(diào)整飽和度
- e、圖像標(biāo)準(zhǔn)化
- f、處理標(biāo)注框(bounding box)
- g、隨機(jī)截取圖像
- 2、圖像預(yù)處理實例
- 三、多線程輸入數(shù)據(jù)處理框架
- 1、隊列與多線程
- (1)兩種隊列
- (2)多線程協(xié)同方法1:`tf.Coordinator` 類
- (3)多線程協(xié)同方法2:`tf.QueueRunner` 類
- 2、輸入文件隊列
- 3、組合訓(xùn)練數(shù)據(jù)(batching)
- (1)`tf.train.batch`
- (2)`tf.train.shuffle_batch`
- 4、輸入數(shù)據(jù)處理框架
- 5、總結(jié)
- 四、數(shù)據(jù)集(DataSet)
- 1、數(shù)據(jù)集的基本使用方法
- (1)tensor -> 數(shù)據(jù)集
- (2)文本文件 -> 數(shù)據(jù)集
- (3)TFRecord -> 數(shù)據(jù)集
- 2、數(shù)據(jù)集的高層操作
- (1)map操作
- (2)shuffle 和 batch 操作
- (3)repeat 操作
- (4)其他操作
- (5)示例
目標(biāo):如何對圖像數(shù)據(jù)進(jìn)行預(yù)處理使得訓(xùn)練得到的神經(jīng)網(wǎng)絡(luò)模型盡可能小地被無關(guān)因素所影響。同時,為減小預(yù)處理對訓(xùn)練速度的影響,使用多線程處理。
一、TFRecords輸入數(shù)據(jù)格式
TF統(tǒng)一存儲數(shù)據(jù)的格式:TFRecords,二進(jìn)制文件,可更好利用內(nèi)存、便于復(fù)制和移動,且不需要單獨(dú)的標(biāo)簽文件。
1、TFRecord格式介紹
TFRecord 文件中的數(shù)據(jù)都是通過 tf.train.Example協(xié)議內(nèi)存塊(Protocol Buffer)(協(xié)議內(nèi)存塊包含了字段Features)的格式存儲的。
tf.train.Example定義:
message Example {Features features = 1; };message Features {map<string, Feature> feature = 1; };message Feature {oneof kind{BytesList bytes_list = 1;FloatList float_list = 2;Int64List int64_list = 3;} };tf.train.Example 中包含了一個從屬性名稱到取值的字典。
其中屬性名稱為一個字符串,屬性的取值可以為字符串(BytesList)、實數(shù)列表 (FloatList)或者整數(shù)列表( Int64List)。
比如將一張解碼前的圖像存為一個字符串,圖像所對應(yīng)的類別編號存為整數(shù)列表。在2中將給出 一個使用 TFRecord 的具體樣例 。
數(shù)據(jù)存儲:將數(shù)據(jù)填入到 Example 協(xié)議內(nèi)存塊(protocol buffer),將協(xié)議內(nèi)存塊序列化為一個字符串, 并且通過tf.python_io.TFRecordWriter 寫入到 TFRecords 文件。
從TFRecords文件中讀取數(shù)據(jù), 可以使用tf.TFRecordReader的tf.parse_single_example解析器。這個操作可以將Example協(xié)議內(nèi)存塊(protocol buffer)解析為張量。
2、TFRecord樣例程序
(1)生成TFRecords文件
將MNIST輸入數(shù)據(jù)轉(zhuǎn)換為TFRecord的格式
import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data import numpy as np# 生成整數(shù)型的屬性。 def _int64_feature(value):return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))# 生成字符串型的屬性。 def _bytes_feature(value):return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))DATASET_PATH = '/home/jie/Jie/codes/tf/datasets/MNIST_DATA' mnist = input_data.read_data_sets(DATASET_PATH, dtype=tf.uint8, one_hot=True) images = mnist.train.images # 訓(xùn)練數(shù)據(jù)所對應(yīng)的正確答案,可以作為一個屬性保存在 TFRecord 中 labels = mnist.train.labels # 訓(xùn)練數(shù)據(jù)的圖像分辨率,這可以作為 Example 中的一個屬性。 pixels = images.shape[1] num_example = mnist.train.num_example# 輸出 TFRecord 文件的地址。 filename = '/home/jie/Jie/codes/tf/datasets/MNIST_DATA/output.tfrecords' # 創(chuàng)建一個 writer 來寫 TFRecord 文件。 writer = tf.python_io.TFRecordWriter(filename) for index in range(num_example):# 將圖像矩陣轉(zhuǎn)化成一個字符串。image_raw = images[index].tostring()# 將一個樣例轉(zhuǎn)化為 Example Protocol Buffer ,并將所有的信息寫入這個數(shù)據(jù)結(jié)構(gòu)。example = tf.train.Example(features=tf.train.Features(features={'pixels':_int64_feature(pixels),'label':_int64_feature(np.argmax(labels[index])),'image_raw':_bytes_features(images_raw)}))# 將一個 Example 寫入 TFRecord 文件。writer.write(example.SerializeToString()) writer.close()當(dāng)數(shù)據(jù)量較大時,也可以將數(shù)據(jù)寫入多個 TFRecord 文件。
(2)讀取TFRecord文件
import tensorflow as tf# 輸出 TFRecord 文件的地址。 filename = '/home/jie/Jie/codes/tf/datasets/MNIST_DATA/output.tfrecords' # 創(chuàng)建一個 reader 來讀取 TFRecord 文件中的樣例。 reader = tf.TFRecordReader() # tf.train.string_input_producer 創(chuàng)建一個隊列來維護(hù)輸入文件列表 filename_queue = tf.train.string_input_producer([filename])# 從文件中讀取一個樣例。也可以使用 read_up_to 函數(shù)-次性讀取多個樣例。 _, serialized_example = reader.read(filename_queue) # 解析讀入的一個樣例。若需要解析多個樣例,可用parse_example函數(shù) features = tf.parse_single_example(serialized_example,features={# TensorFlow 提供兩種不同的屬性解析方法。 # 1. tf.FixedLenFeature,所解析的結(jié)果為一個 Tensor。# 2. tf.VarLenFeature,所得到的解析結(jié)果為SparseTensor,用于處理稀疏數(shù)據(jù)。# 這里解析數(shù)據(jù)的格式需要和上面程序可入數(shù)據(jù)的格式一致。'image_raw':tf.FixedLenFeature([], tf.string),'pixels':tf.FixedLenFeature([], tf.int64),'label':tf.FixedLenFeature([], tf.int64),})# tf.decode_raw 可以將字符串解析成圖像對應(yīng)的像索數(shù)組。 image = tf.decode_raw(features['image_raw'], tf.uint8) label = tf.cast(features['label'], tf.int32) pixels = tf.cast(features['pixels'], tf.int32)sess = tf.Session() # 啟動多線程處理輸入數(shù)據(jù) coord = tf.train.Coordinator() threads = tf.train.start_queue_runners(sess=sess, coord=coord)# 每次運(yùn)行可以讀取 TFRecord 文件中的一個樣例。 # 當(dāng)所有樣例都讀取完之后,在此樣例中程序會再重頭讀取 for i in range(10):print(sess.run([image, label, pixels]))二、圖像數(shù)據(jù)處理
目標(biāo):可以盡量避免模型受到無關(guān)因素的影響。在大部分圖像識別中,通過預(yù)處理過程可以提高模型的準(zhǔn)確率。
1、TF圖像處理函數(shù)
(1)圖像編碼處理
RGB三維矩陣( jpeg 和 png 格式) 《==》 編碼/解碼
# -*-coding:utf-8-*- import tensorflow as tf import matplotlib.pyplot as pltIMAGES_PATH = '/home/jie/Desktop/6987985.jpeg' SAVE_PATH = '/home/jie/Desktop/output.jpeg' # 讀取圖像的原始數(shù)據(jù)。 image_raw_data = tf.gfile.FastGFile(IMAGES_PATH, 'rb').read()with tf.Session() as sess:# 對圖像進(jìn)行 jpeg 的格式解碼從而得到圖像對應(yīng)的三維矩陣。 # tf.image.decode_png 函數(shù)對 png 格式的圖像進(jìn)行解碼。# 解碼之后的結(jié)果為一個張量,在使用它的取值之前需要明確調(diào)用運(yùn)行的過程。img_data = tf.image.decode_jpeg(image_raw_data)print(img_data.eval())# 輸出解碼之后的三維矩陣,上面這一行代碼將輸出以下內(nèi)容。# 使用 pyplot 工具可視化得到的圖像。plt.imshow(img_data.eval())plt.show()# 將表示一張圖像的三維矩陣重新按照jpeg格式編碼并存入文件中。# 打開該圖像,與原始圖像一樣encoded_image = tf.image.encode_jpeg(img_data)with tf.gfile.GFile(SAVE_PATH, "wb") as f:f.write(encoded_image.eval())輸出結(jié)果
[[[ 11 7 0][ 29 25 22][ 32 33 38]...[ 29 31 28][ 19 21 20][ 5 7 2]][[ 38 31 15][191 184 174][213 213 213]...[209 214 217][183 187 188][ 32 37 33]][[ 19 44 51][200 228 242][153 189 215]...[188 201 209][210 218 221][ 24 33 32]]...[[ 56 34 23][245 217 196][125 87 42]...[ 77 74 65][214 215 209][ 36 37 32]][[ 20 30 40][177 184 190][211 216 212]...[217 216 214][185 185 183][ 27 27 25]][[ 6 6 4][ 29 29 27][ 29 33 32]...[ 29 30 32][ 32 32 32][ 1 1 1]]](2)圖像大小處理
由于很多網(wǎng)絡(luò)輸入節(jié)點的個數(shù)是固定的,所以需要先將圖像的大小統(tǒng)一。
圖像大小調(diào)整有兩種方法:
a、通過算法使得新的圖像盡量保存原始圖像上所有的信息。
TF的 tf.image.resize_images 函數(shù)提供四種不同的方法
# 略去加載原始圖像,定義會話等過程,假設(shè):img_data是已經(jīng)解碼的圖像。# 1. 首先將圖片數(shù)據(jù)轉(zhuǎn)換為實數(shù)類型,即將0-255轉(zhuǎn)化為0.0-1.0范圍內(nèi)的實數(shù)。# 大多數(shù)圖像處理 API 支持整數(shù)和實數(shù)類型的輸入。# 如果輸入是整數(shù)類型,這些 API 會在內(nèi)部將輸入轉(zhuǎn)化為實數(shù)后處理,再將輸出轉(zhuǎn)化為整數(shù)。# 如果有多個處理步驟,在格數(shù)和實數(shù)之間的反復(fù)轉(zhuǎn)化將導(dǎo)致精度損失,# 因此推薦在圖像處理前將其轉(zhuǎn)化為實數(shù)類型。img_data = tf.image.convert_image_dtype(img_data, dtype=tf.float32)# 通過 tf.image.resize_images 函數(shù)調(diào)整圖像的大小。# 第一個參數(shù): 原始圖像# 第二個和第三個參數(shù)為調(diào)整后圖像的大小 # method 參數(shù)給出了調(diào)整圖像大小的算法resized = tf.image.resize_images(img_data, [300, 300], method=0)plt.imshow(resized.eval())plt.show()| 0 | 雙線性插值法(Bilinear interpolation) |
| 1 | 最近鄰插值(Nearest neighbor interpolation) |
| 2 | 雙三次插值法(Bicubic interpolation) |
| 3 | 面積插值法(Area interpolation) |
==》不同算法效果差別不大
b、對圖像裁剪或填充——tf.image.resize_image_with_crop_or_pad函數(shù)
# 通過 tf.image.resize_image_with_crop_or_pad 函數(shù)調(diào)整圖像的大小。# 第一個參數(shù): 原始圖像# 第二個和第三個參數(shù)為調(diào)整后的目標(biāo)圖像大小。# 如果原始圖像的尺寸大于大于目標(biāo)圖像,則該函數(shù)會自動截取y你是圖像中居中的部分。# 若目標(biāo)圖像大于原始圖像,則會自動在原始圖像的四周填充全0背景。croped = tf.image.resize_image_with_crop_or_pad(img_data, 400, 300)padded = tf.image.resize_image_with_crop_or_pad(img_data, 1500, 1500)plt.imshow(croped.eval())plt.show()plt.imshow(padded.eval())plt.show()
c、比例調(diào)整圖像大小——tf.image.entral_crop函數(shù)
# 第一個參數(shù)為原始圖像, 第二個參數(shù)為調(diào)整比例,這個比例需要是-個(0, 1]的實數(shù)central_cropped = tf.image.central_crop(img_data, 0.25)plt.imshow(central_cropped.eval())plt.show()d、剪裁或填充給定區(qū)域的圖像——tf.image.crop_to_bounding_box函數(shù)和tf.image.pad_to_bounding_box函數(shù)
這兩個函數(shù)都要求給出的尺寸滿足一定的要求,否則程序會報錯。比如在使用 tf.image.crop_to_bounding_box 函數(shù)時, TensorFlow 要求提供的圖像尺寸要大于目標(biāo)尺寸,也就是要求原始圖像能夠裁剪出目標(biāo)圖像的大小。
(3)圖像翻轉(zhuǎn)
功能:上下翻轉(zhuǎn)、左右翻轉(zhuǎn)以及沿對角線翻轉(zhuǎn)
# 將圖像上下翻轉(zhuǎn)flipped1 = tf.image.flip_up_down(img_data)# 將圖像左右翻轉(zhuǎn)flipped2 = tf.image.flip_left_right(img_data)# 將圖像沿對角線翻轉(zhuǎn)flipped3 = tf.image.transpose_image(img_data)plt.subplot(221)plt.imshow(img_data.eval())plt.subplot(222)plt.imshow(flipped1.eval())plt.subplot(223)plt.imshow(flipped2.eval())plt.subplot(224)plt.imshow(flipped3.eval())plt.show()輸出結(jié)果
在很多圖像識別問題中,圖像的翻轉(zhuǎn)不會影響識別的結(jié)果。
==》訓(xùn)練得到的模型可以識別不同角度的實體
==》隨機(jī)翻轉(zhuǎn)訓(xùn)練圖像是一種很常用的圖像預(yù)處理方式。
(4)圖像色彩調(diào)整
調(diào)整圖像的亮度、對比度、飽和度和色相,在很多圖像識別應(yīng)用中不會影響識別結(jié)果。
==》隨機(jī)調(diào)整這些屬性,使得訓(xùn)練的模型盡可能小地受無關(guān)因素的影響。
a、調(diào)整亮度
tf.image.adjust_brightness()函數(shù)和tf.image.random_brightness()函數(shù)
注意:
- 色彩調(diào)整的 API 可能導(dǎo)致像素的實數(shù)值超出 0.0-1.0 的范圍。在輸出最終圖像前需要將其值截斷在 0.0-1.0 范圍區(qū)間,否則不僅圖像無法正常可視化,以此為輸入的神經(jīng)網(wǎng)絡(luò)的訓(xùn)練質(zhì)量可能受到影響。
- 若對圖像進(jìn)行多項處理操作,則截斷應(yīng)在最后一步
b、調(diào)整對比度
tf.image.adjust_contrast()函數(shù)和tf.image.random_contrast()函數(shù)
# 將圖像的對比度減少到 0.5 倍adjusted1 = tf.image.adjust_contrast(img_data, 0.5) # 將圖像的對比度增加 5 倍adjusted2 = tf.image.adjust_contrast(img_data, 5)# 在[lower, upper] 的范圍隨機(jī)調(diào)整圖像的對比度。lower = 10upper = 100adjusted3 = tf.image.random_contrast(img_data, lower, upper)c、調(diào)整色相
tf.image.adjust_hue函數(shù)和tf.image.random_hue函數(shù)
# 分別將色相加 0.1、0.3、0.6 和 0.9 adjusted1 = tf.image.adjust_hue(img_data, 0.1)adjusted2 = tf.image.adjust_hue(img_data, 0.3) adjusted3 = tf.image.adjust_hue(img_data, 0.6) adjusted4 = tf.image.adjust_hue(img_data, 0.9) # 在 [-max_delta, max_delta] 的范圍隨機(jī)調(diào)整圖像的色相。max_delta = 0.5adjusted5 = tf.image.random_hue(img_data, max_delta)注意:max_delta最大為 0.5
d、調(diào)整飽和度
tf.image.adjust_saturation函數(shù)和tf.image.random_saturation函數(shù)
# 將圖像的飽和度-5adjusted1 = tf.image.adjust_saturation(img_data, -5) # 將圖像的飽和度+5adjusted2 = tf.image.adjust_saturation(img_data, 5)# 在[lower, upper] 的范圍隨機(jī)調(diào)整圖像的飽和度lower = 10upper = 100adjusted3 = tf.image.random_saturation(img_data, lower, upper)e、圖像標(biāo)準(zhǔn)化
tf.image.per_image_standardization() :函數(shù)將圖像上的亮度均值變?yōu)?,方差變?yōu)?。
adjusted = tf.image.per_image_standardization(img_data)f、處理標(biāo)注框(bounding box)
tf.image.draw_bounding_boxes()函數(shù)在圖像中添加標(biāo)注框。輸入圖像矩陣的數(shù)字為實數(shù),且是一個 batch 的數(shù)據(jù)(四維張量)。所以,繪制圖時,需要tf.reduce_sum()函數(shù)進(jìn)行降維。
# 將圖像縮小一些,這樣可視化能讓標(biāo)注框更加清楚。img_data = tf.image.resize_images(img_data, [180, 267], method=1)# tf.image.draw_bounding_boxes 函數(shù)要求圖像矩陣中的數(shù)字為實數(shù).# -> 先將圖像矩陣轉(zhuǎn)化為實數(shù)類型。# 輸入是一個 bacth 的數(shù)據(jù)(多張圖像組成的四維矩陣),要將解碼之后的圖像矩陣添加一維bacthed = tf.expand_dims(tf.image.convert_image_dtype(img_data, tf.float32), 0)# 給出每一張圖像的所有標(biāo)注框。一個標(biāo)注框有 4 個數(shù)字,分別代表[Y_min, X_min, Y_max, X_max]# 注意這里給出的數(shù)字都是圖像的相對位置。# 比如在 180 × 267 的圖像中,[0.35, 0.47, 0.5, 0.56]代表了從(63,125)到(90, 150)的圖像。boxes = tf.constant([[[0.05, 0.05, 0.9, 0.7], [0.35, 0.47, 0.5, 0.56]]])result = tf.image.draw_bounding_boxes(bacthed, boxes)result = tf.reduce_sum(result, 0) # 這里顯示的時候需要進(jìn)行降維處理plt.imshow(result.eval())plt.show()
注:bounding box位置不準(zhǔn),僅用于學(xué)習(xí)。
g、隨機(jī)截取圖像
隨機(jī)截取圖像上有信息含量的部分也是一個提
高模型健壯性( robustness )的一種方式。
==》使得訓(xùn)練模型不受被識別物體大小的影響。
==》tf.image.sample_distorted_bounding_box()函數(shù)
2、圖像預(yù)處理實例
在解決真實的圖像識別問題時, 一般會同時使用多種處理方法。
本實例展示:將不同的圖像處理函數(shù)結(jié)合成一個完成的圖像預(yù)處理流程。從圖像片段截取,到圖像大小調(diào)整再到圖像翻轉(zhuǎn)及色彩調(diào)整的整個圖像預(yù)處理過程。
- 調(diào)整亮度、對比度、飽和度和色相的順序會影響最后的結(jié)果,所以定義多種不同的順序。
- 注意:數(shù)據(jù)預(yù)處理只針對訓(xùn)練數(shù)據(jù),而非測試數(shù)據(jù)
輸入:原始訓(xùn)練圖像
輸出:神經(jīng)網(wǎng)絡(luò)的輸入層
三、多線程輸入數(shù)據(jù)處理框架
復(fù)雜的預(yù)處理過程會減慢整個訓(xùn)練的過程,所以為了避免預(yù)處理成為神經(jīng)網(wǎng)絡(luò)模型訓(xùn)練效率的瓶頸,TF提供多線程處理輸入數(shù)據(jù)的框架。
經(jīng)典輸入流程:
1、隊列與多線程
在TF中,隊列也是計算圖上有狀態(tài)的節(jié)點,修改隊列狀態(tài)的操作主要有 Enqueue、EnqueueMany和Dequeue。
(1)兩種隊列
- FIFOQueue:先進(jìn)先出隊列。
- RandomShuffleQueue:將隊列中的元素打亂,每次出隊列操作得到的是從當(dāng)前隊列所有元素中隨機(jī)選擇的一個。(訓(xùn)練網(wǎng)絡(luò)時希望每次使用的訓(xùn)練數(shù)據(jù)盡可能隨機(jī),推薦使用)
輸出結(jié)果
0 10 1 11 2TF中,隊列還是異步計算張量取值的一個重要機(jī)制。例:多個線程可同時向一個隊列中寫入或讀取元素。
(2)多線程協(xié)同方法1:tf.Coordinator 類
tf.Coordinator類:用于協(xié)同多個線程一起停止, 并提供了 should_stop、request_stop 和 join 三個函數(shù)。
工作過程:
輸出
Working on id: 0 Working on id: 1 Working on id: 2 Working on id: 3 Working on id: 4 Stoping from id: 0 Working on id: 2 Working on id: 3分析:主與所有線程啟動之后,每個線程會打印各自的 ID,于是前面 4 行打印出了它們的 ID 。然后在暫停 1 秒之后,所有線程又開始第二遍打印 ID。 在這個時候有一個線程退出的條件達(dá)到,于是調(diào)用了 coord.request_stop 函數(shù)來停止所有其他的線程。 然而在打印 Stoping from id: 0 之后,可以看到有線程仍然在輸出。這是因為這些線程已經(jīng)執(zhí)行完coord.should_stop的判斷,于是仍然會繼續(xù)輸出自己的 ID 。 但在下一輪判斷是否需要停止時將退出線程。 于是在打印一次 ID 之后就不會再有輸出了。
(3)多線程協(xié)同方法2:tf.QueueRunner 類
tf.QueueRunner類:用于啟動多個線程來操作同一個隊列,啟動的這些線程可以通過上面介紹的 tf.Coordinator 類來統(tǒng)一管理
# 聲明一個先進(jìn)先出的隊列,隊列中最多100個元素,類型是實數(shù) queue = tf.FIFOQueue(100, "float") # 定義隊列的入隊操作 enqueue_op = queue.enqueue([tf.random_normal([1])])# 使用 tf.train.QueueRunner 創(chuàng)建多個線程運(yùn)行隊列的入隊操作。 # 第1個參數(shù):被操作的隊列, # [enqueue_op]*5 需要啟動5個線程,每個線程中運(yùn)行的是 enqueue_op 操作。 qr = tf.train.QueueRunner(queue, [enqueue_op] * 5)# 將定義過的QueueRunner加入TensorFlow 計算圖上指定的集合。 # tf.train.add_queue_runner 函數(shù)沒有指定集合,則加入默認(rèn)集合 tf.GraphKeys.QUEUE_RUNNERS。 # 下面的函數(shù)就是將剛剛定義的 qr 加入默認(rèn)的 tf.GraphKeys.QUEUE_RUNNERS 集合。 tf.train.add_queue_runner(qr) # 定義出隊操作。 out_tensor = queue.dequeue()with tf.Session() as sess:# 使用 tf.train.Coordinator 來協(xié)同啟動的線程。coord = tf.train.Coordinator()# 使用tf.train.QueueRunner時,需要明確調(diào)用tf.train.start_queue_runners來啟動所有線程。# 否則因為沒有線程運(yùn)行入隊操作,當(dāng)調(diào)用出隊操作時,程序會一直等待入隊操作被運(yùn)行。# tf.train.start_queue_runners 函數(shù)會默認(rèn)啟動tf.GraphKeys.QUEUE_RUNNERS集合中所有的QueueRunner。# 這個函數(shù)只支持啟動指定集合中的 QueueRnner,# 所以一般來說 tf.train.add_queue_runner 函數(shù)和tf.train.start_queue_runners函數(shù)會指定同一個集合。threads = tf.train.start_queue_runners(sess = sess, coord=coord)# 獲取隊列中的取值。for _ in range(3):print(sess.run(out_tensor)[0])# 使用 tf.train.Coordinator 來停止所有的線程。coord.request_stop()coord.join(threads)輸出結(jié)果
-1.475753 -0.27835137 2.24072552、輸入文件隊列
雖然一個TFRecord文件可以存儲多個訓(xùn)練樣本,但當(dāng)訓(xùn)練數(shù)據(jù)量較大時,可以將數(shù)據(jù)分成多個TFRecord文件來提高處理效率。
該函數(shù)會使用初始化時提供的文件列表創(chuàng)建一個輸入隊列,輸入隊列中的原始元素為文件列表中的所有文件,創(chuàng)建好的輸入隊列可以作為文件讀取函數(shù)的參數(shù),每層調(diào)用文件讀取函數(shù)時,該函數(shù)會先判斷當(dāng)前是否已經(jīng)有打開的文件可讀,如果沒有或者打開的文件已經(jīng)讀完,則該函數(shù)會從輸入隊列中出隊一個文件并從該文件中讀取數(shù)據(jù)。
- 當(dāng) shuffle=True 時,文件在加入隊列之前會被打亂順序,所以出隊順序也是隨機(jī)的;
- 隨機(jī)打亂文件順序以及加入輸入隊列的過程是一個單獨(dú)的線程,不會影響獲取文件的速度。
- 當(dāng)輸入隊列中的所有文件都被處理完之后,會將初始化時提供的文件列表中的文件全部重新加入隊列。
- num_epochs:限制加載初始文件列表的最大輪數(shù)
- 當(dāng)設(shè)置為1時,計算完一輪之后,程序?qū)⒆詣油V埂?/li>
- 神經(jīng)網(wǎng)絡(luò)模型測試時,所有測試數(shù)據(jù)僅僅需要使用一次即可,所以將其設(shè)置為1。
每一個文件中存儲了兩個樣例 。
生成樣例之后,以下代碼展示了兩個函數(shù)的使用方法
# 使用 tf.train.match_filenames_once 函數(shù)獲取文件列表。 files = tf.train.match_filenames_once('./datasets/data.tfrecords-*')# 通過 tf.train.string_input_producer 函數(shù)創(chuàng)建輸入隊列. # 輸入隊列巾的中的文件列表為tf.train.string_input_producer函數(shù)獲取的文件列表。 # 參數(shù)設(shè)置:shuffle為False,在一般解決真實問題時,shuffle設(shè)置為True filename_queue = tf.train.string_input_producer(files, shuffle=False)reader = tf.TFRecordReader() _, seraialized_example = reader.read(filename_queue) features = tf.parse_single_example(seraialized_example,features={'i':tf.FixedLenFeature([], tf.int64),'j':tf.FixedLenFeature([], tf.int64),})with tf.Session() as sess:# 雖然在本段程序中沒有聲明任何變量,# 但使用 tf.train.match_filenames_once 函數(shù)時前要初始化一些變量 。tf.local_variables_initializer().run()print(sess.run(files))# 聲明 tf.train.Coordinator 類來協(xié)同不同線程,并啟動線程。coord = tf.train.Coordinator()threads = tf.train.start_queue_runners(sess=sess, coord=coord)# 多次執(zhí)行技取數(shù)據(jù)的操作。for i in range(6):print(sess.run([features['i'], features['j']]))coord.request_stop()coord.join(threads)輸出結(jié)果
[b'./datasets/data.tfrecords-00000-of-00002'b'./datasets/data.tfrecords-00001-of-00002'] [0, 0] [0, 1] [1, 0] [1, 1] [0, 0] [0, 1]3、組合訓(xùn)練數(shù)據(jù)(batching)
將多個輸入樣例組織成一個batch可以提高模型訓(xùn)練的效率,所以在得到單個樣例的預(yù)處理結(jié)果之后,還需要將其組織成batch,再提供給神經(jīng)網(wǎng)絡(luò)的輸入層。
(1)tf.train.batch
import tensorflow as tf# 使用 tf.train.match_filenames_once 函數(shù)獲取文件列表。 files = tf.train.match_filenames_once('./datasets/data.tfrecords-*')# 通過 tf.train.string_input_producer 函數(shù)創(chuàng)建輸入隊列. # 輸入隊列巾的中的文件列表為tf.train.string_input_producer函數(shù)獲取的文件列表。 # 參數(shù)設(shè)置:shuffle為False,在一般解決真實問題時,shuffle設(shè)置為True filename_queue = tf.train.string_input_producer(files, shuffle=False)reader = tf.TFRecordReader() _, seraialized_example = reader.read(filename_queue) features = tf.parse_single_example(seraialized_example,features={'i':tf.FixedLenFeature([], tf.int64),'j':tf.FixedLenFeature([], tf.int64),})# 使用上面樣例。這里假設(shè) Example 結(jié)構(gòu)中 i 表示一個樣例的特征向量, # 比如一張圖像的像索矩陣。而 j 表示該樣例對應(yīng)的標(biāo)簽 。 example, label = features['i'], features['j']# 一個 batch 中樣例的個數(shù)。 batch_size = 3 # 文件隊列中最多可以存儲的樣例個數(shù) capacity = 1000 + 3 * batch_size# 使用 tf.train.batch 函數(shù)來組合樣例。[example, label]參數(shù)給出需組合的元素. # 當(dāng)隊列長度等于容量時,TF將暫停入隊操作,而只是等待元索出隊。 # 當(dāng)元素個數(shù)小于容量時, TF將自動重新啟動入隊操作。 example_batch, label_batch = tf.train.batch([example, label], batch_size=batch_size, capacity=capacity)with tf.Session() as sess:# tf.initialize_all_variables().run()tf.local_variables_initializer().run()coord = tf.train.Coordinator()threads = tf.train.start_queue_runners(sess=sess, coord=coord)# 獲取并打印組合之后的樣例。在真實問題中,這個輸出一般會作為神經(jīng)網(wǎng)絡(luò)的輸入。for i in range(2):cur_example_batch, cur_label_batch = sess.run([example_batch, label_batch])print(cur_example_batch, cur_label_batch)coord.request_stop()coord.join(threads)報錯:因為使用match_filenames_once需要用local_variables_initializer初始化局部變量(local variables)沒有初始化
- 局部變量初始化:tf.local_variables_initializer().run()
輸出結(jié)果
[0 0 1] [0 1 0] [1 0 0] [1 0 1] # tf.train.batch 函數(shù)可以將單個的數(shù)據(jù)組織成3個一組的batch # 在example,lable中讀到的數(shù)據(jù)依次為: example:0, lable:0 example:0, lable:1 example:1, lable:0 example:1, lable:1 # 這是因為函數(shù)不會隨機(jī)打亂順序,所以組合之后得到的數(shù)據(jù)組合成了上面給出的輸出(2)tf.train.shuffle_batch
tf.train.shuffle_batch 代碼示例如下:
import tensorflow as tf# 獲取文件列表。 files = tf.train.match_filenames_once('./datasets/data.tfrecords-*')# 創(chuàng)建文件輸入隊列 filename_queue = tf.train.string_input_producer(files, shuffle=False)# 讀取并解析Example reader = tf.TFRecordReader() _, seraialized_example = reader.read(filename_queue) features = tf.parse_single_example(seraialized_example,features={'i':tf.FixedLenFeature([], tf.int64),'j':tf.FixedLenFeature([], tf.int64),})# i代表特征向量,j代表標(biāo)簽 example, label = features['i'], features['j']# 一個 batch 中樣例的個數(shù)。 batch_size = 3 # 文件隊列中最多可以存儲的樣例個數(shù) capacity = 1000 + 3 * batch_size# 組合樣例。 # `min_after_dequeue` 是該函數(shù)特有的參數(shù),參數(shù)限制了出隊時隊列中元素的最少個數(shù), # 但當(dāng)隊列元素個數(shù)太少時,隨機(jī)的意義就不大了 example_batch, label_batch = tf.train.shuffle_batch([example, label], batch_size=batch_size, capacity=capacity, min_after_dequeue=30)with tf.Session() as sess:# 使用match_filenames_once需要用local_variables_initializer初始化一些變量tf.local_variables_initializer().run()# 用Coordinator協(xié)同線程,并啟動線程coord = tf.train.Coordinator()threads = tf.train.start_queue_runners(sess=sess, coord=coord)# 獲取并打印組合之后的樣例。在真實問題中,這個輸出一般會作為神經(jīng)網(wǎng)絡(luò)的輸入。for i in range(2):cur_example_batch, cur_label_batch = sess.run([example_batch, label_batch])print(cur_example_batch, cur_label_batch)coord.request_stop()coord.join(threads)輸出結(jié)果
[0 0 0] [1 1 0] [0 0 1] [1 1 0]4、輸入數(shù)據(jù)處理框架
框架主要是三方面的內(nèi)容:
- TFRecord 輸入數(shù)據(jù)格式
- 圖像數(shù)據(jù)處理
- 多線程輸入數(shù)據(jù)處理
5、總結(jié)
對于輸入數(shù)據(jù)的處理,大體上流程都差不多,可以歸結(jié)如下:
四、數(shù)據(jù)集(DataSet)
TF核心組件:tf.data。數(shù)據(jù)集框架中,每一個數(shù)據(jù)來源被抽象為一個數(shù)據(jù)集,以數(shù)據(jù)集為基本對象,方便地進(jìn)行 batching、隨機(jī)打亂( shuffle)等操作。
1、數(shù)據(jù)集的基本使用方法
由于訓(xùn)練數(shù)據(jù)通常無法全部寫入內(nèi)存中,從數(shù)據(jù)集中讀取數(shù)據(jù)時需要使用一個迭代器( iterator)按順序進(jìn)行讀取,數(shù)據(jù)集也是計算圖上的一個節(jié)點。
(1)tensor -> 數(shù)據(jù)集
示例:從一個張量創(chuàng)建一個數(shù)據(jù)集,遍歷這個數(shù)據(jù)集,并對每個輸入輸出 y=x2y=x^2y=x2 的值。
import tensorflow as tf# 1.定義數(shù)據(jù)集構(gòu)造方法:從-個數(shù)組創(chuàng)建數(shù)據(jù)集 input_data = [1, 2, 3, 5, 8] dataset = tf.data.Dataset.from_tensor_slices(input_data)# 2.定義一個迭代器用于遍歷數(shù)據(jù)集 # 因為數(shù)據(jù)集定義時未用placeholder作為參數(shù),則此處用one_shot_iterator() iterator = dataset.make_one_shot_iterator() # 3.返回一個輸入數(shù)據(jù)的張量 x = iterator.get_next() y = x * xwith tf.Session() as sess:for i in range(len(input_data)):print(sess.run(y))輸出結(jié)果
1 4 9 25 64數(shù)據(jù)集讀取數(shù)據(jù)有三個基本步驟:
(2)文本文件 -> 數(shù)據(jù)集
tf.data.TextLineDataset()函數(shù)
TXT_FILE1 = './datasets/Text/1.txt' TXT_FILE2 = './datasets/Text/2.txt'input_files = [TXT_FILE1, TXT_FILE2] dataset = tf.data.TextLineDataset(input_files)# 定義一個迭代器用于遍歷數(shù)據(jù)集 iterator = dataset.make_one_shot_iterator() # 返回一個字符串類型的張量,代表文件中的一行。 x = iterator.get_next() with tf.Session() as sess:# 循環(huán)的次數(shù)為文檔中總共的行數(shù),否則會報錯for i in range(3):print(sess.run(x))結(jié)果輸出
b'HELLO' b'MY FRIENDS!' b'AHHAHA'(3)TFRecord -> 數(shù)據(jù)集
TFRecordDataset()函數(shù)讀取 TFRecord 形式的數(shù)據(jù)(常為圖像數(shù)據(jù)),因為每個 TFRecord 都有自己不同的 feature 格式,因此在讀取時,需提供一個 parse 函數(shù)進(jìn)行解析。
# 解析一個 TFRecord 的方法。record是從文件中讀取的一個樣例。 def parser(record):# 解析讀入的一個樣例。features = tf.parse_single_example(record,features={'feat1': tf.FixedLenFeature([], tf.int64),'feat2': tf.FixedLenFeature([], tf.inp64)})return features['feat1'], features['feat2']# 從 TFRecord 文件創(chuàng)建數(shù)據(jù)集 FILE1_PATH = '' FILE2_PATH = '' input_files = [FILE1_PATH, FILE2_PATH] dataset = tf.dataset.TFRecordDataset(input_files) # 二進(jìn)制的數(shù)據(jù)# map()函數(shù)表示對數(shù)據(jù)集中的每一條數(shù)據(jù)進(jìn)行調(diào)用相應(yīng)方法。 # 通過 map() 來調(diào)用 parser() 對二進(jìn)制數(shù)據(jù)進(jìn)行解析。 dataset = dataset.map(parser)# 定義一個迭代器用于遍歷數(shù)據(jù)集 iterator = Dataset.make_one_shot_iterator()# feat1, feat2 是 parser() 返回的一維int64型張量,可以作為輸入用于進(jìn)一步的計算。feat1, feat2 = iterator.get_next()with tf.Session() as sess:for i in range(10):f1, f2 = sess.run([feat1, feat2])注意:
- 使用 one shot_iterator 時,數(shù)據(jù)集的所有參數(shù)必須已經(jīng)確定,因此不需要特別的初始化過程。
- 使用 placeholder 初始化數(shù)據(jù)集,則需用 initializable_iterator 動態(tài)初始化數(shù)據(jù)集。如下所示
上述示例通過使用placeholder和feed_dict的方式傳給數(shù)據(jù)集。
注意:上面的循環(huán)體不是指定循環(huán)10次sess.run,而是使用while(True)try-except的形式來將所有數(shù)據(jù)遍歷一遍(即一個epoch),因為在動態(tài)指定輸入數(shù)據(jù)時,不同數(shù)據(jù)來源的數(shù)據(jù)量大小難以預(yù)知,而這個方法我們不必提前知道數(shù)據(jù)量的精確大小。
2、數(shù)據(jù)集的高層操作
(1)map操作
dataset = dataset.map(parser)map(parser) 表示對數(shù)據(jù)集中每一條數(shù)據(jù)調(diào)用參數(shù)中指定的 parser 方法。對每一條數(shù)據(jù)進(jìn)行處理后,map將處理后的數(shù)據(jù)包裝成一個新的數(shù)據(jù)集返回。
map可用于對數(shù)據(jù)的任何預(yù)處理操作。
dataset = dataset.map(lambda x: preprocess_for_train(x, image_size, image_size, None))lambda表達(dá)式的作用:將原來有4個參數(shù)的函數(shù)轉(zhuǎn)化為只有1個參數(shù)的函數(shù)。第一個參數(shù)decoded_image 變成了 lambda 表達(dá)式中的 x ,后3個參數(shù)都被換成了具體的數(shù)值。注意這里的 image_size 是一個變量,有具體取值,該值需要在程序的上文中給出。
在返回的新的數(shù)據(jù)集上,可以直接繼續(xù)調(diào)用其他高層操作,比如預(yù)處理、shuffle、batch等操作。==》更加干凈簡潔
(2)shuffle 和 batch 操作
dataset = dataset.shuffle(buffer_size) # 隨機(jī)打亂順序 dataset = dataset.batch(batch_size) # 將數(shù)據(jù)組合成batch其中buffer_size相當(dāng)于tf.train.shuffle_batch 的 min_after_dequeue 參數(shù)。shuffle 算法在內(nèi)部使用一個緩沖區(qū)中保存 buffer_size 條數(shù)據(jù),每讀入一條新數(shù)據(jù)時,從這個緩沖區(qū)中隨機(jī)選擇一條數(shù)據(jù)進(jìn)行輸出。緩沖區(qū)的大小越大,隨機(jī)的性能越好,但占用的內(nèi)存也越多 。
batch方法中的 batch_size 參數(shù)表示要輸出的每個 batch 由多少條數(shù)據(jù)組成
(3)repeat 操作
作用:將數(shù)據(jù)集中的數(shù)據(jù)復(fù)制多份,其中每一份數(shù)據(jù)被稱為一個 epoch。
dataset = dataset.repeat(N) # 將數(shù)據(jù)集重復(fù) N 份注意:如果數(shù)據(jù)集在 repeat 前己經(jīng)進(jìn)行了 shuffle 操作,輸出的每個 epoch 中隨機(jī) shuffle 的結(jié)果并不會相同 。repeat 和 map 、shuffle 、batch 等操作一樣,都只是計算圖中的一個計算節(jié)點 。repeat 只代表重復(fù)相同的處理過程,并不會記錄前一個epoch的處理結(jié)果。
(4)其他操作
- concatenate():將兩個數(shù)據(jù)集順序連接起來;
- take(N):從數(shù)據(jù)集中讀取前 N 項數(shù)據(jù);
- skip(N):在數(shù)據(jù)集中跳過前 N 項數(shù)據(jù);
- flap_map():從多個數(shù)據(jù)集中輪流讀取數(shù)據(jù)。
(5)示例
從文件中讀取原始數(shù)據(jù),進(jìn)行預(yù)處理、shuffle、batching 等操作,并通過 repeat 方法訓(xùn)練多個 epoch。此外,測試集和訓(xùn)練集做了不同的預(yù)處理。在訓(xùn)練時,調(diào)用上小節(jié)中的 preprocess_for_train 方法對圖像進(jìn)行隨機(jī)反轉(zhuǎn)等預(yù)處理操作;而在測試時,測試數(shù)據(jù)以原本的樣子直接輸入測試。
對數(shù)據(jù)依次進(jìn)行預(yù)處理、shuffle、batching操作
import tensorflow as tf import tempfile# 1. 列舉輸入文件 TRAIN_PATH = 'output.tfrecords' TEST_PATH = 'output_test.tfrecords' train_files = tf.train.match_filenames_once(TRAIN_PATH) test_files = tf.train.match_filenames_once(TEST_PATH)# 2. 定義parser方法TFRecord中解析數(shù)據(jù) # 解析一個TFRecord的方法。 def parser(record):features = tf.parse_single_example(record,features={'image_raw':tf.FixedLenFeature([],tf.string),'pixels':tf.FixedLenFeature([],tf.int64),'label':tf.FixedLenFeature([],tf.int64)})decoded_images = tf.decode_raw(features['image_raw'],tf.uint8)retyped_images = tf.cast(decoded_images, tf.float32)images = tf.reshape(retyped_images, [784])labels = tf.cast(features['label'],tf.int32)#pixels = tf.cast(features['pixels'],tf.int32)return images, labelsimage = 299 # 定義神經(jīng)網(wǎng)絡(luò)輸入層圖片的大小 batch_size = 100 # 定義組合數(shù)據(jù) batch 的大小 shuffle_buffer = 10000 # 定義隨機(jī)打亂數(shù)據(jù)時buffer大小# 定義讀取訓(xùn)練集和測試集 dataset = tf.data.TFRecordDataset(train_files) dataset = dataset.map(parser)# 對數(shù)據(jù)依次進(jìn)行預(yù)處理、shuffle、batching操作 # dataset = dataset.map( # lambda image, label: ( # preprocess_for_train(image, image_size, image_size, None), label)) dataset = dataset.shuffle(shuffle_buffer).batch(batch_size)# 重復(fù)NUM_EPOCHS 個epoch NUM_EPOCHS = 10 dataset = dataset.repeat(NUM_EPOCHS)# 定義數(shù)據(jù)集迭代器。 iterator = dataset.make_initializable_iterator() image_batch, label_batch = iterator.get_next()# 4. 定義神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)和優(yōu)化過程 def inference(input_tensor, weights1, biases1, weights2, biases2):layer1 = tf.nn.relu(tf.matmul(input_tensor, weights1) + biases1)return tf.matmul(layer1, weights2) + biases2INPUT_NODE = 784 OUTPUT_NODE = 10 LAYER1_NODE = 500 REGULARAZTION_RATE = 0.0001 TRAINING_STEP = 5000weights1 = tf.Variable(tf.truncated_normal([INPUT_NODE, LAYER1_NODE], stddev=0.1)) biases1 = tf.Variable(tf.constant(0.1, shape=[LAYER1_NODE])) weights2 = tf.Variable(tf.truncated_normal([LAYER1_NODE, OUTPUT_NODE], stddev=0.1)) biases2 = tf.Variable(tf.constant(0.1, shape=[OUTPUT_NODE]))y = inference(image_batch, weights1, biases1, weights2, biases2)# 計算交叉熵及其平均值 cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=label_batch) cross_entropy_mean = tf.reduce_mean(cross_entropy)# 損失函數(shù)的計算 regularizer = tf.contrib.layers.l2_regularizer(REGULARAZTION_RATE) regularaztion = regularizer(weights1) + regularizer(weights2) loss = cross_entropy_mean + regularaztion# 優(yōu)化損失函數(shù) train_step = tf.train.GradientDescentOptimizer(0.01).minimize(loss)# 5. 定義測試用數(shù)據(jù)集 # 定義測試用的Dataset。 test_dataset = tf.data.TFRecordDataset(test_files) test_dataset = test_dataset.map(parser) test_dataset = test_dataset.batch(batch_size)# 定義測試數(shù)據(jù)上的迭代器 test_iterator = test_dataset.make_initializable_iterator() test_image_batch, test_label_batch = test_iterator.get_next()# 定義測試數(shù)據(jù)上的預(yù)測結(jié)果 test_logit = inference(test_image_batch, weights1, biases1, weights2, biases2) predictions = tf.argmax(test_logit, axis=-1, output_type=tf.int32)# 聲明會話并運(yùn)行神經(jīng)網(wǎng)絡(luò)的優(yōu)化過程。 with tf.Session() as sess:# 初始化變量。sess.run((tf.global_variables_initializer(),tf.local_variables_initializer()))# 初始化訓(xùn)練數(shù)據(jù)的迭代器。sess.run(iterator.initializer)# 循環(huán)進(jìn)行訓(xùn)練,直到數(shù)據(jù)集完成輸入、拋出OutOfRangeError錯誤。while True:try:sess.run(train_step)except tf.errors.OutOfRangeError as e:break# 初始化測試數(shù)據(jù)的迭代器。test_results = []test_labels = []sess.run(test_iterator.initializer)while True:try:pred, label = sess.run([predictions, test_label_batch])test_results.extend(pred)test_labels.extend(label)except tf.errors.OutOfRangeError as e:break# 計算準(zhǔn)確率 correct = [float(y==y_) for (y, y_) in zip(test_results, test_labels)] accuarcy = sum(correct)/len(correct) print("Test accuarcy is: ", accuarcy)輸出結(jié)果
Test accuarcy is: 0.9005總結(jié)
以上是生活随笔為你收集整理的【TensorFlow】笔记5:图像数据处理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【TensorFlow】笔记4:图像识别
- 下一篇: 【NLP实战】Task1 数据集探索