基于InceptionV3深度學習實作巖石影像智能識別與分類
文章目錄
- 基于InceptionV3深度學習實作巖石影像智能識別與分類
- 總體流程
- 資料預處理
- 構建InceptionV3模型
- 訓練、保存模型
- 可視化acc/loss圖
- 預測
- 源代碼與資料集
- 參考
總體流程
- 整理資料集(訓練集、驗證集),按照目錄格式分類
- 讀取資料集影像,歸一化處理和資料增強
- 加載預訓練模型InceptionV3,作為基礎模型
- 在Inception卷積神經網路的瓶頸層后設計適用于本專案的網路結構,成為my_mode
- 凍結預訓練模型的所有層,變為不可訓練,便于正確獲得瓶頸層輸出的特征,自己添加的層需要訓練,相當于把InceptionV3變為一個特征提取器
- 編譯、訓練、保存
- 預測:讀取需識別的影像,轉換資料格式,預測輸出
資料預處理
使用一個ImageDataGenerator圖片生成器,定義圖片處理以及資料增強相關
-
ImageDataGenerator,這個API提供資料處理相關功能,以及資料增強功能,使得資料多樣化
-
datagen = ImageDataGenerator(rescale=1. / 255, # 歸一化 zoom_range=0.2, rotation_range=40., channel_shift_range=25., width_shift_range=0.2, height_shift_range=0.2, horizontal_flip=True, fill_mode='nearest') # fill_mode:‘constant’,‘nearest’,‘reflect’或‘wrap’之一, # 當進行變換時超出邊界的點將根據本引數給定的方法進行處理 -
引數(參考:http://www.51zixue.net/Keras/853.html)
- featurewise_center:布林值,使輸入資料集去中心化(均值為0), 按feature執行
- samplewise_center:布林值,使輸入資料的每個樣本均值為0
- featurewise_std_normalization:布林值,將輸入除以資料集的標準差以完成標準化, 按feature執行
- samplewise_std_normalization:布林值,將輸入的每個樣本除以其自身的標準差
- zca_whitening:布林值,對輸入資料施加ZCA白
- zca_epsilon: ZCA使用的eposilon,默認1e-6
- rotation_range:整數,資料提升時圖片隨機轉動的角度
- width_shift_range:浮點數,圖片寬度的某個比例,資料提升時圖片水平偏移的幅度
- height_shift_range:浮點數,圖片高度的某個比例,資料提升時圖片豎直偏移的幅度
- shear_range:浮點數,剪切強度(逆時針方向的剪切變換角度)
- zoom_range:浮點數或形如[lower,upper]的串列,隨機縮放的幅度,若為浮點數,則相當于[lower,upper] = [1 - zoom_range, 1+zoom_range]
- channel_shift_range:浮點數,隨機通道偏移的幅度
- fill_mode:;‘constant’,‘nearest’,‘reflect’或‘wrap’之一,當進行變換時超出邊界的點將根據本引數給定的方法進行處理
- cval:浮點數或整數,當fill_mode=constant時,指定要向超出邊界的點填充的值
- horizontal_flip:布林值,進行隨機水平翻轉
- vertical_flip:布林值,進行隨機豎直翻轉
- rescale: 重放縮因子,默認為None. 如果為None或0則不進行放縮,否則會將該數值乘到資料上(在應用其他變換之前)
-
-
ImageDataGenerator.flow_from_directory() ,實作了自動給固定格式目錄下的資料集打標簽,分批無序讀取,回傳張量型別資料集,
-
這個API要求有嚴格的目錄格式,如下:
-
data/ train/ dogs/ dog001.jpg dog002.jpg ... cats/ cat001.jpg cat002.jpg ... validation/ dogs/ dog001.jpg dog002.jpg ... cats/ cat001.jpg cat002.jpg ... -
generator = datagen.flow_from_directory( dir_path, # 資料存放路徑 target_size=(img_row, img_col), # 目標形狀 batch_size=batch_size, # 批數量大小 class_mode='categorical', # 二分類使用binary # "categorical" :2D one-hot encoded labels # "binary" will be 1D binary labels shuffle=is_train # 是否打亂 ) -
引數:(參考:https://blog.csdn.net/mieleizhi0522/article/details/82191331)
directory: 目標檔案夾路徑,對于每一個類,該檔案夾都要包含一個子檔案夾.子檔案夾中任何JPG、PNG、BNP、PPM的圖片都會被生成器使用.詳情請查看此腳本
target_size: 整數tuple,默認為(256, 256). 影像將被resize成該尺寸
color_mode: 顏色模式,為"grayscale",“rgb"之一,默認為"rgb”.代表這些圖片是否會被轉換為單通道或三通道的圖片.
classes: 可選引數,為子檔案夾的串列,如[‘dogs’,‘cats’]默認為None. 若未提供,則該類別串列將從directory下的子檔案夾名稱/結構自動推斷,每一個子檔案夾都會被認為是一個新的類,(類別的順序將按照字母表順序映射到標簽值),通過屬性class_indices可獲得檔案夾名與類的序號的對應字典,
class_mode: “categorical”, “binary”, "sparse"或None之一. 默認為"categorical. 該引數決定了回傳的標簽陣列的形式, "categorical"會回傳2D的one-hot編碼標簽,"binary"回傳1D的二值標簽."sparse"回傳1D的整數標簽,如果為None則不回傳任何標簽, 生成器將僅僅生成batch資料, 這種情況在使用model.predict_generator()和model.evaluate_generator()等函式時會用到.
batch_size: batch資料的大小,默認32
shuffle: 是否打亂資料,默認為True
seed: 可選引數,打亂資料和進行變換時的亂數種子
save_to_dir: None或字串,該引數能讓你將提升后的圖片保存起來,用以可視化
save_prefix:字串,保存提升后圖片時使用的前綴, 僅當設定了save_to_dir時生效
save_format:“png"或"jpeg"之一,指定保存圖片的資料格式,默認"jpeg”
flollow_links: 是否訪問子檔案夾中的軟鏈接
-
構建InceptionV3模型

Bottleneck feature即為瓶頸層輸出的特征,后面的層被丟棄,我們在瓶頸層后添加全連接層進行分類,輸出變為符合概率分布的概率,添加的層結構如下,

設定整體卷積神經網路的輸入為(150,150,3)矩陣,
自建層輸入的是3 * 3 * 2048 的張量,因為我們是分類5類影像,所以最后一層用5個神經元,使用softmax激活函式,輸出五種類別各自的概率值,
Flatten:Flatten層用來將輸入“壓平”,即把多維的輸入一維化,常用在從卷積層到全連接層的過渡,Flatten不影響batch的大小,
Dropout:做分類的時候,Dropout 層一般加在全連接層 防止過擬合,提升模型泛化能力,而很少見到卷積層后接Dropout(原因主要是 卷積引數少,不易過擬合)
凍結源模型的所有層,針對資料集大小有三種不同方案:
- 資料集少的就凍住所有的特征提取層
- 資料集中的可以凍住開始一部分的特征提取層
- 資料集多的可以自行訓練引數
可視化神經網路結構:
from tensorflow.keras.utils import plot_model
plot_model(model, to_file='inceptionV3_model.png',
show_shapes=True)
def InceptionV3_model(self, lr=0.005, decay=1e-6, momentum=0.9, nb_classes=2,
img_rows=197, img_cols=197, RGB=True, is_plot_model=False):
"""InceptionV3模型,建立自己的模型
Args:
lr (float, optional): 學習率. Defaults to 0.005.
decay ([type], optional): 學習衰減率. Defaults to 1e-6.
momentum (float, optional): Defaults to 0.9.
nb_classes (int, optional): 分類數. Defaults to 2.
img_rows (int, optional): 圖片行數. Defaults to 197.
img_cols (int, optional): 圖片列數. Defaults to 197.
RGB (bool, optional): 是否為3通道圖片. Defaults to True.
is_plot_model (bool, optional): 是否畫出模型網路結構圖. Defaults to False.
Returns:
[type]: 回傳模型
"""
color = 3 if RGB else 1
# 假設最后一層CNN的層輸出為(img_rows, img_cols, color
base_model = InceptionV3(weights='imagenet',
include_top=False,
input_shape=(img_rows, img_cols, color),
)
# 對我們的輸出進行平鋪操作,為全連接層做準備
x = layers.Flatten()(base_model.output)
# 增加一個全連接層,并使用relu作為激活函式,這是需要訓練的
x = layers.Dense(1024, activation='relu')(x)
# 添加隨機失活,抑制過擬合
x = layers.Dropout(0.2)(x)
# ,輸出層,把輸出設定成softmax函式
predictions = layers.Dense(nb_classes, activation='softmax')(x)
# 訓練模型
model = Model(inputs=base_model.input, outputs=predictions)
# 凍結base_model所有層,這樣就可以正確獲得bottleneck特征
for layer in base_model.layers:
layer.trainable = False
sgd = SGD(lr=lr, decay=decay, momentum=momentum, nesterov=True)
model.compile(loss='categorical_crossentropy',
optimizer=sgd, metrics=['acc'])
model.summary()
# 可視化網路結構,生成圖片
if is_plot_model:
plot_model(model, to_file='inceptionV3_model.png',
show_shapes=True)
return model
訓練、保存模型
訓練:用fit_generator函式,它可以避免了一次性加載大量的資料,并且生成器與模型將并行執行以提高效率,比如可以在CPU上進行實時的資料提升,同時在GPU上進行模型訓練
def train_model(self, model, epochs, train_generator, steps_per_epoch,
validation_generator, validation_steps,path_save_model, is_load_model=False):
"""訓練模型,載入、保存、斷點續訓
Args:
model ([type]): 模型
epochs ([type]): 訓練次數
train_generator ([type]): 訓練集
steps_per_epoch ([type]):
validation_generator ([type]): 驗證集
validation_steps ([type]):
path_save_model ([type]): 保存模型路徑
is_load_model (bool, optional): 是否載入模型. Defaults to False.
Returns:
[type]: 訓練記錄
"""
# 載入模型
if is_load_model and os.path.exists(path_save_model):
print('================載入已訓練模型===============')
model = load_model(path_save_model)
# 使用tensorboard
log_dir = "logs/fit/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
tensorboard_callback = tf.keras.callbacks.TensorBoard(
log_dir=log_dir, histogram_freq=1)
# 斷點續訓
# 格式化字符,防止檔案名沖突
checkpoint_path = 'ckpt/transfer_{epoch:02d}-{val_acc:.2f}.h5'
checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(
checkpoint_path, # 保存路徑,指定為tensorflow二進制檔案ckpt
monitor='val_acc', # 監測指標,這里是測驗集的acc
save_best_only=False, # 是否只保存最佳
save_weights_only=True, # 只保存權重
mode='max',
period=1 # 每period個周期保存一次
)
# 訓練
print('================開始訓練================')
history_ft = model.fit_generator(
train_generator,
steps_per_epoch=steps_per_epoch,
epochs=epochs,
validation_data=validation_generator,
validation_steps=validation_steps,
verbose=1, # 日志顯示,0/1,1表示輸出進度條日志資訊
callbacks=[tensorboard_callback, checkpoint_callback])
# 模型保存
print('================保存模型================')
model.save(path_save_model, overwrite=True)
return history_ft
斷點續訓:在模型訓練時保存檢查點,防止因意外情況丟失訓練進度,
# 斷點續訓
# 格式化字符,防止檔案名沖突
checkpoint_path = 'ckpt/transfer_{epoch:02d}-{val_acc:.2f}.h5'
checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(
checkpoint_path, # 保存路徑,指定為tensorflow二進制檔案ckpt
monitor='val_acc', # 監測指標,這里是測驗集的acc
save_best_only=False, # 是否只保存最佳
save_weights_only=True, # 只保存權重
mode='max',
period=1 # 每period個周期保存一次
)
保存、載入模型的詳細用法,請參考我的另一篇博客:https://blog.csdn.net/jun_zhong866810/article/details/119708120?spm=1001.2014.3001.5501
可視化acc/loss圖
def plot_training(self, history):
"""可視化acc/loss圖
Args:
history ([type]): 訓練
"""
print('================繪制acc/loss圖================')
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(acc))
plt.plot(epochs, acc, 'b-')
plt.plot(epochs, val_acc, 'r')
plt.title('Training and validation accuracy')
plt.figure()
plt.plot(epochs, loss, 'b-')
plt.plot(epochs, val_loss, 'r-')
plt.title('Training and validation loss')
plt.show()
預測
- 載入我們訓練好的模型(讀取全部模型或者讀取權重)
- 輸入待識別巖石影像,影像格式標準化
- 預測并輸出對于的巖石標簽
# 建立標簽字典,便于輸出結果
label_dict = {
'0': '安山巖',
'1': '石灰巖',
'2': '石英巖',
'3': '礫巖',
'4': '花崗巖'
}
def loadModel():
"""讀取全部模型資料"""
model = tf.keras.models.load_model('model/my_saved_InceptionV3_model.h5')
return model
if __name__ == '__main__':
model = loadModel()
print(model.summary())
for img_name in os.listdir(path):
img_path = path+img_name
img = image.load_img(img_path, target_size=(150, 150))
# 保持輸入格式一致
x = image.img_to_array(img) / 255
# 變為四維資料
x = np.expand_dims(x, axis=0)
# 預測
result = model.predict(x)
# 回傳最大概率值的索引,型別是張量
index = tf.argmax(result, axis=1)
print(img_name, '======================>', label_dict[str(int(index))])

原始影像:

源代碼與資料集
資料集:
鏈接:https://pan.baidu.com/s/15ZfB79YGxdMZwT4I3OPRiQ
提取碼:zjsg
train.py
# -*- coding: utf-8 -*-
# @Time : 2021/08/15
# @Author : Z.J
# @File : train.py
# @Software: vs code
# -*- coding: utf-8 -*-
import os
import datetime
import tensorflow as tf
import matplotlib.pyplot as plt
from tensorflow.keras import layers
from tensorflow.keras.utils import plot_model
from tensorflow.keras.applications.inception_v3 import InceptionV3
from tensorflow.keras.models import Model, load_model
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.regularizers import l2
class PowerTransferMode:
"""遷移學習類
"""
def DataGen(self, dir_path, img_row, img_col, batch_size, is_train):
"""讀取資料集,并進行資料增強和打標簽
Args:
dir_path (str)): 資料集路徑
img_row (int): 行數
img_col (int): 行數
batch_size (int): 批數量
is_train (bool): 是否為訓練集
Returns:
[type]: 資料集
"""
if is_train:
# ImageDataGenerator :生產圖片的批次張量值并且提供資料增強功能
print('==================讀取訓練資料================')
datagen = ImageDataGenerator(rescale=1. / 255,
zoom_range=0.2,
rotation_range=40.,
channel_shift_range=25.,
width_shift_range=0.2,
height_shift_range=0.2,
horizontal_flip=True,
fill_mode='nearest') # fill_mode:‘constant’,‘nearest’,‘reflect’或‘wrap’之一,
# 當進行變換時超出邊界的點將根據本引數給定的方法進行處理
else:
# 驗證集不需要資料增強
print('==================讀取驗證資料================')
datagen = ImageDataGenerator(rescale=1. / 255)
generator = datagen.flow_from_directory(
dir_path, # 資料存放路徑
target_size=(img_row, img_col), # 目標形狀
batch_size=batch_size, # 批數量大小
class_mode='categorical', # 二分類使用binary
# "categorical" :2D one-hot encoded labels
# "binary" will be 1D binary labels
shuffle=is_train # 是否打亂
)
return generator
# InceptionV3模型
def InceptionV3_model(self, lr=0.005, decay=1e-6, momentum=0.9, nb_classes=2,
img_rows=197, img_cols=197, RGB=True, is_plot_model=False):
"""InceptionV3模型,建立自己的模型
Args:
lr (float, optional): 學習率. Defaults to 0.005.
decay ([type], optional): 學習衰減率. Defaults to 1e-6.
momentum (float, optional): Defaults to 0.9.
nb_classes (int, optional): 分類數. Defaults to 2.
img_rows (int, optional): 圖片行數. Defaults to 197.
img_cols (int, optional): 圖片列數. Defaults to 197.
RGB (bool, optional): 是否為3通道圖片. Defaults to True.
is_plot_model (bool, optional): 是否畫出模型網路結構圖. Defaults to False.
Returns:
[type]: 回傳模型
"""
color = 3 if RGB else 1
# 假設最后一層CNN的層輸出為(img_rows, img_cols, color)
print('=================加載預訓練模型=================')
base_model = InceptionV3(weights='imagenet',
include_top=False,
input_shape=(img_rows, img_cols, color),
)
# 對我們的輸出進行平鋪操作,為全連接層做準備
x = layers.Flatten()(base_model.output)
# 增加一個全連接層,并使用relu作為激活函式,這是需要訓練的
x = layers.Dense(1024, activation='relu')(x)
# 添加隨機失活,抑制過擬合
x = layers.Dropout(0.2)(x)
# ,輸出層,把輸出設定成softmax函式
predictions = layers.Dense(nb_classes, activation='softmax')(x)
# 訓練模型
print('================創建自己的模型==================')
model = Model(inputs=base_model.input, outputs=predictions)
# 凍結base_model所有層,這樣就可以正確獲得bottleneck特征
for layer in base_model.layers:
layer.trainable = False
sgd = SGD(lr=lr, decay=decay, momentum=momentum, nesterov=True)
print('================編譯模型=================')
model.compile(loss='categorical_crossentropy',
optimizer=sgd, metrics=['acc'])
print('=================列印模型結構資訊=================')
model.summary()
# 可視化網路結構,生成圖片
if is_plot_model:
plot_model(model, to_file='inceptionV3_model.png',
show_shapes=True)
return model
def train_model(self, model, epochs, train_generator, steps_per_epoch,
validation_generator, validation_steps,path_save_model, is_load_model=False):
"""訓練模型,載入、保存、斷點續訓
Args:
model ([type]): 模型
epochs ([type]): 訓練次數
train_generator ([type]): 訓練集
steps_per_epoch ([type]):
validation_generator ([type]): 驗證集
validation_steps ([type]):
path_save_model ([type]): 保存模型路徑
is_load_model (bool, optional): 是否載入模型. Defaults to False.
Returns:
[type]: 訓練記錄
"""
# 載入模型
if is_load_model and os.path.exists(path_save_model):
print('================載入已訓練模型===============')
model = load_model(path_save_model)
# 使用tensorboard
log_dir = "logs/fit/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
tensorboard_callback = tf.keras.callbacks.TensorBoard(
log_dir=log_dir, histogram_freq=1)
# 斷點續訓
# 格式化字符,防止檔案名沖突
checkpoint_path = 'ckpt/transfer_{epoch:02d}-{val_acc:.2f}.h5'
checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(
checkpoint_path, # 保存路徑,指定為tensorflow二進制檔案ckpt
monitor='val_acc', # 監測指標,這里是測驗集的acc
save_best_only=False, # 是否只保存最佳
save_weights_only=True, # 只保存權重
mode='max',
period=1 # 每period個周期保存一次
)
# 訓練
print('================開始訓練================')
history_ft = model.fit_generator(
train_generator,
steps_per_epoch=steps_per_epoch,
epochs=epochs,
validation_data=validation_generator,
validation_steps=validation_steps,
verbose=1, # 日志顯示,0/1,1表示輸出進度條日志資訊
callbacks=[tensorboard_callback, checkpoint_callback])
# 模型保存
print('================保存模型================')
model.save(path_save_model, overwrite=True)
return history_ft
def plot_training(self, history):
"""可視化acc/loss圖
Args:
history ([type]): 訓練
"""
print('================繪制acc/loss圖================')
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(acc))
plt.plot(epochs, acc, 'b-')
plt.plot(epochs, val_acc, 'r')
plt.title('Training and validation accuracy')
plt.figure()
plt.plot(epochs, loss, 'b-')
plt.plot(epochs, val_loss, 'r-')
plt.title('Training and validation loss')
plt.show()
if __name__ == '__main__':
image_size = 150
batch_size = 20
num_train = 300
num_val = 100
transfer_model = PowerTransferMode()
# 得到資料
train_generator = transfer_model.DataGen('./data/巖石資料集/train',
image_size,
image_size,
batch_size,
True)
validation_generator = transfer_model.DataGen('./data/巖石資料集/validation',
image_size,
image_size,
batch_size,
False)
# InceptionV3
model = transfer_model.InceptionV3_model(nb_classes=5,
img_rows=image_size,
img_cols=image_size,
is_plot_model=False)
# 訓練模型
history_ft = transfer_model.train_model(model,
epochs=50,
train_generator=train_generator,
steps_per_epoch=num_train / batch_size,
validation_generator=validation_generator,
validation_steps=num_val / batch_size,
path_save_model='model/my_saved_InceptionV3_model.h5',
is_load_model=True)
transfer_model.plot_training(history_ft)
predict.py
# -*- coding: utf-8 -*-
# @Time : 2021/08/15
# @Author : Z.J
# @File : predict.py
# @Software: vs code
from tensorflow.keras.preprocessing import image
import numpy as np
import os
import tensorflow as tf
import train
from tensorflow.python.training.checkpoint_management import latest_checkpoint
path = "./tmp/predict/" # 預測圖片的路徑
path_save_model = './model/my_saved_InceptionV3_model.h5' # 保存的模型的路徑
checkpoint_path = 'ckpt/transfer_{epoch:02d}-{val_acc:.2f}.h5' # 檢查點路徑
checkpoint_root = os.path.dirname(checkpoint_path) # 檢查點檔案根目錄
image_size = 150 # 圖片格式(150,150)
# 建立標簽字典,便于輸出結果
label_dict = {
'0': '安山巖',
'1': '石灰巖',
'2': '石英巖',
'3': '礫巖',
'4': '花崗巖'
}
def loadWeights():
"""
讀取保存的權重資料,需先構建網路結構一致的新模型
"""
base_model = train.PowerTransferMode()
model = base_model.InceptionV3_model(
nb_classes=5,
img_rows=image_size,
img_cols=image_size,
is_plot_model=False
)
# 從檢查點恢復權重
saved_weights = './ckpt/transfer_50-1.00.h5'
# latest_weights = tf.train.latest_checkpoint(checkpoint_root) 只對ckpt格式檔案有用!
model.load_weights(saved_weights)
return model
def loadModel():
"""讀取全部模型資料"""
model = tf.keras.models.load_model('model/my_saved_InceptionV3_model.h5')
return model
if __name__ == '__main__':
model = loadWeights()
print(model.summary())
for img_name in os.listdir(path):
img_path = path+img_name
img = image.load_img(img_path, target_size=(150, 150))
# 保持輸入格式一致
x = image.img_to_array(img) / 255
# 變為四維資料
x = np.expand_dims(x, axis=0)
# 預測
result = model.predict(x)
# 回傳最大概率值的索引,型別是張量
index = tf.argmax(result, axis=1)
print(img_name, '======================>', label_dict[str(int(index))])
參考
https://blog.csdn.net/pengdali/article/details/79050662
https://blog.csdn.net/m0_46334316/article/details/117607628 感謝博主提供的巖石資料集
https://blog.csdn.net/weixin_43999137/article/details/104093095
http://www.51zixue.net/Keras/853.html
https://blog.csdn.net/mieleizhi0522/article/details/82191331
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/294804.html
標籤:其他
