使用tf.data創建本身的數據集,並使用CNN卷積神經網絡實現對衛星圖像的二分類問題。html
數據下載連接:https://pan.baidu.com/s/141zi1BvDU6rHsq5VKgRl4Q 提取碼:2kbc
python
使用tf.data將已有的圖片打上標籤,並將數據分爲訓練集與測試集用於訓練神經網絡。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轉換成字符串
首先導入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 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建立了一個自定義的數據集,而且隨時能夠放到神經網絡中進行訓練了!(完結撒花*★,°*:.☆( ̄▽ ̄)/$:*.°★*),撒個鬼,還沒訓練呢 !
上一篇博客中已經講過了如何構建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(* ̄▽ ̄*)ブ❀❀❀