Tensorflow學習筆記No.5

tf.data卷積神經網絡綜合應用實例

使用tf.data創建本身的數據集,並使用CNN卷積神經網絡實現對衛星圖像的二分類問題。html

數據下載連接:https://pan.baidu.com/s/141zi1BvDU6rHsq5VKgRl4Q  提取碼:2kbc
python

1.使用tf.data創建數據集

使用tf.data將已有的圖片打上標籤,並將數據分爲訓練集與測試集用於訓練神經網絡。windows

下面將逐步介紹如何創建數據集。網絡

1.1讀取windows下的文件路徑

首先,頭文件走一波(python中應該叫導入模塊)dom

1 import tensorflow as tf
2 import numpy as np
3 import matplotlib.pyplot as plt
4 import pathlib
5 %matplotlib inline

注:我一直在使用jupyter notebook編寫程序,展現的代碼也是基於notebook,使用本文中提供的代碼時也儘可能使用notebook,不使用notebook的小夥伴請去掉第五行代碼!(想了解notebook的小夥伴能夠自行百度或者參考我之前的博客Tensorflow學習筆記No.0函數

模塊導入完成後,咱們先定位到數據文件所在的目錄,並將路徑存入data_dir變量中。學習

1 data_dir = 'C:/Users/18083/Desktop/新建文件夾/opencv/衛星圖像識別數據/2_class'

注意,路徑要使用本身電腦上'2_class'文件所在的路徑,而且路徑分隔符要使用'/'而非'\'測試

獲取到文件的路徑後咱們使用pathlib.Path()方法,將data_dir從字符串變爲一個WindowsPath類型的數據,便於後續的子目錄讀取操做ui

1 data_root = pathlib.Path(data_dir)

這是在notebook中的執行結果,咱們看到data_root已是一個WindowPath類型的文件了。編碼

 

 

 咱們可使用如下代碼來便利data_root目錄下的全部子目錄

1 for item in data_root.iterdir():
2     print(item)
3 #遍歷data_root目錄下的全部子目錄

運行結果以下:

 

 

 咱們能夠看到'2_class'目錄下全部的子目錄,也就是咱們即將要進行分類的兩類圖片的儲存位置。

下面咱們就着手獲取咱們所要進行分類的所有圖片的路徑。

對WindowPath對象使用.glob()方法來遍歷目錄下特定類型的文件或文件夾。

1 all_image_path = list(data_root.glob('*/*'))
2 #獲取全部路徑,'*/*'指全部子目錄下的全部文件,並轉換爲list

咱們建立一個all_image_path變量來存儲全部的圖片的路徑。

對data_root使用.glob()方法,參數指定爲'*/*',‘*'指代該目錄下的所有文件,也就是'2_class'文件夾下的'airplane'文件夾和'lake'文件夾,'*/*'就指代了data_root目錄的全部子目錄中的全部文件,也就是咱們要獲取的圖片文件。並使用list()將其轉換爲列表便於存儲。

隨後咱們使用列表推導式將其轉換爲字符串,讓它能夠做爲路徑參數從而進行讀取文件內容,即圖片的數據。

1 all_image_path = [str(path) for path in all_image_path]
2 #使用列表推導式將windowpath轉換成字符串

1.2對圖像數據使用label進行類別標記

首先導入random模塊,對路徑進行亂序便於後面將數據分割爲訓練集和測試集。

亂序是爲了防止數據之間產生強依賴關係而影響訓練結果。

1 import random
2 random.shuffle(all_image_path)
3 #對數據進行亂序處理
4 image_count = len(all_image_path)
5 #圖像總數
6 image_count

對數據進行亂序處理並得到圖像總數。

而後咱們從子目錄中得到標籤名。

1 label_name = sorted([item.name for item in data_root.glob('*/')])
2 #從子目錄中提取標籤名

使用列表推導式將data_root下的全部子目錄名字存入列表中,並按照字典序排序。

結果以下:

 

 

這樣咱們便得到了兩個類別的名稱,也就是存儲圖片的文件夾的名字。

而後咱們使用這個列表構建一個字典,將名字映射爲分類的類別編號0,1便於神經網絡的分類器進行分類。

1 label_to_indx = dict((name, indx) for indx, name in enumerate(label_name))
2 #enumerate()返回列表的下標和內容
3 #將label編號 並轉爲字典型

獲得一個字典:

下一步,咱們使用這個字典和以前獲得的路徑,將全部的圖片分類,編號0或1。

1 all_image_label = [label_to_indx[pathlib.Path(p).parent.name] for p in all_image_path]
2 #從all_iamge_path中讀取路徑字符串,轉換爲windowpath對象
3 #調用.parent.name方法得到父親目錄的名字,並用字典映射爲對應編號

咱們使用path.Path()方法將路徑字符串p轉換成一個可執行對象,並調用.parent.name方法來得到它的父目錄的名字,並使用剛剛創建的字典對其進行編號。

隨機取三張圖片驗證一下all_image_label標籤的正確性:

1 import IPython.display as display

導入模塊用於經過路徑顯示圖片。

1 for n in range(3):#隨機取三張圖片
2     image_indx = random.choice(range(image_count))
3     #從全部圖片中隨機選擇一個編號
4     display.display(display.Image(all_image_path[image_indx]))
5     #經過路徑播放圖片 
6     print(indx_to_label[all_image_label[image_indx]])
7     #path和label一一對應,因此能夠得到圖片的l 經過字典得到圖片名
8     print()

結果以下:

 

 

 標籤與圖片一致

1.3讀取圖片數據並建立數據集

通過了前面許許多多複雜步驟的鋪墊,咱們終於到了讀取數據並建立數據集的時刻!

咱們先對單一的圖片進行讀取~

隨便找個圖片試試水!

1 img_path = all_image_path[0]

經過tf.io.read_file()讀取圖片數據

1 img_raw = tf.io.read_file(img_path)
2 #讀取圖片

注意,此時獲得的img_raw是圖片的16進制編碼,而不是咱們平時所使用的RGB三通道uint8編碼,因此要進行解碼。使用tf.image.decode_image_jpeg()進行解碼,tf.image中提供了針對不一樣類型圖片的多種解碼方式,根據須要自行選擇。

1 img_tensor = tf.image.decode_image(img_raw, channels = 3)
2 #解碼轉換成tensor array

因爲咱們要將0~255的色彩空間映射到0~1的浮點數,因此要將數據從uint8類型轉換爲float32。

1 img_tensor = tf.cast(img_tensor, tf.float32)
2 #轉換數據類型
3 img_tensor = img_tensor / 255
4 #標準化

這樣咱們就處理好了一張圖片。。。

但。。。咱們有整整1400張圖片。。。

不可能這樣一張張的處理下去,因此咱們寫個函數來執行這一操做。

1 def load_preprosess_image(path):
2     img_raw = tf.io.read_file(path)
3     img_tensor = tf.image.decode_jpeg(img_raw, channels = 3)
4     img_tensor = tf.image.resize(img_tensor, [256, 256])
5     #resize_with_crop_or_pad填充與裁剪
6     img_tensor = tf.cast(img_tensor, tf.float32)
7     img = img_tensor / 255
8     return img
9 #加載和預處理圖片

隨後,咱們使用tf.data.Dataset中的.from_tensor_slices()方法,將all_image_path進行切片,變成一個dateset類型的數據,並使用函數和map()將路徑一一映射爲處理好的圖片數據。

1 path_ds = tf.data.Dataset.from_tensor_slices(all_image_path)
2 image_dataset = path_ds.map(load_preprosess_image)

此時,all_image_path與all_image_label還是一一對應的,因此咱們對label也進行切片操做轉換成dataset類型的數據,並用tf.data.Dataset.zip()方法將其合併成爲完整的數據集!(天哪~~!終於建好了!!)

1 label_dataset = tf.data.Dataset.from_tensor_slices(all_image_label)
2 dataset = tf.data.Dataset.zip((image_dataset, label_dataset))

(右側進度條告訴你事情沒有這麼簡單~)

數據集雖然建好了,可是。。。

還要數據集還要分爲訓練集和測試集,還要分別對數據進行一些處理。。。

咱們首先按照4:1的比例將數據分爲訓練集和測試集。

 

1 test_count = int(image_count * 0.2) #280張
2 train_count = image_count - test_count #1120張

 

分配訓練集和測試集的圖片數量,而後對數據集進行分割。

使用.skip()得到後280張做爲測試集

使用.take()得到前1120張做爲訓練集

1 train_dataset = dataset.skip(test_count)
2 test_dataset = dataset.take(test_count)

設置一個batch_size神經網絡防止一次讀取的圖片過分致使內存爆炸,這一點很是重要哦。

1 BATCH_SIZE = 32

對訓練集進行亂序和重複處理,並按照BATCH_SIZE分配好數據。

對測試集分配好BATCH_SIZE便可。

1 train_dataset = train_dataset.shuffle(train_count).repeat().batch(BATCH_SIZE)
2 test_dataset = test_dataset.batch(BATCH_SIZE)

  到這裏,咱們就使用tf.data建立了一個自定義的數據集,而且隨時能夠放到神經網絡中進行訓練了!(完結撒花*★,°*:.☆( ̄▽ ̄)/$:*.°★*),撒個鬼,還沒訓練呢 !

2.使用CNN模型進行預測

  上一篇博客中已經講過了如何構建CNN模型並介紹了須要用到的API,這裏再也不贅述,不瞭解的小夥伴能夠看看上一篇博客(Tensorflow學習筆記No.4.2)

 

 

 

這裏直接上代碼!網絡模型以下:

大體結構爲:每兩層卷積設置一層池化層,最後加入全鏈接層以及分類器進行分類。

 1 model = tf.keras.Sequential()
 2 model.add(tf.keras.layers.Conv2D(64, (3, 3), input_shape = (256, 256, 3), activation = 'relu'))
 3 model.add(tf.keras.layers.Conv2D(64, (3, 3), activation = 'relu'))
 4 model.add(tf.keras.layers.MaxPooling2D())
 5 model.add(tf.keras.layers.Conv2D(128, (3, 3), activation = 'relu'))
 6 model.add(tf.keras.layers.Conv2D(128, (3, 3), activation = 'relu'))
 7 model.add(tf.keras.layers.MaxPooling2D())
 8 model.add(tf.keras.layers.Conv2D(256, (3, 3), activation = 'relu'))
 9 model.add(tf.keras.layers.Conv2D(256, (3, 3), activation = 'relu'))
10 model.add(tf.keras.layers.MaxPooling2D())
11 model.add(tf.keras.layers.Conv2D(512, (3, 3), activation = 'relu'))
12 model.add(tf.keras.layers.MaxPooling2D())
13 model.add(tf.keras.layers.Conv2D(512, (3, 3), activation = 'relu'))
14 model.add(tf.keras.layers.MaxPooling2D())
15 model.add(tf.keras.layers.Conv2D(1024, (3, 3), activation = 'relu'))
16 model.add(tf.keras.layers.GlobalAveragePooling2D())
17 model.add(tf.keras.layers.Dense(1024, activation = 'relu'))
18 model.add(tf.keras.layers.Dense(256, activation = 'relu'))
19 model.add(tf.keras.layers.Dense(1, activation = 'sigmoid'))

模型詳細參數以下:

雖然模型相對複雜,可是咱們的數據量不多,使用GPU訓練起來仍是不慢的。

設置好訓練方式,而後開始訓練!

 

 1 model.compile(optimizer = 'adam',
 2               loss = 'binary_crossentropy',
 3               metrics = ['acc']
 4              )
 5 
 6 steps = train_count // BATCH_SIZE
 7 valid_steps = test_count // BATCH_SIZE
 8 
 9 history = model.fit(train_dataset, epochs = 30, 
10                     steps_per_epoch = steps,
11                     validation_data = test_dataset,
12                     validation_steps = valid_steps
13                    )

 

這裏的steps以及valid_steps對應了BATCH_SIZE,也是爲了防止數據和內存溢出而加入的限制手段。

訓練部分過程以下:

能夠看出模型的擬合效果仍是很是不錯的,在訓練過程當中測試集上的正確率一度達到過98%。

 繪製出訓練過程的圖像:

 

1 plt.plot(history.epoch, history.history.get('acc'), label = 'acc')
2 plt.plot(history.epoch, history.history.get('val_acc'), label = 'val_acc')
3 plt.legend()

 

 

使用.evaluate()方法驗證一下模型準確率。

1 model.evaluate(test_dataset)

 

 

在測試集上的準確率也達到了不錯的95%

最後咱們隨便從數據集中隨便找幾張圖片預測一下~

這裏我用了測試集中的前三張

1 for i in list(model.predict(test_dataset))[:3]:
2     if i < 0.5:
3         print('airplane')
4     else :
5         print('lake')

運行結果:

 

 

 把這三張圖片放出來看看~ 看一下預測的是否正確~

1 test_img = list(test_dataset)[:1]
2 i, j = test_img[0]
3 plt.imshow(i[0])

 

 

1 plt.imshow(i[1])

 

 

1 plt.imshow(i[2])

 

與預測結果一致!

此次咱們的模型預測的很成功!

Tensorflow tf.data與卷積神經網絡綜合實例到這裏就結束了~

完結撒花~!o(* ̄▽ ̄*)ブ❀❀❀ 

相關文章
相關標籤/搜索