利用TensorFlow實現對花朵數據集的圖片分類
提示:以下是本篇文章正文內容,下面案例可供參考
數據集是五個分別存放着對應類別花朵圖片的五個文件夾,包括daisy(雛菊)633張;dandelion(蒲公英)898張,rose(玫瑰)641張,sunflower(向日葵)699張,tulips(鬱金香)799張。
import tensorflow as tfAUTOTUNE = tf.data.experimental.AUTOTUNEimport pathlibdata_root_orig = tf.keras.utils.get_file(origin='https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz', fname='flower_photos', untar=True)data_root = pathlib.Path(data_root_orig)print(data_root)for item in data_root.iterdir(): print(item)
打印下載後的文件路徑和文件成員:
output:
C:\Users\Administrator.keras\datasets\flower_photos
C:\Users\Administrator.keras\datasets\flower_photos\daisy
C:\Users\Administrator.keras\datasets\flower_photos\dandelion
C:\Users\Administrator.keras\datasets\flower_photos\LICENSE.txt
C:\Users\Administrator.keras\datasets\flower_photos\roses
C:\Users\Administrator.keras\datasets\flower_photos\sunflowers
C:\Users\Administrator.keras\datasets\flower_photos\tulips
2、統計並觀察數據
#獲取五個文件夾的名字label_names = sorted(item.name for item in data_root.glob('*/') if item.is_dir())label_names
output:[『daisy』, 『dandelion』, 『roses』, 『sunflowers』, 『tulips』]
#將文件夾的名字(即花的分類)標上序號label_to_index = dict((name, index) for index, name in enumerate(label_names))label_to_index
{『daisy』: 0, 『dandelion』: 1, 『roses』: 2, 『sunflowers』: 3, 『tulips』: 4}
#獲取所以圖片的標籤(0,1,2,3,4)import randomall_image_paths = list(data_root.glob('*/*'))all_image_paths = [str(path) for path in all_image_paths]random.shuffle(all_image_paths)image_count = len(all_image_paths)image_countall_image_labels = [label_to_index[pathlib.Path(path).parent.name]for path in all_image_paths]print("First 10 labels indices: ", all_image_labels[:10])
3670First 10 labels indices: [0, 2, 3, 4, 2, 1, 4, 1, 4, 0]
下面我們先來觀察一張圖片
#觀察第一張圖片img_path = all_image_paths[0]img_path
『C:\Users\Administrator\.keras\datasets\flower_photos\daisy\11124324295_503f3a0804.jpg』
#讀取原圖img_raw = tf.io.read_file(img_path)#轉換成TensorFlow可以使用的tensor類型img_tensor = tf.image.decode_image(img_raw)print(img_tensor.shape)print(img_tensor.dtype)
#對圖片按要求進行轉換,這裡將size規定到【192,192】;值域映射到【0,1】img_final = tf.image.resize(img_tensor, [192, 192])img_final = img_final/255.0print(img_final.shape)print(img_final.numpy().min())print(img_final.numpy().max())
(192, 192, 3)0.00.99984366
定義預處理和加載函數
def preprocess_image(image): image = tf.image.decode_jpeg(image, channels=3) image = tf.image.resize(image, [192, 192]) image /= 255.0 # normalize to [0,1] range return image return imagedef load_and_preprocess_image(path): image = tf.io.read_file(path) return preprocess_image(image)
導入matpoltlib進行畫圖(導入失敗的解決方案見我的另一篇博文)
import matplotlib.pyplot as pltimage_path = all_image_paths[0]label = all_image_labels[0]print (load_and_preprocess_image(img_path))plt.imshow(load_and_preprocess_image(img_path))plt.grid(False)使用tf.data.Dataset來構建規範的數據集
#「from_tensor_slices 」方法使用張量的切片元素構建圖片路徑的數據集path_ds = tf.data.Dataset.from_tensor_slices(all_image_paths)#同理,構建標籤數據集,並用tf.cast轉換成int64數據類型label_ds = tf.data.Dataset.from_tensor_slices(tf.cast(all_image_labels, tf.int64))#根據路徑獲取圖片,並經過加載和預處理得到圖片數據集image_ds = path_ds.map(load_and_preprocess_image )image_ds將image_ds和label_ds打包成新的數據集
image_label_ds = tf.data.Dataset.zip((image_ds, label_ds))print(image_label_ds)plt.figure(figsize=(8,8))for n,image_label in enumerate(image_label_ds.take(4)): plt.subplot(2,2,n+1) plt.imshow(image_label[0]) plt.grid(False)
4、遷移學習進行分類
接下來用創建的數據集訓練一個分類模型,簡單起見,直接用tf.keras.applications包中訓練好的模型,並將其遷移到我們的圖片分類問題上來。這裡使用的模型是MobileNetV2模型
#遷移MobileNetV2模型,並且不加載頂層base_model=tf.keras.applications.mobilenet_v2.MobileNetV2(include_top=False,weights='imagenet',input_shape=(192,192,3))inputs=tf.keras.layers.Input(shape=(192,192,3))#模型可視化1,使用model.summary()方法base_model.summary()
接下來,我們打亂一下數據集,並定義好訓練過程中每個批次(Batch)數據的大小
#使用shuffle方法打亂數據集image_count = len(all_image_paths)ds = image_label_ds.shuffle(buffer_size = image_count)#讓數據集重複多次ds = ds.repeat()#設置每個批次的大小BATCH_SIZE = 32ds = ds.batch(BATCH_SIZE)#通過prefetch方法讓模型的訓練和每個批次數據的加載並行ds = ds.prefetch(buffer_size = AUTOTUNE)
然後,針對MobileNetV2改變一樣數據集的取值範圍,因為MobileNetV2接受輸入的數據值域是【-1,1】,而我們之前的預處理函數將圖片的像素值映射到【0,1】
def change_range(image,label): return 2*image-1,labelkeras_ds = ds.map(change_range)
接下來定義模型,由於預訓練好的MobileNetV2返回的數據維度是(32,6,6,128),其中32是一個批次Batch的大小,「6,6」是輸出的特徵的大小為6*6,1280代表該層使用的1280個卷積核。為了使用花朵分類問題,需要做一下調整
model = tf.keras.Sequential([ base_model, tf.keras.layers.GlobalAveragePooling2D(), tf.keras.layers.Dense(len(label_names),activation="softmax") ])
如上代碼,我們用Sequentail建立我們的網絡結構,base_model是遷移過來的模型,我們添加了全局評價池化層GlobalAveragePooling,經過此操作6*6的特徵被降維,變為(32,1280)。
最後,由於該分類問題有五個結果,我們增加一個全連接層(Dense)將維度變為(32,5)。
最後,編譯一下模型,同時制定使用的優化器,損失函數和評價標準
model.compile(optimizer = tf.keras.optimizers.Adam(), loss='sparse_categorical_crossentropy', metrics=['accuracy'])model.summary()
使用model.fit訓練模型,epochs是訓練的回合數,step_per_epoch代表每個回合要去多少個批次數據。通常等於我們數據集大小除以批次大小後取證(3670/32≈10)
model.fit(ds,epochs=10,steps_per_epoch=100)
雖然沒有跑完整個代碼,但是已經能看出來準確度達到一個很高的程度,並在逐步上升。