【TensorFlow】笔记5:图像数据处理
文章目錄
- 一、TFRecords輸入數據格式
- 1、TFRecord格式介紹
- 2、TFRecord樣例程序
- (1)生成TFRecords文件
- (2)讀取TFRecord文件
- 二、圖像數據處理
- 1、TF圖像處理函數
- (1)圖像編碼處理
- (2)圖像大小處理
- a、通過算法使得新的圖像盡量保存原始圖像上所有的信息。
- b、對圖像裁剪或填充——`tf.image.resize_image_with_crop_or_pad`函數
- c、比例調整圖像大小——`tf.image.entral_crop`函數
- d、剪裁或填充給定區域的圖像——`tf.image.crop_to_bounding_box`函數和`tf.image.pad_to_bounding_box`函數
- (3)圖像翻轉
- (4)圖像色彩調整
- a、調整亮度
- b、調整對比度
- c、調整色相
- d、調整飽和度
- e、圖像標準化
- f、處理標注框(bounding box)
- g、隨機截取圖像
- 2、圖像預處理實例
- 三、多線程輸入數據處理框架
- 1、隊列與多線程
- (1)兩種隊列
- (2)多線程協同方法1:`tf.Coordinator` 類
- (3)多線程協同方法2:`tf.QueueRunner` 類
- 2、輸入文件隊列
- 3、組合訓練數據(batching)
- (1)`tf.train.batch`
- (2)`tf.train.shuffle_batch`
- 4、輸入數據處理框架
- 5、總結
- 四、數據集(DataSet)
- 1、數據集的基本使用方法
- (1)tensor -> 數據集
- (2)文本文件 -> 數據集
- (3)TFRecord -> 數據集
- 2、數據集的高層操作
- (1)map操作
- (2)shuffle 和 batch 操作
- (3)repeat 操作
- (4)其他操作
- (5)示例
目標:如何對圖像數據進行預處理使得訓練得到的神經網絡模型盡可能小地被無關因素所影響。同時,為減小預處理對訓練速度的影響,使用多線程處理。
一、TFRecords輸入數據格式
TF統一存儲數據的格式:TFRecords,二進制文件,可更好利用內存、便于復制和移動,且不需要單獨的標簽文件。
1、TFRecord格式介紹
TFRecord 文件中的數據都是通過 tf.train.Example協議內存塊(Protocol Buffer)(協議內存塊包含了字段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)、實數列表 (FloatList)或者整數列表( Int64List)。
比如將一張解碼前的圖像存為一個字符串,圖像所對應的類別編號存為整數列表。在2中將給出 一個使用 TFRecord 的具體樣例 。
數據存儲:將數據填入到 Example 協議內存塊(protocol buffer),將協議內存塊序列化為一個字符串, 并且通過tf.python_io.TFRecordWriter 寫入到 TFRecords 文件。
從TFRecords文件中讀取數據, 可以使用tf.TFRecordReader的tf.parse_single_example解析器。這個操作可以將Example協議內存塊(protocol buffer)解析為張量。
2、TFRecord樣例程序
(1)生成TFRecords文件
將MNIST輸入數據轉換為TFRecord的格式
import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data import numpy as np# 生成整數型的屬性。 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 # 訓練數據所對應的正確答案,可以作為一個屬性保存在 TFRecord 中 labels = mnist.train.labels # 訓練數據的圖像分辨率,這可以作為 Example 中的一個屬性。 pixels = images.shape[1] num_example = mnist.train.num_example# 輸出 TFRecord 文件的地址。 filename = '/home/jie/Jie/codes/tf/datasets/MNIST_DATA/output.tfrecords' # 創建一個 writer 來寫 TFRecord 文件。 writer = tf.python_io.TFRecordWriter(filename) for index in range(num_example):# 將圖像矩陣轉化成一個字符串。image_raw = images[index].tostring()# 將一個樣例轉化為 Example Protocol Buffer ,并將所有的信息寫入這個數據結構。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()當數據量較大時,也可以將數據寫入多個 TFRecord 文件。
(2)讀取TFRecord文件
import tensorflow as tf# 輸出 TFRecord 文件的地址。 filename = '/home/jie/Jie/codes/tf/datasets/MNIST_DATA/output.tfrecords' # 創建一個 reader 來讀取 TFRecord 文件中的樣例。 reader = tf.TFRecordReader() # tf.train.string_input_producer 創建一個隊列來維護輸入文件列表 filename_queue = tf.train.string_input_producer([filename])# 從文件中讀取一個樣例。也可以使用 read_up_to 函數-次性讀取多個樣例。 _, serialized_example = reader.read(filename_queue) # 解析讀入的一個樣例。若需要解析多個樣例,可用parse_example函數 features = tf.parse_single_example(serialized_example,features={# TensorFlow 提供兩種不同的屬性解析方法。 # 1. tf.FixedLenFeature,所解析的結果為一個 Tensor。# 2. tf.VarLenFeature,所得到的解析結果為SparseTensor,用于處理稀疏數據。# 這里解析數據的格式需要和上面程序可入數據的格式一致。'image_raw':tf.FixedLenFeature([], tf.string),'pixels':tf.FixedLenFeature([], tf.int64),'label':tf.FixedLenFeature([], tf.int64),})# tf.decode_raw 可以將字符串解析成圖像對應的像索數組。 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() # 啟動多線程處理輸入數據 coord = tf.train.Coordinator() threads = tf.train.start_queue_runners(sess=sess, coord=coord)# 每次運行可以讀取 TFRecord 文件中的一個樣例。 # 當所有樣例都讀取完之后,在此樣例中程序會再重頭讀取 for i in range(10):print(sess.run([image, label, pixels]))二、圖像數據處理
目標:可以盡量避免模型受到無關因素的影響。在大部分圖像識別中,通過預處理過程可以提高模型的準確率。
1、TF圖像處理函數
(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' # 讀取圖像的原始數據。 image_raw_data = tf.gfile.FastGFile(IMAGES_PATH, 'rb').read()with tf.Session() as sess:# 對圖像進行 jpeg 的格式解碼從而得到圖像對應的三維矩陣。 # tf.image.decode_png 函數對 png 格式的圖像進行解碼。# 解碼之后的結果為一個張量,在使用它的取值之前需要明確調用運行的過程。img_data = tf.image.decode_jpeg(image_raw_data)print(img_data.eval())# 輸出解碼之后的三維矩陣,上面這一行代碼將輸出以下內容。# 使用 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())輸出結果
[[[ 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)圖像大小處理
由于很多網絡輸入節點的個數是固定的,所以需要先將圖像的大小統一。
圖像大小調整有兩種方法:
a、通過算法使得新的圖像盡量保存原始圖像上所有的信息。
TF的 tf.image.resize_images 函數提供四種不同的方法
# 略去加載原始圖像,定義會話等過程,假設:img_data是已經解碼的圖像。# 1. 首先將圖片數據轉換為實數類型,即將0-255轉化為0.0-1.0范圍內的實數。# 大多數圖像處理 API 支持整數和實數類型的輸入。# 如果輸入是整數類型,這些 API 會在內部將輸入轉化為實數后處理,再將輸出轉化為整數。# 如果有多個處理步驟,在格數和實數之間的反復轉化將導致精度損失,# 因此推薦在圖像處理前將其轉化為實數類型。img_data = tf.image.convert_image_dtype(img_data, dtype=tf.float32)# 通過 tf.image.resize_images 函數調整圖像的大小。# 第一個參數: 原始圖像# 第二個和第三個參數為調整后圖像的大小 # method 參數給出了調整圖像大小的算法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函數
# 通過 tf.image.resize_image_with_crop_or_pad 函數調整圖像的大小。# 第一個參數: 原始圖像# 第二個和第三個參數為調整后的目標圖像大小。# 如果原始圖像的尺寸大于大于目標圖像,則該函數會自動截取y你是圖像中居中的部分。# 若目標圖像大于原始圖像,則會自動在原始圖像的四周填充全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、比例調整圖像大小——tf.image.entral_crop函數
# 第一個參數為原始圖像, 第二個參數為調整比例,這個比例需要是-個(0, 1]的實數central_cropped = tf.image.central_crop(img_data, 0.25)plt.imshow(central_cropped.eval())plt.show()d、剪裁或填充給定區域的圖像——tf.image.crop_to_bounding_box函數和tf.image.pad_to_bounding_box函數
這兩個函數都要求給出的尺寸滿足一定的要求,否則程序會報錯。比如在使用 tf.image.crop_to_bounding_box 函數時, TensorFlow 要求提供的圖像尺寸要大于目標尺寸,也就是要求原始圖像能夠裁剪出目標圖像的大小。
(3)圖像翻轉
功能:上下翻轉、左右翻轉以及沿對角線翻轉
# 將圖像上下翻轉flipped1 = tf.image.flip_up_down(img_data)# 將圖像左右翻轉flipped2 = tf.image.flip_left_right(img_data)# 將圖像沿對角線翻轉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()輸出結果
在很多圖像識別問題中,圖像的翻轉不會影響識別的結果。
==》訓練得到的模型可以識別不同角度的實體
==》隨機翻轉訓練圖像是一種很常用的圖像預處理方式。
(4)圖像色彩調整
調整圖像的亮度、對比度、飽和度和色相,在很多圖像識別應用中不會影響識別結果。
==》隨機調整這些屬性,使得訓練的模型盡可能小地受無關因素的影響。
a、調整亮度
tf.image.adjust_brightness()函數和tf.image.random_brightness()函數
注意:
- 色彩調整的 API 可能導致像素的實數值超出 0.0-1.0 的范圍。在輸出最終圖像前需要將其值截斷在 0.0-1.0 范圍區間,否則不僅圖像無法正常可視化,以此為輸入的神經網絡的訓練質量可能受到影響。
- 若對圖像進行多項處理操作,則截斷應在最后一步
b、調整對比度
tf.image.adjust_contrast()函數和tf.image.random_contrast()函數
# 將圖像的對比度減少到 0.5 倍adjusted1 = tf.image.adjust_contrast(img_data, 0.5) # 將圖像的對比度增加 5 倍adjusted2 = tf.image.adjust_contrast(img_data, 5)# 在[lower, upper] 的范圍隨機調整圖像的對比度。lower = 10upper = 100adjusted3 = tf.image.random_contrast(img_data, lower, upper)c、調整色相
tf.image.adjust_hue函數和tf.image.random_hue函數
# 分別將色相加 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] 的范圍隨機調整圖像的色相。max_delta = 0.5adjusted5 = tf.image.random_hue(img_data, max_delta)注意:max_delta最大為 0.5
d、調整飽和度
tf.image.adjust_saturation函數和tf.image.random_saturation函數
# 將圖像的飽和度-5adjusted1 = tf.image.adjust_saturation(img_data, -5) # 將圖像的飽和度+5adjusted2 = tf.image.adjust_saturation(img_data, 5)# 在[lower, upper] 的范圍隨機調整圖像的飽和度lower = 10upper = 100adjusted3 = tf.image.random_saturation(img_data, lower, upper)e、圖像標準化
tf.image.per_image_standardization() :函數將圖像上的亮度均值變為0,方差變為1。
adjusted = tf.image.per_image_standardization(img_data)f、處理標注框(bounding box)
tf.image.draw_bounding_boxes()函數在圖像中添加標注框。輸入圖像矩陣的數字為實數,且是一個 batch 的數據(四維張量)。所以,繪制圖時,需要tf.reduce_sum()函數進行降維。
# 將圖像縮小一些,這樣可視化能讓標注框更加清楚。img_data = tf.image.resize_images(img_data, [180, 267], method=1)# tf.image.draw_bounding_boxes 函數要求圖像矩陣中的數字為實數.# -> 先將圖像矩陣轉化為實數類型。# 輸入是一個 bacth 的數據(多張圖像組成的四維矩陣),要將解碼之后的圖像矩陣添加一維bacthed = tf.expand_dims(tf.image.convert_image_dtype(img_data, tf.float32), 0)# 給出每一張圖像的所有標注框。一個標注框有 4 個數字,分別代表[Y_min, X_min, Y_max, X_max]# 注意這里給出的數字都是圖像的相對位置。# 比如在 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) # 這里顯示的時候需要進行降維處理plt.imshow(result.eval())plt.show()
注:bounding box位置不準,僅用于學習。
g、隨機截取圖像
隨機截取圖像上有信息含量的部分也是一個提
高模型健壯性( robustness )的一種方式。
==》使得訓練模型不受被識別物體大小的影響。
==》tf.image.sample_distorted_bounding_box()函數
2、圖像預處理實例
在解決真實的圖像識別問題時, 一般會同時使用多種處理方法。
本實例展示:將不同的圖像處理函數結合成一個完成的圖像預處理流程。從圖像片段截取,到圖像大小調整再到圖像翻轉及色彩調整的整個圖像預處理過程。
- 調整亮度、對比度、飽和度和色相的順序會影響最后的結果,所以定義多種不同的順序。
- 注意:數據預處理只針對訓練數據,而非測試數據
輸入:原始訓練圖像
輸出:神經網絡的輸入層
三、多線程輸入數據處理框架
復雜的預處理過程會減慢整個訓練的過程,所以為了避免預處理成為神經網絡模型訓練效率的瓶頸,TF提供多線程處理輸入數據的框架。
經典輸入流程:
1、隊列與多線程
在TF中,隊列也是計算圖上有狀態的節點,修改隊列狀態的操作主要有 Enqueue、EnqueueMany和Dequeue。
(1)兩種隊列
- FIFOQueue:先進先出隊列。
- RandomShuffleQueue:將隊列中的元素打亂,每次出隊列操作得到的是從當前隊列所有元素中隨機選擇的一個。(訓練網絡時希望每次使用的訓練數據盡可能隨機,推薦使用)
輸出結果
0 10 1 11 2TF中,隊列還是異步計算張量取值的一個重要機制。例:多個線程可同時向一個隊列中寫入或讀取元素。
(2)多線程協同方法1:tf.Coordinator 類
tf.Coordinator類:用于協同多個線程一起停止, 并提供了 should_stop、request_stop 和 join 三個函數。
工作過程:
輸出
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。 在這個時候有一個線程退出的條件達到,于是調用了 coord.request_stop 函數來停止所有其他的線程。 然而在打印 Stoping from id: 0 之后,可以看到有線程仍然在輸出。這是因為這些線程已經執行完coord.should_stop的判斷,于是仍然會繼續輸出自己的 ID 。 但在下一輪判斷是否需要停止時將退出線程。 于是在打印一次 ID 之后就不會再有輸出了。
(3)多線程協同方法2:tf.QueueRunner 類
tf.QueueRunner類:用于啟動多個線程來操作同一個隊列,啟動的這些線程可以通過上面介紹的 tf.Coordinator 類來統一管理
# 聲明一個先進先出的隊列,隊列中最多100個元素,類型是實數 queue = tf.FIFOQueue(100, "float") # 定義隊列的入隊操作 enqueue_op = queue.enqueue([tf.random_normal([1])])# 使用 tf.train.QueueRunner 創建多個線程運行隊列的入隊操作。 # 第1個參數:被操作的隊列, # [enqueue_op]*5 需要啟動5個線程,每個線程中運行的是 enqueue_op 操作。 qr = tf.train.QueueRunner(queue, [enqueue_op] * 5)# 將定義過的QueueRunner加入TensorFlow 計算圖上指定的集合。 # tf.train.add_queue_runner 函數沒有指定集合,則加入默認集合 tf.GraphKeys.QUEUE_RUNNERS。 # 下面的函數就是將剛剛定義的 qr 加入默認的 tf.GraphKeys.QUEUE_RUNNERS 集合。 tf.train.add_queue_runner(qr) # 定義出隊操作。 out_tensor = queue.dequeue()with tf.Session() as sess:# 使用 tf.train.Coordinator 來協同啟動的線程。coord = tf.train.Coordinator()# 使用tf.train.QueueRunner時,需要明確調用tf.train.start_queue_runners來啟動所有線程。# 否則因為沒有線程運行入隊操作,當調用出隊操作時,程序會一直等待入隊操作被運行。# tf.train.start_queue_runners 函數會默認啟動tf.GraphKeys.QUEUE_RUNNERS集合中所有的QueueRunner。# 這個函數只支持啟動指定集合中的 QueueRnner,# 所以一般來說 tf.train.add_queue_runner 函數和tf.train.start_queue_runners函數會指定同一個集合。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)輸出結果
-1.475753 -0.27835137 2.24072552、輸入文件隊列
雖然一個TFRecord文件可以存儲多個訓練樣本,但當訓練數據量較大時,可以將數據分成多個TFRecord文件來提高處理效率。
該函數會使用初始化時提供的文件列表創建一個輸入隊列,輸入隊列中的原始元素為文件列表中的所有文件,創建好的輸入隊列可以作為文件讀取函數的參數,每層調用文件讀取函數時,該函數會先判斷當前是否已經有打開的文件可讀,如果沒有或者打開的文件已經讀完,則該函數會從輸入隊列中出隊一個文件并從該文件中讀取數據。
- 當 shuffle=True 時,文件在加入隊列之前會被打亂順序,所以出隊順序也是隨機的;
- 隨機打亂文件順序以及加入輸入隊列的過程是一個單獨的線程,不會影響獲取文件的速度。
- 當輸入隊列中的所有文件都被處理完之后,會將初始化時提供的文件列表中的文件全部重新加入隊列。
- num_epochs:限制加載初始文件列表的最大輪數
- 當設置為1時,計算完一輪之后,程序將自動停止。
- 神經網絡模型測試時,所有測試數據僅僅需要使用一次即可,所以將其設置為1。
每一個文件中存儲了兩個樣例 。
生成樣例之后,以下代碼展示了兩個函數的使用方法
# 使用 tf.train.match_filenames_once 函數獲取文件列表。 files = tf.train.match_filenames_once('./datasets/data.tfrecords-*')# 通過 tf.train.string_input_producer 函數創建輸入隊列. # 輸入隊列巾的中的文件列表為tf.train.string_input_producer函數獲取的文件列表。 # 參數設置:shuffle為False,在一般解決真實問題時,shuffle設置為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 函數時前要初始化一些變量 。tf.local_variables_initializer().run()print(sess.run(files))# 聲明 tf.train.Coordinator 類來協同不同線程,并啟動線程。coord = tf.train.Coordinator()threads = tf.train.start_queue_runners(sess=sess, coord=coord)# 多次執行技取數據的操作。for i in range(6):print(sess.run([features['i'], features['j']]))coord.request_stop()coord.join(threads)輸出結果
[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、組合訓練數據(batching)
將多個輸入樣例組織成一個batch可以提高模型訓練的效率,所以在得到單個樣例的預處理結果之后,還需要將其組織成batch,再提供給神經網絡的輸入層。
(1)tf.train.batch
import tensorflow as tf# 使用 tf.train.match_filenames_once 函數獲取文件列表。 files = tf.train.match_filenames_once('./datasets/data.tfrecords-*')# 通過 tf.train.string_input_producer 函數創建輸入隊列. # 輸入隊列巾的中的文件列表為tf.train.string_input_producer函數獲取的文件列表。 # 參數設置:shuffle為False,在一般解決真實問題時,shuffle設置為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),})# 使用上面樣例。這里假設 Example 結構中 i 表示一個樣例的特征向量, # 比如一張圖像的像索矩陣。而 j 表示該樣例對應的標簽 。 example, label = features['i'], features['j']# 一個 batch 中樣例的個數。 batch_size = 3 # 文件隊列中最多可以存儲的樣例個數 capacity = 1000 + 3 * batch_size# 使用 tf.train.batch 函數來組合樣例。[example, label]參數給出需組合的元素. # 當隊列長度等于容量時,TF將暫停入隊操作,而只是等待元索出隊。 # 當元素個數小于容量時, 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)# 獲取并打印組合之后的樣例。在真實問題中,這個輸出一般會作為神經網絡的輸入。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()
輸出結果
[0 0 1] [0 1 0] [1 0 0] [1 0 1] # tf.train.batch 函數可以將單個的數據組織成3個一組的batch # 在example,lable中讀到的數據依次為: example:0, lable:0 example:0, lable:1 example:1, lable:0 example:1, lable:1 # 這是因為函數不會隨機打亂順序,所以組合之后得到的數據組合成了上面給出的輸出(2)tf.train.shuffle_batch
tf.train.shuffle_batch 代碼示例如下:
import tensorflow as tf# 獲取文件列表。 files = tf.train.match_filenames_once('./datasets/data.tfrecords-*')# 創建文件輸入隊列 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代表標簽 example, label = features['i'], features['j']# 一個 batch 中樣例的個數。 batch_size = 3 # 文件隊列中最多可以存儲的樣例個數 capacity = 1000 + 3 * batch_size# 組合樣例。 # `min_after_dequeue` 是該函數特有的參數,參數限制了出隊時隊列中元素的最少個數, # 但當隊列元素個數太少時,隨機的意義就不大了 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協同線程,并啟動線程coord = tf.train.Coordinator()threads = tf.train.start_queue_runners(sess=sess, coord=coord)# 獲取并打印組合之后的樣例。在真實問題中,這個輸出一般會作為神經網絡的輸入。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)輸出結果
[0 0 0] [1 1 0] [0 0 1] [1 1 0]4、輸入數據處理框架
框架主要是三方面的內容:
- TFRecord 輸入數據格式
- 圖像數據處理
- 多線程輸入數據處理
5、總結
對于輸入數據的處理,大體上流程都差不多,可以歸結如下:
四、數據集(DataSet)
TF核心組件:tf.data。數據集框架中,每一個數據來源被抽象為一個數據集,以數據集為基本對象,方便地進行 batching、隨機打亂( shuffle)等操作。
1、數據集的基本使用方法
由于訓練數據通常無法全部寫入內存中,從數據集中讀取數據時需要使用一個迭代器( iterator)按順序進行讀取,數據集也是計算圖上的一個節點。
(1)tensor -> 數據集
示例:從一個張量創建一個數據集,遍歷這個數據集,并對每個輸入輸出 y=x2y=x^2y=x2 的值。
import tensorflow as tf# 1.定義數據集構造方法:從-個數組創建數據集 input_data = [1, 2, 3, 5, 8] dataset = tf.data.Dataset.from_tensor_slices(input_data)# 2.定義一個迭代器用于遍歷數據集 # 因為數據集定義時未用placeholder作為參數,則此處用one_shot_iterator() iterator = dataset.make_one_shot_iterator() # 3.返回一個輸入數據的張量 x = iterator.get_next() y = x * xwith tf.Session() as sess:for i in range(len(input_data)):print(sess.run(y))輸出結果
1 4 9 25 64數據集讀取數據有三個基本步驟:
(2)文本文件 -> 數據集
tf.data.TextLineDataset()函數
TXT_FILE1 = './datasets/Text/1.txt' TXT_FILE2 = './datasets/Text/2.txt'input_files = [TXT_FILE1, TXT_FILE2] dataset = tf.data.TextLineDataset(input_files)# 定義一個迭代器用于遍歷數據集 iterator = dataset.make_one_shot_iterator() # 返回一個字符串類型的張量,代表文件中的一行。 x = iterator.get_next() with tf.Session() as sess:# 循環的次數為文檔中總共的行數,否則會報錯for i in range(3):print(sess.run(x))結果輸出
b'HELLO' b'MY FRIENDS!' b'AHHAHA'(3)TFRecord -> 數據集
TFRecordDataset()函數讀取 TFRecord 形式的數據(常為圖像數據),因為每個 TFRecord 都有自己不同的 feature 格式,因此在讀取時,需提供一個 parse 函數進行解析。
# 解析一個 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 文件創建數據集 FILE1_PATH = '' FILE2_PATH = '' input_files = [FILE1_PATH, FILE2_PATH] dataset = tf.dataset.TFRecordDataset(input_files) # 二進制的數據# map()函數表示對數據集中的每一條數據進行調用相應方法。 # 通過 map() 來調用 parser() 對二進制數據進行解析。 dataset = dataset.map(parser)# 定義一個迭代器用于遍歷數據集 iterator = Dataset.make_one_shot_iterator()# feat1, feat2 是 parser() 返回的一維int64型張量,可以作為輸入用于進一步的計算。feat1, feat2 = iterator.get_next()with tf.Session() as sess:for i in range(10):f1, f2 = sess.run([feat1, feat2])注意:
- 使用 one shot_iterator 時,數據集的所有參數必須已經確定,因此不需要特別的初始化過程。
- 使用 placeholder 初始化數據集,則需用 initializable_iterator 動態初始化數據集。如下所示
上述示例通過使用placeholder和feed_dict的方式傳給數據集。
注意:上面的循環體不是指定循環10次sess.run,而是使用while(True)try-except的形式來將所有數據遍歷一遍(即一個epoch),因為在動態指定輸入數據時,不同數據來源的數據量大小難以預知,而這個方法我們不必提前知道數據量的精確大小。
2、數據集的高層操作
(1)map操作
dataset = dataset.map(parser)map(parser) 表示對數據集中每一條數據調用參數中指定的 parser 方法。對每一條數據進行處理后,map將處理后的數據包裝成一個新的數據集返回。
map可用于對數據的任何預處理操作。
dataset = dataset.map(lambda x: preprocess_for_train(x, image_size, image_size, None))lambda表達式的作用:將原來有4個參數的函數轉化為只有1個參數的函數。第一個參數decoded_image 變成了 lambda 表達式中的 x ,后3個參數都被換成了具體的數值。注意這里的 image_size 是一個變量,有具體取值,該值需要在程序的上文中給出。
在返回的新的數據集上,可以直接繼續調用其他高層操作,比如預處理、shuffle、batch等操作。==》更加干凈簡潔
(2)shuffle 和 batch 操作
dataset = dataset.shuffle(buffer_size) # 隨機打亂順序 dataset = dataset.batch(batch_size) # 將數據組合成batch其中buffer_size相當于tf.train.shuffle_batch 的 min_after_dequeue 參數。shuffle 算法在內部使用一個緩沖區中保存 buffer_size 條數據,每讀入一條新數據時,從這個緩沖區中隨機選擇一條數據進行輸出。緩沖區的大小越大,隨機的性能越好,但占用的內存也越多 。
batch方法中的 batch_size 參數表示要輸出的每個 batch 由多少條數據組成
(3)repeat 操作
作用:將數據集中的數據復制多份,其中每一份數據被稱為一個 epoch。
dataset = dataset.repeat(N) # 將數據集重復 N 份注意:如果數據集在 repeat 前己經進行了 shuffle 操作,輸出的每個 epoch 中隨機 shuffle 的結果并不會相同 。repeat 和 map 、shuffle 、batch 等操作一樣,都只是計算圖中的一個計算節點 。repeat 只代表重復相同的處理過程,并不會記錄前一個epoch的處理結果。
(4)其他操作
- concatenate():將兩個數據集順序連接起來;
- take(N):從數據集中讀取前 N 項數據;
- skip(N):在數據集中跳過前 N 項數據;
- flap_map():從多個數據集中輪流讀取數據。
(5)示例
從文件中讀取原始數據,進行預處理、shuffle、batching 等操作,并通過 repeat 方法訓練多個 epoch。此外,測試集和訓練集做了不同的預處理。在訓練時,調用上小節中的 preprocess_for_train 方法對圖像進行隨機反轉等預處理操作;而在測試時,測試數據以原本的樣子直接輸入測試。
對數據依次進行預處理、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中解析數據 # 解析一個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 # 定義神經網絡輸入層圖片的大小 batch_size = 100 # 定義組合數據 batch 的大小 shuffle_buffer = 10000 # 定義隨機打亂數據時buffer大小# 定義讀取訓練集和測試集 dataset = tf.data.TFRecordDataset(train_files) dataset = dataset.map(parser)# 對數據依次進行預處理、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)# 重復NUM_EPOCHS 個epoch NUM_EPOCHS = 10 dataset = dataset.repeat(NUM_EPOCHS)# 定義數據集迭代器。 iterator = dataset.make_initializable_iterator() image_batch, label_batch = iterator.get_next()# 4. 定義神經網絡結構和優化過程 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)# 損失函數的計算 regularizer = tf.contrib.layers.l2_regularizer(REGULARAZTION_RATE) regularaztion = regularizer(weights1) + regularizer(weights2) loss = cross_entropy_mean + regularaztion# 優化損失函數 train_step = tf.train.GradientDescentOptimizer(0.01).minimize(loss)# 5. 定義測試用數據集 # 定義測試用的Dataset。 test_dataset = tf.data.TFRecordDataset(test_files) test_dataset = test_dataset.map(parser) test_dataset = test_dataset.batch(batch_size)# 定義測試數據上的迭代器 test_iterator = test_dataset.make_initializable_iterator() test_image_batch, test_label_batch = test_iterator.get_next()# 定義測試數據上的預測結果 test_logit = inference(test_image_batch, weights1, biases1, weights2, biases2) predictions = tf.argmax(test_logit, axis=-1, output_type=tf.int32)# 聲明會話并運行神經網絡的優化過程。 with tf.Session() as sess:# 初始化變量。sess.run((tf.global_variables_initializer(),tf.local_variables_initializer()))# 初始化訓練數據的迭代器。sess.run(iterator.initializer)# 循環進行訓練,直到數據集完成輸入、拋出OutOfRangeError錯誤。while True:try:sess.run(train_step)except tf.errors.OutOfRangeError as e:break# 初始化測試數據的迭代器。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# 計算準確率 correct = [float(y==y_) for (y, y_) in zip(test_results, test_labels)] accuarcy = sum(correct)/len(correct) print("Test accuarcy is: ", accuarcy)輸出結果
Test accuarcy is: 0.9005總結
以上是生活随笔為你收集整理的【TensorFlow】笔记5:图像数据处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【TensorFlow】笔记4:图像识别
- 下一篇: 【NLP实战】Task1 数据集探索