日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

手把手带你从0完成医疗行业影像图像检测三大经典模型InceptionV3-RestNet50-VGG16(附python源代码及数据库)——改变世界经典人工智能项目实战(一)手把手教学迁移学习

發(fā)布時間:2024/1/8 python 49 豆豆

手把手帶你從0完成醫(yī)療行業(yè)影像圖像檢測三大經(jīng)典模型InceptionV3-RestNet50-VGG16

  • 1、遷移學習簡介
  • 2、項目簡介
  • 3、糖尿病視網(wǎng)膜病變數(shù)據(jù)集
  • 4、考慮類別不平衡問題
  • 5、定義模型質量
  • 6、定義損失函數(shù)
  • 7、預處理圖像
  • 8、搭建遷移學習網(wǎng)絡
    • VGG16 遷移學習網(wǎng)絡
    • InceptionV3遷移學習網(wǎng)絡
    • ResNet50遷移學習網(wǎng)絡
  • 9、動態(tài)創(chuàng)建小批量數(shù)據(jù)進行訓練
  • 10、測試集預測
  • 11、全部代碼
  • 其它資料下載

如果你想使用現(xiàn)在最火的ChatGPT來訓練屬于你的專屬ChatGPT模型,那你千萬不能錯過這篇文章。遷移學習是機器學習領域中的一種重要方法,它通過利用先前的學習經(jīng)驗來提高當前任務的性能。本文通過3個經(jīng)典的模型:InceptionV3-RestNet50-VGG16作為示例,為大家從0開始搭建了醫(yī)療影像行業(yè)遷移學習網(wǎng)絡,并獲取到了較好的準確度與結果一致性。而掌握好遷移學習的基礎知識和應用,你就能通過模型微調(也稱遷移學習),調用ChatGPT接口加上你的訓練集去訓練你的模型了。

1、遷移學習簡介

遷移學習(transfer learning)是將在特定領域的一個任務中獲得的知識遷移到另一個相似領域的相關項目的過程。它允許我們將在一個特定領域的任務中獲得的知識應用到另一個相似領域的相關項目中。在深度學習中,這通常意味著使用預先訓練好的模型作為解決新問題的起點。由于計算機視覺和自然語言處理問題需要大量的數(shù)據(jù)和計算資源來訓練有效的深度學習模型,遷移學習可以減少對大量數(shù)據(jù)和時間的需求,因此在這些領域非常重要。

在2016年的NIPS教程中,Andrew Ng指出遷移學習將成為繼監(jiān)督學習之后推動機器學習商業(yè)成功的重要因素。這一預測正在變?yōu)楝F(xiàn)實,因為遷移學習現(xiàn)在被廣泛應用于各種需要人工神經(jīng)網(wǎng)絡解決的困難問題。這引發(fā)了一個問題:為什么會出現(xiàn)這種情況?

從頭開始訓練人工神經(jīng)網(wǎng)絡是一項艱巨的任務,這主要有以下兩個原因:

  • 人工神經(jīng)網(wǎng)絡的損失面是非凸的。因此,它需要一組良好的初始權重才能得到合理的收斂。
  • 人工神經(jīng)網(wǎng)絡有很多參數(shù),因此需要大量的數(shù)據(jù)進行訓練。遺憾的是,對于許多項目而言,可以被用于訓練神經(jīng)網(wǎng)絡的特定數(shù)據(jù)并不夠,但同時項目要解決的問題又非常復雜,需要依賴于神經(jīng)網(wǎng)絡的解決方案。

本項目將介紹如何使用遷移學習來解決醫(yī)療問題

2、項目簡介

在醫(yī)療領域,糖尿病視網(wǎng)膜病變通常在糖尿病患者中發(fā)現(xiàn),病人的高血糖會對其視網(wǎng)膜中的血管造成損害。糖尿病視網(wǎng)膜病變的檢測通常是手動檢測,即由經(jīng)驗豐富的醫(yī)生通過檢查彩色的眼底視網(wǎng)膜圖像來完成。這樣的診斷過程通過會引人一定程度的延遲,進而導致治療的延誤。在本項目中,我們將使用遷移學習建立一個用于檢測人眼中的糖尿病視網(wǎng)膜病變的模型,通過輸人彩色視網(wǎng)膜眼底圖像來檢測是否存在糖尿病視網(wǎng)膜病變,并根據(jù)病變的嚴重程度進行分類。

下圖左側圖片是輕度糖尿病視網(wǎng)膜病變的視網(wǎng)膜,右側圖片是正常的視網(wǎng)膜。

3、糖尿病視網(wǎng)膜病變數(shù)據(jù)集

數(shù)據(jù)集下載鏈接: https://pan.baidu.com/s/1hsijpCLD5oF7G5NqspWAbQ?pwd=eu0j 提取碼: eu0j

該數(shù)據(jù)集包含來自2015年糖尿病視網(wǎng)膜病變檢測的圖像。每個圖像都已調整大小和裁剪,最大尺寸為1024 px。調整大小和裁剪的代碼可以在這里找到。

對于2015年糖尿病視網(wǎng)膜病變檢測圖像,為每個受試者提供左眼和右眼。圖像標記有受試者ID以及左或右(例如,1_left.jpeg是患者ID 1的左眼)。

臨床醫(yī)生已經(jīng)在0到4的尺度上針對糖尿病性視網(wǎng)膜病變的嚴重性對每個圖像進行了評級:

0 - No DR 0 -沒有糖尿病視網(wǎng)膜病變
1 - Mild 1 -輕度糖尿病視網(wǎng)膜病變
2 - Moderate 2 -中度糖尿病視網(wǎng)膜病變
3 - Severe 3 -重度糖尿病視網(wǎng)膜病變
4 - Proliferative DR 4 -增生性糖尿病視網(wǎng)膜病變

與任何真實世界的數(shù)據(jù)集一樣,您將在圖像和標簽中遇到噪聲。圖像可能包含偽影、失焦、曝光不足或曝光過度。這些圖像是在很長一段時間內(nèi)使用各種相機從多個診所收集的,這將引入進一步的變化。

文件夾說明:
labels.zip-包含2015年的訓練和測試標簽
resized train15.zip-包含2015年已調整大小和裁剪的訓練集圖像,總共35126個圖像。
resized test 15.zip-包含2015年已調整大小和裁剪的測試集圖像,總共53576個圖像。

4、考慮類別不平衡問題

類別不平衡是分類問題中的一個主要挑戰(zhàn)。下圖描繪了訓練集5個嚴重性類的類密度。


接近73%的訓練數(shù)據(jù)屬于類0,即沒有糖尿病視網(wǎng)膜病變。因此,如果我們碰巧將所有數(shù)據(jù)都標記為類0,那么準確度可能會達到73%,但是在實際生活中,我們寧愿在患者實際沒有某種健康問題的情況下誤判為有問題(假陽性),而不是在有某種健康問題的情況下誤判為沒有問題(假陰性)。因此,即使模型學會將所有數(shù)據(jù)歸類為類0,它的73%準確度也可能沒有太大意義。

檢測更高的嚴重性類別比檢測不嚴重類別更為重要。使用對數(shù)損失或交叉熵損失函數(shù)的分類模型的問題在于它的結果通常會有利于數(shù)據(jù)量大的類別。這是因為交叉熵誤差在最大相似性原則上更傾向于為數(shù)量更多的類別分配更高的概率。針對這個問題,我們可以兩件事:

  • 從具有更多樣本的類別中丟棄數(shù)據(jù)或者對類別進行低頻率采樣以保持樣本之間的均勻分布。
  • 在損失函數(shù)中,為類別賦予與其密度成反比的權重。這可以保證當模型未能對它們進行分類時,損失函數(shù)對低頻類別賦予更高的懲罰。

我們將使用方案二,因為它不涉及生成更多的數(shù)據(jù)或者丟棄現(xiàn)有數(shù)據(jù)。如果我們使用類頻率的倒數(shù)作為權重,我們將得到下表所示的類別權重:

我們將在訓練分類網(wǎng)絡時使用這些權重。詳見下面9、動態(tài)創(chuàng)建小批量數(shù)據(jù)進行訓練

5、定義模型質量

我們將對訓練集進行交叉驗證,使用標記的訓練數(shù)據(jù)構建模型,并在保留數(shù)據(jù)集上驗證模型。由于我們正在處理分類問題,因此準確度是一個有用的驗證指標。準確度定義如下:

這里,c是被正確分類的樣本的數(shù)量,N是用于評估的樣本總數(shù)。

我們還將使用二次加權kappa(quadratic weighted kappa)統(tǒng)計量來定義模型的質量,并與Kaggle標準相比,看我們建立的模型相較于基準是否有提升。二次加權kappa定義如下:

二次加權kappa表達式中的權重(wi,j)定義如下:

上述公式中包括以下內(nèi)容:

  • N表示類別的數(shù)量。
  • Oi,j表示被預測為類別i且實際類別為j的圖像的數(shù)量。
  • Ei,j表示被預測為類別i且實際類別為j的圖像的期望數(shù)量,并假設預測類別與實際類之間相互獨立。

可查看更加詳細的關于二次加權kappa定義及示例。

6、定義損失函數(shù)

在本項目中,數(shù)據(jù)有五個類別,即沒有糖尿病視網(wǎng)膜病變、輕度糖尿病視網(wǎng)膜病變、中度糖尿病視網(wǎng)膜病變、嚴重的糖尿病視網(wǎng)膜病變和增生性糖尿病視網(wǎng)膜病變。因此,我們可以將其視為分類問題。對于我們的分類問題,輸出標簽需要進行獨熱編碼,如下所示:

  • 無糖尿病視網(wǎng)膜病變:[10000]T
  • 輕度糖尿病視網(wǎng)膜病變:[01000]T
  • 中度糖尿病視網(wǎng)膜病變:[00100]T
  • 嚴重糖尿病視網(wǎng)膜病變:[00010]T
  • 增生性糖尿病視網(wǎng)膜病變:00001]T
  • Softmax是用于在輸出層中呈現(xiàn)不同類別的概率的最佳激活函數(shù),而每個數(shù)據(jù)點的類別交叉熵損失之和是要優(yōu)化的最佳損失。對于具有輸出標簽向量y和預測概率p的單個數(shù)據(jù)點,交叉熵損失由以下公式給出:

    這里,y=[y1…yj…ys]T,且p=[p1…pj…p5]T
    同樣地,M個訓練數(shù)據(jù)點的平均損失可以表示為:

    在訓練過程中,基于上式得到的平均對數(shù)損失(average log loss)來產(chǎn)生小批量的梯度,其中M是所選的批量的大小。對于我們將結合驗證準確度監(jiān)視的驗證對數(shù)損失,M是驗證集數(shù)據(jù)點數(shù)。由于我們將在K折交叉驗證(K-foldcross-validation)的每一折進行驗證,因此我們將在每個折中使用不同的驗證數(shù)據(jù)集。

    請注意,輸出中的類別具有序數(shù)性,并且嚴重性逐類遞增。因此回歸也可能是不錯的
    解決方法。大家也可以嘗試用回歸來代替分類,看看它是否合理。回歸的挑戰(zhàn)之一是將原始得分轉換為類別。

    7、預處理圖像

    不同類別的圖像將存儲在不同的文件夾中,因此可以很容易地標記它們的類別。博主寫了個分類代碼,可以直接修改文件夾地址,然后自動處理成不同類別的文件夾。具體python代碼如下:

    #!/usr/bin/env python3.8# -*- coding: utf-8 -*- # @Time : 2023/3/24 10:24 # @Author : Steven Hu # @FileName: split_data.py # @Software: PyCharmimport os import shutil import random import pandas as pddef split_dataset(folder_path, train_folder_path, validation_folder_path):# Get all list of files in the folderfiles = []for root, dirs, file_ in os.walk(folder_path):# 這里可以進行判斷,如果不是要搜索的就跳過;# 也可以對 `files` 列表進行遍歷,以達到具體文件的搜索if len(file_):file_path_list = [str(root) + os.sep + str(i) for i in file_]files.extend(file_path_list)# Split the dataset by 80%-20%train_files = random.sample(files, round(0.8 * len(files)))validation_files = [file for file in files if file not in train_files]# Copy the files from original folder to training folderif not os.path.exists(train_folder_path):os.makedirs(train_folder_path)for file in train_files:shutil.copy(file, train_folder_path)# Copy the files from original folder to validation folderif not os.path.exists(validation_folder_path):os.makedirs(validation_folder_path)for file in validation_files:shutil.copy(file, validation_folder_path)def class_generation_from_train(csv_file, source_dir, class_list=None):# 如果train中沒有分類,需要運行該函數(shù),對class進行分類# 讀取excel中的數(shù)據(jù)if class_list is None:class_list = ["class0", "class1", "class2", "class3", "class4"]df = pd.read_csv(csv_file)# 獲取指定文件夾中的所有文件名file_list = os.listdir(source_dir)for class_ in class_list:path_ = os.path.join(source_dir, class_)if not os.path.exists(path_):os.makedirs(path_)# 遍歷所有文件for filename in file_list:# 依次在excel中查找,如果找到對應行,則根據(jù)excel中指定列的值,將文件移動到以該值命名的文件夾中filename_ = filename[:-4]if filename_ in df['image'].values:row = df[df['image'] == filename_]target_dir = row['level'].values[0]target_dir = os.path.join(source_dir, "class" + str(target_dir))source_path = os.path.join(source_dir, filename)target_path = os.path.join(target_dir, filename)os.rename(source_path, target_path)if __name__ == '__main__':# 先隨機80%-20%拆分訓練集和驗證集folder_path = r"E:\Data\resized train 15"train_folder_path = r"E:\Data\train_validation\train"validation_folder_path = r"E:\Data\train_validation\validation"split_dataset(folder_path, train_folder_path, validation_folder_path)# 再對訓練集和驗證集內(nèi)進行class分類train_csv_file = r'E:\Data\labels\trainLabels15.csv' # 這個是訓練集的label文件testLabels15.csvclass_generation_from_train(train_csv_file, train_folder_path,["class0", "class1", "class2", "class3", "class4"])class_generation_from_train(train_csv_file, validation_folder_path,["class0", "class1", "class2", "class3", "class4"])# 再對測試集進行class分類test_folder_path = r"E:\Data\resized test 15"test_csv_file = r'E:\Data\labels\testLabels15.csv' # 這個是測試集的label文件class_generation_from_train(test_csv_file, test_folder_path,["class0", "class1", "class2", "class3", "class4"])

    我們使用OpenCV函數(shù)讀取圖像,并調整它們的尺寸,如224x224x3。我們將參照ImageNe數(shù)服集從每個圖像中逐通道減去平均像素強度。這樣可以保證在模型上訓練之前,糖尿病視像將數(shù)存的圖像濕度與所處理的Imagener 圖像具有相同的強度范圍。一旦完成預處理,機像將被存儲在一個numpy 數(shù)組中。圖像預處理函數(shù)可以定義如下:

    def pre_process(img):resized_img = cv2.resize(img, (224, 224), cv2.INTER_LINEAR)resized_img[:, :, 0] = resized_img[:, :, 0] - 103.939resized_img[:, :, 1] = resized_img[:, :, 0] - 116.779resized_img[:, :, 2] = resized_img[:, :, 0] - 123.68return resized_img

    我們通過通過行間插值的方法將其大小調整為(224,224,3)或其他任意指定維度。

    ImageNet 圖像的紅色、綠色和藍色通道的平均像素強度分別為103.939、116.779和123.68。預訓練模型是在從圖像中減去這些平均值之后進行訓練的。這種減去平均值的方法是為了使數(shù)據(jù)特征標準化,將數(shù)據(jù)集中在0附近有助于避免梯度消失和梯度爆炸問題,進而有助于模型更快地收斂。此外,每個通道標準化有助于保持梯度流均勻地進人每個通道。由于我們在這個項目中使用預訓練模型,因此合理的做法是在將圖像輸人預訓練網(wǎng)絡之前,每個通道也基于同樣的方式進行標準化。然而,使用基于預訓練網(wǎng)絡ImageNet的平均值來校正項目中的圖像并非強制要求,也可以通過項目中訓練集圖像的平均像素強度來進行標準化。

    同樣,還可以選擇對整個圖像進行均值歸一化,而不是分別對每個通道進行均值歸一化。這需要從圖像自身中減去每個圖像的平均值。想象一下,CNN中識別的物體可能來自不同的光照條件(如白天和夜晚)。而我們希望無論何種光照條件,都能正確地對物體進行分類,然而,不同的像素強度將不同程度地激活神經(jīng)網(wǎng)絡的神經(jīng)元,這會增加對象被錯誤分類的可能性。然而,如果從圖像中減去每個圖像的平均值,則該圖像對象將不再受到不同照明條件的影響。因此,根據(jù)具體圖像的性質,我們需要自己選擇最佳的圖像標準化方案,不過任何默認的標準化性能都不錯。

    另外為了擴充數(shù)據(jù),我們將使用keras的ImageDataGenerator在圖像像素坐標上進行仿射變換(affine transformation)來生成額外的數(shù)據(jù)。我們主要使用的仿射變換是旋轉、平移和縮放。具體代碼如下:

    datagen = ImageDataGenerator(horizontal_flip=True, vertical_flip=True, width_shift_range=0.1,height_shift_range=0.1,channel_shift_range=0, zoom_range=0.2,rotation_range=20,preprocessing_function=pre_process)

    從定義的生成器中可以看出,我們啟用了水平和垂直翻轉,這會生成分別沿水平軸和垂直軸反射得到的圖像。類似地,我們還讓圖像沿寬度和高度方向平移10%像素位警旋轉范圍限制在20度的角度范圍內(nèi),而縮放因子則定義在原始圖像的0.8~1.2以內(nèi)。

    8、搭建遷移學習網(wǎng)絡

    我們現(xiàn)在將使用預訓練的ResNet50、InceptionV3和VGG16網(wǎng)絡進行實驗,并找出能夠獲得最佳結果的網(wǎng)絡。每個預訓練模型的權重都基于ImageNet。我在下面提供了 ResNet InceptionV3和VGG16架構的原始論文鏈接以供參考。建議讀者閱讀這些文章,深入了解這些網(wǎng)絡架構以及它們之間細微的差別。
    以下是VGG論文的鏈接:
    論文題目:Very Deep Convolutional Networks for Large-Scale Image Recognition
    以下是ResNet 論文的鏈接:
    論文題目:Deep Residual Learning for Image Recognition
    以下是InceptionV3論文的鏈接:
    論文題目:Rethinking the Inception Architecture for Computer Vision

    論文百度下載鏈接:
    鏈接: https://pan.baidu.com/s/1B6XklxnrwCBCkmNd1ATOYw?pwd=tpwm 提取碼: tpwm

    VGG16 遷移學習網(wǎng)絡

    VGG16是一個16層的CNN,它使用3x3的濾波器和2x2 感受野(receptive field)進行卷積。整個網(wǎng)絡中使用的激活函數(shù)都是ReLU。VGG架構是由 Simonyan 和 Zisserman 開發(fā)的,它是2014年ILSVRC比賽的亞軍。VGG16網(wǎng)絡由于其簡單性而獲得廣泛的普及,而且它是用于從圖像中提取特征的最流行的網(wǎng)絡。

    使用VGG16網(wǎng)絡進行遷移學習,還是比較簡單,使用在ImageNet上預訓練的VGG16的權重作為模型的初始權重,然后對模型進行微調。凍結了前幾個層(默認為10層)的權重,因為在 CNN 中,前幾層會學習檢測通用的特征,如邊緣、顏色構成等。因此,不同領域圖像的通用特征不會有很大差異。凍結層是指不訓練特定于該層的權重。我們可以嘗試不同的凍結層數(shù)量,并采用提供最佳驗證結果的凍結層數(shù)量。由于我們現(xiàn)在面臨的是多分類任務,因此最終輸出層選擇了softmax激活函數(shù)。具體代碼如下:

    def VGG16_pseudo(dim=224, freeze_layers=10, full_freeze='N'):model = VGG16(weights='imagenet', include_top=False)# model = VGG16(weights=r'E:\learning_AI\Intelligent_projects_using_python\weights\vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5', include_top=False) # 如果上面運行出現(xiàn)網(wǎng)絡下載錯誤,建議先下載權重后,使用權重地址x = model.output # 從預訓練的網(wǎng)絡中提取最后一個最大池化層的輸出x = GlobalAveragePooling2D()(x) #確保池輸出是一維數(shù)組格式,而不是二維點陣格式x = Dense(dim, activation='relu')(x) # 連接一個全連接層,這個dim可以自定義成其它值,比如512x = Dropout(0.5)(x) # 防止過擬合x = Dense(dim, activation='relu')(x) # 再連接一個全連接層,這個dim可以自定義成其它值,比如512x = Dropout(0.5)(x) # 防止過擬合out = Dense(5, activation='softmax')(x) # 這里輸出必須是分類數(shù)量的單元數(shù)model_final = Model(input=model.input, outputs=out)if full_freeze != 'N':for layer in model.layers[0:freeze_layers]:layer.trainable = Falsereturn model_final

    VGG16_weights下載百度鏈接:https://pan.baidu.com/s/1pBz70iEtHe2PTXX7Mw20uw?pwd=wic3 提取碼: wic3

    InceptionV3遷移學習網(wǎng)絡

    InceptionV3是來自谷歌的最先進的CNN。InceptionV3架構不是在每層使用固定大小的卷積濾波器,而是使用不同大小的濾波器來提取不同粒度級別的特征。Inception是2014年ImageNet競賽的冠軍,前五錯誤率為6.67%,非常接近人類的表現(xiàn)。InceptionV3層的卷積塊如下圖所示。

    具體代碼如下:

    def inception_pseudo(dim=224, freeze_layers=30, full_freeze='N'):# model = InceptionV3(weights='imagenet', include_top=False) # 如果下載不了,則直接使用權重地址賦值到weights參數(shù)上model = InceptionV3(weights=r"E:\learning_AI\Intelligent_projects_using_python\weights\inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5",include_top=False)x = model.output # 從預訓練的網(wǎng)絡中提取最后一個最大池化層的輸出x = GlobalAveragePooling2D()(x) #確保池輸出是一維數(shù)組格式,而不是二維點陣格式x = Dense(dim, activation='relu')(x) # 連接一個全連接層,這個dim可以自定義成其它值,比如512x = Dropout(0.5)(x) # 防止過擬合x = Dense(dim, activation='relu')(x) # 再連接一個全連接層,這個dim可以自定義成其它值,比如512x = Dropout(0.5)(x) # 防止過擬合out = Dense(5, activation='softmax')(x) # 這里輸出必須是分類數(shù)量的單元數(shù)model_final = Model(input=model.input, outputs=out)if full_freeze != 'N':for layer in model.layers[0:freeze_layers]:layer.trainable = Falsereturn model_final

    需要注意的一點是,由于InceptionV3是一個更深的網(wǎng)絡,因此可以擁有更多的初始層。在數(shù)據(jù)有限的情況下,不需要訓練模型中的所有層是一個優(yōu)勢。如果我們使用較少的訓練數(shù)據(jù),則整個網(wǎng)絡的權重可能會導致過擬合。而凍結層可以減少需要訓練的權重數(shù)量,因此提供了某種形式的正則化。由于初始層學習與問題領域無關的通用特征,因此它們是最適合凍結的層。我們還在完全連接層中使用了dropout,以防止過擬合。

    InceptionV3_weights下載百度鏈接:https://pan.baidu.com/s/1pBz70iEtHe2PTXX7Mw20uw?pwd=wic3 提取碼: wic3

    ResNet50遷移學習網(wǎng)絡

    ResNet50是一個深度CNN,它實現(xiàn)了殘差塊(residualblock)的概念,與VGG16網(wǎng)絡非常不同。在一系列卷積-激活-池化操作之后,塊的輸入再次反饋到輸出。ResNet構由 Kaiming He 等人開發(fā),雖然它有152 層,但其實并沒有VGG網(wǎng)絡復雜。該架構以3.57%的前五錯誤率贏得了 2015 年ILSVRC 競賽,這比競賽數(shù)據(jù)集的人工標注成績還要好。前五錯誤率是通過檢查目標是否在最高概率的五個預測類別中得到的。實際上,ResNet網(wǎng)絡嘗試學習殘差映射,而不是直接從輸出映射到輸入,如下圖所示。

    具體代碼如下:

    def resnet_pseudo(dim=224, freeze_layers=10, full_freeze='N'):# model = ResNet50(weights='imagenet', include_top=False) # 如果下載不了,則直接使用權重地址賦值到weights參數(shù)上model = ResNet50(weights=r"E:\learning_AI\Intelligent_projects_using_python\weights\resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5",include_top=False)x = model.output # 從預訓練的網(wǎng)絡中提取最后一個最大池化層的輸出x = GlobalAveragePooling2D()(x) #確保池輸出是一維數(shù)組格式,而不是二維點陣格式x = Dense(dim, activation='relu')(x) # 連接一個全連接層,這個dim可以自定義成其它值,比如512x = Dropout(0.5)(x) # 防止過擬合x = Dense(dim, activation='relu')(x) # 再連接一個全連接層,這個dim可以自定義成其它值,比如512x = Dropout(0.5)(x) # 防止過擬合out = Dense(5, activation='softmax')(x) # 這里輸出必須是分類數(shù)量的單元數(shù)model_final = Model(input=model.input, outputs=out)if full_freeze != 'N':for layer in model.layers[0:freeze_layers]:layer.trainable = Falsereturn model_final

    ResNet50_weights下載百度鏈接:https://pan.baidu.com/s/1pBz70iEtHe2PTXX7Mw20uw?pwd=wic3 提取碼: wic3

    9、動態(tài)創(chuàng)建小批量數(shù)據(jù)進行訓練

    如果你的電腦內(nèi)存較低,建議使用小批量加載數(shù)據(jù)進行訓練,在訓練時只加載小批量數(shù)據(jù)的方式之一是通過隨機地處理不同位置的圖像來動態(tài)地創(chuàng)建小批量。每個小批量中處理的圖像數(shù)量等于我們指定的小批量大小。當然,在訓練期間動態(tài)地創(chuàng)建小批量會有一些性能瓶頸,但這些瓶頸可以忽略不計,特別是諸如 keras 之類的具有高效的動態(tài)批量創(chuàng)建機制。我們將利用keras 的flow_from_directory數(shù)間動態(tài)創(chuàng)建小批量,以減少訓練過程需要的內(nèi)存。同時使用ImageDataGenerator行圖像增強。相關代碼如下:

    def train_model(train_dir, val_dir, batch_size=16, epochs=40, dim=224, lr=1e-5, model='ResNet50'):model_final = self.inception_pseudo(dim=dim, freeze_layers=30, full_freeze='N') # 默認選擇為'InceptionV3'if model == 'Resnet50':model_final = self.resnet_pseudo(dim=dim, freeze_layers=10, full_freeze='N')if model == 'VGG16':model_final = self.VGG16_pseudo(dim=dim, freeze_layers=10, full_freeze='N')train_file_names = glob.glob(f'{train_dir}/*/*')val_file_names = glob.glob(f'{val_dir}/*/*')train_steps_per_epoch = len(train_file_names) / float(batch_size)val_steps_per_epoch = len(val_file_names) / float(batch_size)train_datagen = ImageDataGenerator(horizontal_flip=True, vertical_flip=True, width_shift_range=0.1,height_shift_range=0.1,channel_shift_range=0, zoom_range=0.2, rotation_range=20,preprocessing_function=pre_process)val_datagen = ImageDataGenerator(preprocessing_function=pre_process)train_generator = train_datagen.flow_from_directory(train_dir,target_size=(dim, dim),batch_size=batch_size,class_mode='categorical')val_generator = val_datagen.flow_from_directory(val_dir,target_size=(dim, dim),batch_size=batch_size,class_mode='categorical')print(train_generator.class_indices)# 如果有GPU則直接用GPUphysical_devices = tf.config.experimental.list_physical_devices('GPU')if physical_devices:tf.config.experimental.set_memory_growth(physical_devices[0], True)joblib.dump(train_generator.class_indices, f'{self.outdir}\class_indices.pkl')adam = optimizers.Adam(lr=lr, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.0)model_final.compile(optimizer=adam, loss=["categorical_crossentropy"], metrics=['accuracy'])reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.50, patience=3, min_lr=0.000001)early = EarlyStopping(monitor='val_loss', patience=10, mode='min', verbose=1)logger_path = self.outdir + os.sep + 'keras-epochs_ib.log'logger = CSVLogger(logger_path, separator=',', append=False)model_name = self.outdir + os.sep + 'keras_transfer_learning-run.check'checkpoint = ModelCheckpoint(model_name,monitor='val_loss', mode='min',save_best_only=True,verbose=1)callbacks = [reduce_lr, early, checkpoint, logger]model_final.fit_generator(train_generator, steps_per_epoch=train_steps_per_epoch, epochs=epochs, verbose=1,validation_data=(val_generator), validation_steps=val_steps_per_epoch,callbacks=callbacks,class_weight={0: 0.012, 1: 0.12, 2: 0.058, 3: 0.36, 4: 0.43})model_to_store_path = self.outdir + os.sep + modelmodel_final.save(model_to_store_path)return model_to_store_path, train_generator.class_indices



    從上面日志可以看到,InceptionV3模型得到接近72%的驗證準確度和0.403的二次Kappa得分。當然讀者也可以嘗試使用其它的模型和訓練超參,最后獲得更高的驗證準確度。其它詳細訓練代碼詳見11、全部代碼

    10、測試集預測

    讀者可以通過9、動態(tài)創(chuàng)建小批量數(shù)據(jù)進行訓練保存的模型,對測試集數(shù)據(jù)進行預測,然后獲取到測試集準確度和二次Kappa得分,具體代碼如下:

    # !/usr/bin/env python3.8# -*- coding: utf-8 -*- # @Time : 2023/3/24 9:07 # @Author : Steven Hu # @FileName: TransferLearning.py # @Software: PyCharmimport numpy as npnp.random.seed(1000)import pandas as pd import time import warningswarnings.filterwarnings("ignore") from sklearn.metrics import cohen_kappa_score from tensorflow.keras.models import load_model from tensorflow.keras.preprocessing.image import ImageDataGenerator import cv2# 平均化預處理圖片,使其能滿足ImageNet pre-trained model 圖片要求 def pre_process(img):resized_img = cv2.resize(img, (224, 224), cv2.INTER_LINEAR)resized_img[:, :, 0] = resized_img[:, :, 0] - 103.939resized_img[:, :, 1] = resized_img[:, :, 0] - 116.779resized_img[:, :, 2] = resized_img[:, :, 0] - 123.68return resized_imgdef prediction_test(model_path, test_dir, class_dict=None, dim=224):if class_dict is None:class_dict = {"class0": 0, "class1": 1, "class2": 2, "class3": 3, "class4": 4, }print(test_dir)model = load_model(model_path)test_datagen = ImageDataGenerator(preprocessing_function=pre_process)test_generator = test_datagen.flow_from_directory(test_dir,target_size=(dim, dim),shuffle=False,class_mode='categorical',batch_size=1)filenames = test_generator.filenamesnb_samples = len(filenames)pred = model.predict_generator(test_generator, steps=nb_samples)print(pred)df = pd.DataFrame()df['filename'] = filenamesdf['actual_class'] = df['filename'].apply(lambda x: x[:6])df['actual_class_index'] = df['actual_class'].apply(lambda x: int(class_dict[x]))df['pred_class_index'] = np.argmax(pred, axis=1)k = list(class_dict.keys())v = list(class_dict.values())inv_class_dict = {}for k_, v_ in zip(k, v):inv_class_dict[v_] = k_df['pred_class'] = df['pred_class_index'].apply(lambda x: (inv_class_dict[x]))return dfdef main():start_time = time.time()test_results_path = r'E:\learning_AI\Intelligent_projects_using_python\resutl_test\test_results.csv'model_path = r"E:\learning_AI\Intelligent_projects_using_python\output_transger_learning\keras_transfer_learning-run.check"test_dir = r'E:\Data\resized test 15'test_results_df = prediction_test(model_path, test_dir)test_results_df.to_csv(test_results_path, index=False)print(f'Validation results saved at : {test_results_path}')pred_class_index = np.array(test_results_df['pred_class_index'].values)actual_class_index = np.array(test_results_df['actual_class_index'].values)print(pred_class_index)print(actual_class_index)accuracy = np.mean(actual_class_index == pred_class_index)kappa = cohen_kappa_score(pred_class_index, actual_class_index, weights='quadratic')print("-----------------------------------------------------")print(f'Validation Accuracy: {accuracy}')print(f'Validation Quadratic Kappa Score: {kappa}')print("-----------------------------------------------------")print("Processing Time", time.time() - start_time, ' secs')if __name__ == "__main__":main()


    從上面日志可以看到,InceptionV3模型訓練后的模型再53576張測試圖片集中也取得接近72%的驗證準確度和0.406的二次Kappa得分。當然讀者也可以嘗試使用其它的模型和訓練超參,最后獲得更高的測試準確度。

    11、全部代碼

    #!/usr/bin/env python3.8# -*- coding: utf-8 -*- # @Time : 2023/3/24 9:03 # @Author : Steven Hu # @FileName: TransferLearning_ffd.py # @Software: PyCharmimport numpy as npnp.random.seed(1000)import os import pandas as pd import time import warningswarnings.filterwarnings("ignore") import tensorflow as tf from sklearn.metrics import cohen_kappa_score from tensorflow.keras.models import Sequential, Model from tensorflow.keras.layers import Dense, Dropout, Flatten from tensorflow.keras.layers import GlobalMaxPooling2D, GlobalAveragePooling2D from tensorflow.keras.models import load_modelfrom tensorflow.keras.applications.inception_v3 import InceptionV3 from tensorflow.keras.applications import ResNet50 from tensorflow.keras.applications.vgg16 import VGG16 from tensorflow.keras import optimizers from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint, CSVLogger from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.callbacks import ReduceLROnPlateau import joblib import json from pathlib import Path import glob import cv2def pre_process(img):resized_img = cv2.resize(img, (224, 224), cv2.INTER_LINEAR)resized_img[:, :, 0] = resized_img[:, :, 0] - 103.939resized_img[:, :, 1] = resized_img[:, :, 0] - 116.779resized_img[:, :, 2] = resized_img[:, :, 0] - 123.68return resized_imgclass TransferLearning:def __init__(self):self.path = r"E:\Data\train_validation"self.train_dir = r"E:\Data\train_validation\train"self.val_dir = r"E:\Data\train_validation\validation"self.class_folders = json.loads('["class0","class1","class2","class3","class4"]')self.dim = 224self.lr = float(1e-4)self.batch_size = 128self.epochs = 1self.initial_layers_to_freeze = 10self.model = "InceptionV3"self.folds = 5self.outdir = "E:\learning_AI\Intelligent_projects_using_python\output_transger_learning"def inception_pseudo(self, dim=224, freeze_layers=30, full_freeze='N'):# model = InceptionV3(weights='imagenet', include_top=False) # 如果下載不了,則直接使用權重地址賦值到weights參數(shù)上model = InceptionV3(weights=r"E:\learning_AI\Intelligent_projects_using_python\weights\inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5",include_top=False)x = model.output # 從預訓練的網(wǎng)絡中提取最后一個最大池化層的輸出x = GlobalAveragePooling2D()(x) #確保池輸出是一維數(shù)組格式,而不是二維點陣格式x = Dense(dim, activation='relu')(x) # 連接一個全連接層,這個dim可以自定義成其它值,比如512x = Dropout(0.5)(x) # 防止過擬合x = Dense(dim, activation='relu')(x) # 再連接一個全連接層,這個dim可以自定義成其它值,比如512x = Dropout(0.5)(x) # 防止過擬合out = Dense(5, activation='softmax')(x) # 這里輸出必須是分類數(shù)量的單元數(shù)model_final = Model(input=model.input, outputs=out)if full_freeze != 'N':for layer in model.layers[0:freeze_layers]:layer.trainable = Falsereturn model_final# ResNet50 Model for transfer Learningdef resnet_pseudo(self, dim=224, freeze_layers=10, full_freeze='N'):# model = ResNet50(weights='imagenet', include_top=False) # 如果下載不了,則直接使用權重地址賦值到weights參數(shù)上model = ResNet50(weights=r"E:\learning_AI\Intelligent_projects_using_python\weights\resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5",include_top=False)x = model.output # 從預訓練的網(wǎng)絡中提取最后一個最大池化層的輸出x = GlobalAveragePooling2D()(x) #確保池輸出是一維數(shù)組格式,而不是二維點陣格式x = Dense(dim, activation='relu')(x) # 連接一個全連接層,這個dim可以自定義成其它值,比如512x = Dropout(0.5)(x) # 防止過擬合x = Dense(dim, activation='relu')(x) # 再連接一個全連接層,這個dim可以自定義成其它值,比如512x = Dropout(0.5)(x) # 防止過擬合out = Dense(5, activation='softmax')(x) # 這里輸出必須是分類數(shù)量的單元數(shù)model_final = Model(input=model.input, outputs=out)if full_freeze != 'N':for layer in model.layers[0:freeze_layers]:layer.trainable = Falsereturn model_final# VGG16 Model for transfer Learningdef VGG16_pseudo(self, dim=224, freeze_layers=10, full_freeze='N'):model = VGG16(weights='imagenet', include_top=False)# model = VGG16(weights=r'E:\learning_AI\Intelligent_projects_using_python\weights\vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5', include_top=False) # 如果上面運行出現(xiàn)網(wǎng)絡下載錯誤,建議先下載權重后,使用權重地址x = model.output # 從預訓練的網(wǎng)絡中提取最后一個最大池化層的輸出x = GlobalAveragePooling2D()(x) #確保池輸出是一維數(shù)組格式,而不是二維點陣格式x = Dense(dim, activation='relu')(x) # 連接一個全連接層,這個dim可以自定義成其它值,比如512x = Dropout(0.5)(x) # 防止過擬合x = Dense(dim, activation='relu')(x) # 再連接一個全連接層,這個dim可以自定義成其它值,比如512x = Dropout(0.5)(x) # 防止過擬合out = Dense(5, activation='softmax')(x) # 這里輸出必須是分類數(shù)量的單元數(shù)model_final = Model(input=model.input, outputs=out)if full_freeze != 'N':for layer in model.layers[0:freeze_layers]:layer.trainable = Falsereturn model_finaldef train_model(self, train_dir, val_dir, batch_size=16, epochs=40, dim=224, lr=1e-5, model='ResNet50'):model_final = self.inception_pseudo(dim=dim, freeze_layers=30, full_freeze='N') # 默認選擇為'InceptionV3'if model == 'Resnet50':model_final = self.resnet_pseudo(dim=dim, freeze_layers=10, full_freeze='N')if model == 'VGG16':model_final = self.VGG16_pseudo(dim=dim, freeze_layers=10, full_freeze='N')train_file_names = glob.glob(f'{train_dir}/*/*')val_file_names = glob.glob(f'{val_dir}/*/*')train_steps_per_epoch = len(train_file_names) / float(batch_size)val_steps_per_epoch = len(val_file_names) / float(batch_size)train_datagen = ImageDataGenerator(horizontal_flip=True, vertical_flip=True, width_shift_range=0.1,height_shift_range=0.1,channel_shift_range=0, zoom_range=0.2, rotation_range=20,preprocessing_function=pre_process)val_datagen = ImageDataGenerator(preprocessing_function=pre_process)train_generator = train_datagen.flow_from_directory(train_dir,target_size=(dim, dim),batch_size=batch_size,class_mode='categorical')val_generator = val_datagen.flow_from_directory(val_dir,target_size=(dim, dim),batch_size=batch_size,class_mode='categorical')print(train_generator.class_indices)# 如果有GPU則直接用GPUphysical_devices = tf.config.experimental.list_physical_devices('GPU')if physical_devices:tf.config.experimental.set_memory_growth(physical_devices[0], True)joblib.dump(train_generator.class_indices, f'{self.outdir}\class_indices.pkl')adam = optimizers.Adam(lr=lr, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.0)model_final.compile(optimizer=adam, loss=["categorical_crossentropy"], metrics=['accuracy'])reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.50, patience=3, min_lr=0.000001)early = EarlyStopping(monitor='val_loss', patience=10, mode='min', verbose=1)logger_path = self.outdir + os.sep + 'keras-epochs_ib.log'logger = CSVLogger(logger_path, separator=',', append=False)model_name = self.outdir + os.sep + 'keras_transfer_learning-run.check'checkpoint = ModelCheckpoint(model_name,monitor='val_loss', mode='min',save_best_only=True,verbose=1)callbacks = [reduce_lr, early, checkpoint, logger]model_final.fit_generator(train_generator, steps_per_epoch=train_steps_per_epoch, epochs=epochs, verbose=1,validation_data=(val_generator), validation_steps=val_steps_per_epoch,callbacks=callbacks,class_weight={0: 0.012, 1: 0.12, 2: 0.058, 3: 0.36, 4: 0.43})model_to_store_path = self.outdir + os.sep + modelmodel_final.save(model_to_store_path)return model_to_store_path, train_generator.class_indicesdef inference(self, model_path, test_dir, class_dict, dim=224):print(test_dir)model = load_model(model_path)test_datagen = ImageDataGenerator(preprocessing_function=pre_process)test_generator = test_datagen.flow_from_directory(test_dir,target_size=(dim, dim),shuffle=False,class_mode='categorical',batch_size=1)filenames = test_generator.filenamesnb_samples = len(filenames)pred = model.predict_generator(test_generator, steps=nb_samples)print(pred)df = pd.DataFrame()df['filename'] = filenamesdf['actual_class'] = df['filename'].apply(lambda x: x[:6])df['actual_class_index'] = df['actual_class'].apply(lambda x: int(class_dict[x]))df['pred_class_index'] = np.argmax(pred, axis=1)k = list(class_dict.keys())v = list(class_dict.values())inv_class_dict = {}for k_, v_ in zip(k, v):inv_class_dict[v_] = k_df['pred_class'] = df['pred_class_index'].apply(lambda x: (inv_class_dict[x]))return dfdef main(self):start_time = time.time()print('Data Processing..')self.num_class = len(self.class_folders)model_to_store_path, class_dict = self.train_model(self.train_dir, self.val_dir,batch_size=self.batch_size,epochs=self.epochs, dim=self.dim, lr=self.lr,model=self.model)print("Model saved to dest:", model_to_store_path)# Validatione evaluate resultsfolder_path = Path(f'{self.val_dir}')val_results_df = self.inference(model_to_store_path, folder_path, class_dict, self.dim)val_results_path = f'{self.outdir}/val_results.csv'val_results_df.to_csv(val_results_path, index=False)print(f'Validation results saved at : {val_results_path}')pred_class_index = np.array(val_results_df['pred_class_index'].values)actual_class_index = np.array(val_results_df['actual_class_index'].values)print(pred_class_index)print(actual_class_index)accuracy = np.mean(actual_class_index == pred_class_index)kappa = cohen_kappa_score(pred_class_index, actual_class_index, weights='quadratic')print("-----------------------------------------------------")print(f'Validation Accuracy: {accuracy}')print(f'Validation Quadratic Kappa Score: {kappa}')print("-----------------------------------------------------")print("Processing Time", time.time() - start_time, ' secs')if __name__ == "__main__":obj = TransferLearning()obj.main()

    其它資料下載

    如果大家想繼續(xù)了解人工智能相關學習路線和知識體系,歡迎大家翻閱我的另外一篇博客《重磅 | 完備的人工智能AI 學習——基礎知識學習路線,所有資料免關注免套路直接網(wǎng)盤下載》
    這篇博客參考了Github知名開源平臺,AI技術平臺以及相關領域專家:Datawhale,ApacheCN,AI有道和黃海廣博士等約有近100G相關資料,希望能幫助到所有小伙伴們。

    總結

    以上是生活随笔為你收集整理的手把手带你从0完成医疗行业影像图像检测三大经典模型InceptionV3-RestNet50-VGG16(附python源代码及数据库)——改变世界经典人工智能项目实战(一)手把手教学迁移学习的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。