<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 三、TensorFlow 中的圖像分類 圖像分類是指根據圖像內容將圖像分類的問題。 讓我們從分類的示例任務開始,其中圖片可能是狗的圖像,也可能不是。 某人可能要完成此任務的一種簡單方法是,像在第 1 章中所做的那樣,獲取輸入圖像,將其重塑為向量,然后訓練線性分類器(或其他某種分類器)。 但是,您很快就會發現此主意不好,原因有幾個。 除了不能很好地縮放到輸入圖像的大小之外,線性分類器將很難將一個圖像與另一個圖像分開。 與可以在圖像中看到有意義的圖案和內容的人類相反,計算機只能看到從 0 到 255 的數字數組。對于同一類的不同圖像,這些數字在相同位置的廣泛波動導致無法直接將它們使用為分類器的輸入。 從**加拿大高級研究學院**(**CIFAR**)數據集中獲取的這 10 張示例狗圖像完美地說明了此問題。 狗的外觀不僅有所不同,而且它們在鏡頭前的姿勢和位置也有所不同。 對于機器來說,每個圖像一目了然,完全沒有共同點,而我們人類卻可以清楚地看到它們都是狗: ![](https://img.kancloud.cn/b0/5c/b05c53ad675fc3cc98d6c183eaee3d4a_32x32.png) ![](https://img.kancloud.cn/09/05/0905b6de85439dc66c7c1af9a21586a0_32x32.png) ![](https://img.kancloud.cn/65/6c/656c1a4e3cd79abfaef368d96a63a76d_32x32.png) ![](https://img.kancloud.cn/b2/3c/b23cb6be4c90531df8100743c9408047_32x32.png) ![](https://img.kancloud.cn/2f/1a/2f1ab9b87aa95399b8499390ab9acf99_32x32.png) ![](https://img.kancloud.cn/96/66/96668d039d86d1ac96a10c3393bcca09_32x32.png) ![](https://img.kancloud.cn/82/22/8222e3fe9cf321ebf583d11d4ae47fda_32x32.png) ![](https://img.kancloud.cn/60/80/6080a501794eac504dff5e087e760d75_32x32.png) ![](https://img.kancloud.cn/3f/02/3f022ed82774506f3ad67443d94921b5_32x32.png) ![](https://img.kancloud.cn/96/64/966429c414c1d174b1c34a10497ab57a_32x32.png) 一個更好的解決方案是告訴計算機從輸入圖像中提取一些有意義的特征,例如常見的形狀,紋理或顏色。 然后,我們可以使用這些功能而不是原始輸入圖像作為分類器的輸入。 現在,我們正在尋找圖像中這些功能的存在,以告訴我們圖像是否包含我們要識別的對象。 這些提取的特征在我們看來將僅僅是一個高維向量(但通常比原始圖像空間要小得多),可以用作分類器的輸入。 多年來開發的一些著名的特征提取方法是**尺度不變特征**(**SIFT**),**最大穩定的末端區域**(**MSER**),**本地二進制模式**(**LBP**)和**直方圖定向梯度**(**HOG**)。 當使用卷積神經網絡進行圖像分類時,2012 年是計算機視覺(以及隨后的其他機器學習領域)最大的轉折點之一,這標志著如何解決這一任務(以及許多其他問題)的方式發生了轉變。 我們不是專注于手工制作更好的特征以從圖像中提取,而是使用數據驅動的方法來找到代表問題數據集的最佳特征集。 CNN 將使用大量訓練圖像,并自己學習代表數據的最佳特征,以解決分類任務。 在本章中,我們將介紹以下主題: * 看一下用于分類的損失函數 * Imagenet 和 CIFAR 數據集 * 訓練 CNN 對 CIFAR 數據集進行分類 * 數據 API 簡介 * 如何初始化權重 * 如何規范化模型來獲得更好的結果 # CNN 模型架構 圖像分類模型的關鍵部分是其 CNN 層。 這些層將負責從圖像數據中提取特征。 這些 CNN 層的輸出將是一個特征向量,就像以前一樣,我們可以將其用作所選分類器的輸入。 對于許多 CNN 模型,分類器將只是連接到我們 CNN 輸出的完全連接層。 如第 1 章,“TensorFlow 簡介和設置”中所示,我們的線性分類器只是一個全連接層; 除了層的大小和輸入會有所不同之外,這里就是這種情況。 重要的是要注意,分類或回歸問題(例如定位)(或其他使用圖像的其他問題)所使用的 CNN 架構在本質上是相同的。 唯一真正的不同是,在 CNN 層完成特征提取之后會發生什么。 例如,一個差異可能是用于不同任務的損失函數,如下圖所示: ![](https://img.kancloud.cn/29/bc/29bc29cbb11e565a8a37797671f18050_627x436.jpg) 當我們著眼于 CNN 可以解決的各種問題時,您會在本書中看到重復出現的模式。 顯然,可以使用 CNN 從輸入數據中提取一些有意義的特征向量來解決許多涉及圖像的任務,然后根據任務以某種方式對其進行處理并將其饋入不同的損失函數。 現在,讓我們通過查看常用的損失函數來開始并專注于圖像分類任務。 # 交叉熵損失(對數損失) 圖像分類的最簡單形式是二分類。 在這里,我們有一個分類器,該分類器只有一個要分類的對象,例如狗/不是狗。 在這種情況下,我們可能使用的損失函數是二元交叉熵損失。 真實標簽`p`與模型預測`q`之間的交叉熵函數定義為: ![](https://img.kancloud.cn/63/f1/63f11cde6f60f7b80dc5359adaf64a9a_2010x430.png) `i`是我們標簽和預測的每個可能元素的索引。 但是,當我們處理只有兩個可能結果`y = 1`和`y = 0`的二元情況時,可以簡化`p ∈ {y, 1-y}`和`q ∈ {y_hat, 1-y_hat}`我們得到: ![](https://img.kancloud.cn/91/ab/91abff3f049ee3f2a4677c79490b2e3c_3340x220.png) 這是等效的 迭代`m`訓練示例,將要最小化的成本函數`L`變為: ![](https://img.kancloud.cn/15/ad/15ad90f934332e0a70496c2e34eb4ccc_4080x540.png) 這在直覺上是正確的,因為當`y=1`時,我們要最小化`L(y, y_hat) = - log(y_hat)`,需要最大化`y_hat`;當`y=0`時,我們要最小化`L(y, y_hat) = - log(1 - y_hat)`,需要最小化`y_hat`。 在 TensorFlow 中,可以在`tf.losses`模塊中找到二元交叉熵損失。 知道我們模型的原始輸出`y_hat`的名稱是`logits`很有用。 在將其傳遞給交叉熵損失之前,我們需要對其應用 **Sigmoid** 函數,以便我們的輸出在 0 到 1 之間縮放。TensorFlow 實際上將所有這些步驟組合為一個操作,如下面的代碼。 TensorFlow 還將為我們平均分批量損失。 ```py loss = tf.losses.sigmoid_cross_entropy(multi_class_labels=labels_in, logits=model_prediction) ``` # 多類交叉熵損失 多類交叉熵損失用于多類分類中,例如第 2 章,“深度學習和卷積神經網絡”中的 MNIST 數字分類問題。 像上面一樣,我們使用交叉熵函數,經過幾次計算,我們為每個訓練示例獲得了多類交叉熵損失`L`: ![](https://img.kancloud.cn/82/ef/82efda468858a7b3bdc9d65d0a5ef2a0_2150x580.png) 在此,`y^(k)`為 0 或 1,表示類別標簽`k`是否是用于預測`y_hat^(k)`的正確分類。 要使用此損失,我們首先需要向模型中最終 FC 層的輸出`y_hat`添加 softmax 激活。 交叉熵與 softmax 的組合如下所示: ![](https://img.kancloud.cn/f8/99/f89933ab3772f4a8df4ef0b5279b6cfe_2710x630.png) 知道我們模型的原始輸出`y_hat`的名稱是`logits`很有用。 對率是傳遞給 softmax 函數的內容。 softmax 函數是 Sigmoid 函數的多類版本。 一旦通過 softmax 函數,我們就可以使用我們的多類交叉熵損失。 TensorFlow 實際上將所有這些步驟組合為一個操作,如下所示: ```py loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=model_logits, labels=labels_in)) ``` 我們必須使用`tf.reduce_mean`,因為我們將獲得批次中每個圖像的損失值。 我們使用`tf.reduce_mean`來獲取批次的平均損失。 我們可以像上面一樣再次使用`tf.losses`模塊,特別是`tf.losses.softmax_cross_entropy`模塊,然后我們不需要`tf.reduce_mean`,但我們決定向您展示一種不同的方式,以便您可以看到,在 TensorFlow 中也有很多方法可以使用。 隨著 TensorFlow 的發展,實現相同結果的不同方法也越來越多,而且通常沒有比其他方法差很多的方法。 # 訓練/測試數據集拆分 暫時請注意,我們需要將數據集分為兩組:訓練和測試。 正如第 1 章,“TensorFlow 簡介和設置”中所述,這是因為我們需要以某種方式檢查模型是否能夠從其自身的訓練樣本中進行泛化(是否能夠正確識別訓練中從未見過的圖像)。 如果我們的模型不能做到這一點,對我們來說就沒有太大用處。 還有一些其他要記住的重要點: * 訓練和測試數據必須來自相同的分布(因此在拆分之前合并并混洗所有數據) * 訓練集通常大于測試集(例如,訓練:占總數的 70%,測試:占總數的 30%)。 對于我們在前幾章中要處理的示例,這些基礎知識就足夠了,但是在隨后的章節中,我們將更詳細地介紹如何為大型項目正確設置數據集。 # 數據集 在本節中,我們將討論圖像分類中使用的最重要和最著名的最新數據集。 這是必要的,因為對計算機視覺的任何細讀都可能與它們重疊(包括本書!)。 在卷積神經網絡到來之前,研究界在圖像分類比賽中使用的兩個主要數據集是 Caltech 和 PASCAL 數據集。 加州理工學院的數據集由加州理工學院建立,并發布了兩個版本。 Caltech-101 于 2003 年發布,包含 101 個類別,每個類別約 40 至 800 張圖像; Caltech-256 于 2007 年發布,具有 256 個對象類別,總共包含 30607 張圖像。 這些圖片是從 Google 圖片和 PicSearch 收集的,其大小約為`300x400`像素。 Pascal **視覺對象類**(**VOC**)挑戰成立于 2005 年。每年組織到 2012 年,它為*圖像分類,對象檢測,分割和操作分類*提供了廣泛的自然圖像的著名基準數據集。 它是一個多樣化的數據集,包含來自各種大小,姿勢,方向,照明和遮擋的 flickr 的圖像。 從 2005 年(僅四個類別:自行車,汽車,摩托車和人,訓練/驗證/測試:包含 5 個圖像的 2578 個注釋對象的 1578 張圖像)到 2012 年(二十個類別,訓練/驗證數據具有 11,530 張圖片,包含 27,450 個 ROI 注釋對象和 6,929 個分割)。 重大變化來自 PASCAL(VOC)2007 挑戰賽,當時類的數量從 4 個增加到 20 個,并且此后一直固定。 分類任務的評估指標已更改為平均精度。 僅在 VOC 2007 挑戰賽之前提供測試數據的注釋。 隨著更復雜的分類方法的出現,前面的數據集是不夠的,以下幾節中介紹的 ImageNet 數據集和 CIFAR 數據集成為分類測試的新標準。 # ImageNet ImageNet 數據集由 Alex Berg(哥倫比亞大學),Jia Deng(普林斯頓大學)和 Lii-Fei Li(斯坦福大學)在 2010 年共同創建,旨在進行大規模視覺識別的測試比賽, *PASCAL 可視對象類挑戰*,2010 年。數據集是代表 WordNet 內容的圖像的集合。 WordNet 是英語的詞匯數據庫。 它以分層結構將英語單詞分成稱為**同義詞集**的同義詞集。 以下屏幕截圖顯示了名詞的 WordNet 結構。 括號中的數字是子樹中的同義詞集的數量。 ![](https://img.kancloud.cn/f1/3e/f13e1fa854a55af0c971aec103c5f993_514x506.png) 圖像分類算法的發展幾乎解決了現有數據集上的分類難題,因此需要一個新的數據集,以實現大規模圖像分類。 這更接近現實情況,在這種情況下,我們希望機器描述模擬人的能力的任意圖像的內容。 與上一代產品的分類數量在 100 年代相比,ImageNet 提供了超過 1000 萬個高質量圖像,覆蓋了 10,000 多個類。 這些類別中的許多類別是相互關聯的,這使分類任務更具挑戰性,例如,區分許多品種的狗。 由于數據集非常龐大,因此很難使用其中存在的所有類別對每個圖像進行注釋,因此按照慣例,每個圖像僅被標記為一個類別。 自 2010 年以來,一年一度的 ImageNet 大規模視覺識別挑戰賽(ILSVRC)挑戰集中于圖像分類,單對象定位和檢測。 對象分類挑戰的數據包括 120 萬張圖像(來自 1000 個類別/同義詞),訓練數據,50,000 張驗證數據圖像和 100,000 張測試數據圖像。 在分類挑戰中,用于評估算法的主要指標是前 5 位錯誤率。 該算法允許給出五個預測類別,并且如果至少一個預測類別與真實情況標簽匹配,則不會受到懲罰。 正式地,如果我們讓`i`為圖像,讓`C[i]`為真實情況標簽。 然后,我們有了預測的標簽`c[ij], j ∈ [1, 5]`,其中至少一個等于`C[i]`才能將其視為成功的預測。 考慮預測誤差如下: ![](https://img.kancloud.cn/7a/00/7a003075e2d3fa64eeac7687e8935273_1580x480.png) 那么,算法的最終誤差就是測試圖像上出錯的比例,如下所示: ![](https://img.kancloud.cn/ab/77/ab77ebdcb702aab3cf182897ff825e42_1790x580.png) Imagenet 是近年來深度學習蓬勃發展的主要原因之一。 在深度學習開始流行之前,ILSVRC 的前五位錯誤率大約為 28%,并且絲毫沒有下降太多。 但是,在 2012 年,挑戰賽的冠軍 SuperVision 將前 5 名的分類錯誤率降低到了 16.4%。 團隊模型(現在稱為 AlexNet)是一個深度卷積神經網絡。 這項巨大的勝利喚醒了人們使用 CNN 的力量,它成為許多現代 CNN 架構的墊腳石。 在接下來的幾年中,CNN 模型繼續占主導地位,前 5 個錯誤率持續下降。 2014 年冠軍 GoogLeNet 將錯誤率降低到 6.7%,而 ResNet 在 2015 年將錯誤率再次降低了一半,降至 3.57%。 此后,2017 年的贏家“WMW 擠壓和激勵網絡”產生了 2.25% 的誤差,出現了較小的改進。 # CIFAR CIFAR-10 和 CIFAR-100 數據集是 Alex Krizhevsky,Vinod Nair 和 Geoffrey Hinton 收集的小型(與現代標準相比)圖像數據集。 這些數據集被研究界廣泛用于圖像分類任務。 它們被認為具有挑戰性,因為圖像質量非常低并且圖像中的對象有時是部分可見的。 同時,由于圖像較小,因此數據集很方便,因此研究人員可以快速在它們上產生結果。 CIFAR-100 增加了挑戰,因為每個類別的圖像數量很少,并且類別的數量也很大。 CIFAR10 和 CIFAR100 數據集每個包含 60,000 張圖像。 兩個數據集中的圖像均為`32x32x3` RGB 彩色圖像。 在 CIFAR-10 中,有 10 個類別,每個類別有 6,000 張圖像。 數據集分為 50,000 個訓練圖像和 10,000 個測試圖像。 以下是 CIFAR-10 數據集的類列表和每個類的一些隨機圖像,因此您可以看到其外觀: ![](https://img.kancloud.cn/62/59/62599a6871a28931264aaed058b7b4e1_488x411.jpg) CIFAR-100 具有 100 個類別,每個類別 600 張圖像。 這 100 個類別分為 20 個超類。 每個圖像都有一個**精細**標簽(它屬于的類)和一個**粗糙**標簽(它屬于的超類)。 CIFAR-100 中的類和超類的列表可在[這個頁面](https://www.cs.toronto.edu/~kriz/cifar.html)中找到。 將類別的數量從粗糙(20)增加到精細(100)有助于最大程度地提高類別間的可變性。 這意味著我們希望模型考慮圖像中兩個看起來相似的對象屬于不同的類。 例如,一張床和一張沙發看起來相似但不完全相同,將它們放在單獨的類中將確保它們與受訓模型看起來不同。 CIFAR 的算法評估過程與 ImageNet 中的相同。 據 Saining Xie 等人報道,CIFAR-10 的報告最好的 top-1 誤差為 3.58%,而 CIFAR-100 的誤差為 17.31%。 深入神經網絡的聚合殘差轉換中,他們介紹了新穎的 ResNeXt 架構。 可以在[這里](http://rodrigob.github.io/are_we_there_yet/build/classification_datasets_results.html)和[這里](https://github.com/RedditSota/state-of-the-art-result-for-machine-learning-problems)找到在 CIFAR-10 和 CIFAR-100 上將深度學習結果用于圖像分類的最新技術。 # 加載 CIFAR 可以從前面提到的 Python,Matlab 和二進制版本的官方網站下載數據集。 有多種加載和讀取這些數據集的方法。 實際上,在我們的 TensorFlow 實現中,我們使用 [Keras 庫](https://keras.io/datasets/)加載它,該庫現在是`tf.keras`模塊中 TensorFlow 的一部分。 在這里,我們提供了一些示例代碼來加載 CIFAR-10 數據集,但是 CIFAR-100 數據集的加載方式幾乎相同: ```py import tensorflow as tf from tf.keras.datasets import cifar10 (x_train, y_train), (x_test, y_test) = cifar10.load_data() print('x_train shape:',x_train.shape) print('y_train shape:',y_train.shape) print('x_test shape:',x_test.shape) print('y_test shape:',y_test.shape) print('x_train.shape[0]:',training samples) print('x_test.shape[0]:',test samples) # Convert class vectors to binary class matrices y_train = tf.keras.utils.to_categorical(y_train,10) y_test = tf.keras.utils.to_categorical(y_test,10) ``` 此代碼返回兩個元組: ```py x_train, x_test: uint8 array of RGB image data with shape (num_samples, 3, 32, 32) y_train, y_test: uint8 array of category labels (integers in range 0-9) with shape (num_samples,) ``` 前面代碼的打印語句的輸出如下: ```py x_train shape:(50000,32,32,3 y_train shape:(50000,1) x_test shape:(10000,32,32,3) y_test shape:(10000,1) ``` 同樣,使用以下命令加載 CIFAR-100 數據集: ```py from tf.keras.datasets import cifar100 (x_train, y_train), (x_test, y_test) = cifar100.load_data(label_mode='fine') ``` # 將 TensorFlow 用于圖像分類 在本節中,我們將向您展示如何實現相對簡單的 CNN 架構。 我們還將研究如何訓練它對 CIFAR-10 數據集進行分類。 首先導入所有必需的庫: ```py import fire import numpy as np import os import tensorflow as tf from tf.keras.datasets import cifar10 ``` 我們將定義一個將實現訓練過程的 Python 類。 類名是`Train`,它實現兩種方法:`build_graph`和`train`。 當執行主程序時,將觸發`train`功能: ```py class Train: __x_ = [] __y_ = [] __logits = [] __loss = [] __train_step = [] __merged_summary_op = [] __saver = [] __session = [] __writer = [] __is_training = [] __loss_val = [] __train_summary = [] __val_summary = [] def __init__(self): pass def build_graph(self): [...] def train(self, save_dir='./save', batch_size=500): [...] if __name__ == '__main__': cnn= Train() cnn.train ``` # 建立 CNN 圖 讓我們通過`build_graph`函數進行詳細介紹,該函數包含網絡定義,損失函數和所使用的優化器。 首先,我們通過為輸入定義占位符來啟動函數。 我們將使用兩個占位符在圖中提供數據和標簽:`__x_`和`__y_`。 占位符`__x_`將保存我們輸入的 RGB 圖像,而占位符`__y_` 存儲一個對應類別的熱門標簽。 在定義占位符形狀的`N`部分時,我們使用`None`,因為這告訴 TensorFlow 該值可以是任何值,并且在執行圖時將提供該值: ```py def build_graph(self): self.__x_ = tf.placeholder("float", shape=[None, 32, 32, 3], name='X') self.__y_ = tf.placeholder("int32", shape=[None, 10], name='Y') self.__is_training = tf.placeholder(tf.bool) ``` 然后,我們將在`name_scope`模型中定義我們的網絡。 `Name_scope`返回定義 TensorFlow 操作時使用的上下文管理器。 該上下文管理器驗證變量是否來自同一圖,將該圖設為默認圖,并在該圖中推送名稱范圍。 對于此模型,我們將構建一個具有三個卷積層,三個池化層和兩個完全連接層的簡單 CNN。 我們使用`tf.layers` API 來構建 CNN 層。 `tf.reshape`函數將張量從最后一個池化層重塑為一維張量,以匹配密集層期望接收的量。 最后一層的輸出分配給`self.__logits`,它是將作為輸入傳遞到我們的損失函數的張量: ```py with tf.name_scope("model") as scope: conv1 = tf.layers.conv2d(inputs=self.__x_, filters=64, kernel_size=[5, 5], padding="same", activation=tf.nn.relu) pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2) conv2 = tf.layers.conv2d(inputs=pool1, filters=64, kernel_size=[5, 5], padding="same", activation=tf.nn.relu) pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2) conv3 = tf.layers.conv2d(inputs=pool2, filters=32, kernel_size=[5, 5], padding="same", activation=tf.nn.relu) pool3 = tf.layers.max_pooling2d(inputs=conv3, pool_size=[2, 2], strides=2) pool3_flat = tf.reshape(pool3, [-1, 4 * 4 * 32]) # FC layers FC1 = tf.layers.dense(inputs=pool3_flat, units=128, activation=tf.nn.relu) FC2 = tf.layers.dense(inputs=FC1, units=64, activation=tf.nn.relu) self.__logits = tf.layers.dense(inputs=FC2, units=10) ``` 下一步是在名稱范圍`loss_func`中定義損失函數。 此處使用的損失函數是 softmax 交叉熵,如前所述,我們使用`tf.reduce_mean`對整個批次的損失進行平均。 我們創建變量來保存訓練`loss __loss`和驗證損失`__loss_val`,并將這些標量添加到 TensorFlow 摘要數據中,以便稍后在 TensorBoard 中顯示: ```py with tf.name_scope("loss_func") as scope: self.__loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=self.__logits, labels=self.__y_)) self.__loss_val = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=self.__logits, labels=self.__y_)) # Add loss to tensorboard self.__train_summary = tf.summary.scalar("loss_train", self.__loss) self.__val_summary = tf.summary.scalar("loss_val", self.__loss_val) ``` 定義模型和損失函數后,我們需要指定將用于最小化損失的優化函數。 我們在這里選擇的優化函數是 Adam 優化器,它在名稱范圍*優化器*中定義。 # 學習率調度 在上一章中,我們簡要提到了在訓練過程中保持恒定的學習率可能會出現的問題。 隨著我們模型的開始學習,我們的初始學習率很可能會變得太大而無法繼續學習。 梯度下降更新將開始超出或繞過我們的最小值; 結果,損失函數的值不會降低。 為了解決這個問題,我們可以不時降低學習率的值。 這個過程稱為學習率調度,有幾種流行的方法。 第一種方法是在訓練過程中的固定時間步長(例如,當訓練完成 33% 和 66% 時)降低學習率。 通常,當達到這些設置時間時,您會將學習率降低 10 倍。 第二種方法涉及根據時間步長的指數或二次函數降低學習率。 可以執行此操作的函數的示例如下: ```py decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps) ``` 通過使用這種方法,學習率會隨著訓練時間的推移而平穩降低。 最后一種方法是使用我們的驗證集,并查看驗證集上的當前準確率。 在驗證準確率不斷提高的同時,我們對學習率無能為力。 一旦驗證準確率停止增加,我們就將學習率降低某種程度。 重復此過程,直到訓練結束。 所有方法都可以產生良好的結果,當您進行訓練以查看哪種方法更適合您時,可能值得嘗試所有這些不同的方法。 對于此特定模型,我們將使用第二種方法,即學習率呈指數衰減。 我們使用 TensorFlow 操作`tf.train.exponential_decay`來執行此操作,該操作遵循前面顯示的公式。 作為輸入,它采用當前的學習率,全局步長,衰減之前的步數和衰減率。 在每次迭代中,當前的學習率都會提供給我們的 Adam 優化器,后者使用`minimize`函數,該函數使用梯度下降來使損失最小化并將`global_step`變量增加 1。 最后,在訓練期間,將`learning_rate`和`global_step`添加到摘要數據以在 TensorBoard 上顯示: ```py with tf.name_scope("optimizer") as scope: global_step = tf.Variable(0, trainable=False) starter_learning_rate = 1e-3 # decay every 10000 steps with a base of 0.96 function learning_rate = tf.train.exponential_decay(starter_learning_rate, global_step, 1000, 0.9, staircase=True) self.__train_step = tf.train.AdamOptimizer(learning_rate).minimize(self.__loss, global_step=global_step) tf.summary.scalar("learning_rate", learning_rate) tf.summary.scalar("global_step", global_step) ``` 盡管 Adam 優化器會自動為我們調整和降低學習率,但我們仍然發現,采用某種形式的學習率調度也可以改善結果。 一旦定義了圖的所有組件,就將圖中收集的所有摘要合并到`__merged_summary_op`中,并通過`tf.global_variables_initializer()`初始化圖的所有變量。 自然,在訓練模型時,我們希望將網絡權重存儲為二進制文件,以便我們可以將其加載回去以執行正向傳播。 TensorFlow 中的那些二進制文件稱為檢查點,它們將變量名稱映射到張量值。 要在檢查點之間保存和還原變量,我們使用`Saver`類。 為避免填滿磁盤,保護程序會自動管理檢查點文件。 例如,他們每訓練一次`N`小時,就只能保留`N`個最新文件或一個檢查點。 在我們的例子中,我們將`max_to_keep`設置為`None`,這意味著將保留所有檢查點文件: ```py # Merge op for tensorboard self.__merged_summary_op = tf.summary.merge_all() # Build graph init = tf.global_variables_initializer() # Saver for checkpoints self.__saver = tf.train.Saver(max_to_keep=None) ``` 另外,我們可以指定`tf.GPUOptions`要使用的 GPU 內存比例。 對象會話封裝了執行操作和求值張量的環境。 創建`FileWriter`對象以將摘要和事件存儲到文件后,`__session.run(init)`方法運行 TensorFlow 計算的一個步驟,方法運行必要的圖片段來執行每個操作,并評估在`init`中初始化的每個張量作為圖的一部分: ```py # Avoid allocating the whole memory gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.6) self.__session = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options)) # Configure summary to output at given directory self.__writer = tf.summary.FileWriter("./logs/cifar10", self.__session.graph) self.__session.run(init) ``` # `tf.data` API 簡介 在繼續之前,我們將看一下 TensorFlow 處理數據輸入到我們可能訓練的任何模型的方式。 TensorFlow `tf.data` API 為我們提供了輕松構建復雜的輸入流水線可能需要的所有工具。 您可能通常會構建的一個流水線將涉及加載原始訓練數據,對其進行一些預處理,改組,然后將其分批準備進行訓練。 `tf.data` API 使我們能夠使用簡單且可重復使用的代碼段輕松地完成所有這些步驟。 您需要了解`tf.data` API 的兩個主要組件。 首先是`tf.data.Dataset`; 這就是您的原始數據。 更具體地說,它包含一系列元素,其中每個元素包含一個或多個張量對象。 對于圖像分類任務,一個元素將是一個訓練示例,并且它將由兩個張量組成-一個張量用于圖像,一個張量用于其相應的標簽。 第二個成分是`tf.data.Iterator`。 這些允許您從數據集中提取元素,并充當數據集和模型代碼之間的連接。 TensorFlow 中有幾種不同類型的迭代器,它們都有不同的用途,涉及不同的使用難度。 創建數據集可以通過兩種方式完成。 第一種方法是通過創建數據源。 一個簡單的例子是使用`tf.data.Dataset.from_tensor_slices()`,它將根據一個或多個張量對象的切片創建一個數據集。 產生數據集的另一種方法是在現有數據集上使用數據集轉換。 這樣做將返回合并了所應用轉換的新數據集。 重要的是要了解所有輸入流水線必須以數據源開頭。 一旦有了`Dataset`對象,通常對它應用所有鏈接在一起的多個轉換。 目前,一些簡單的轉換示例包括`Dataset.batch()`和`Dataset.repeat()`,它們從`Dataset`對象返回一批具有指定大小的批次,當`Dataset`內容到達末尾時,它將繼續重復該內容。 一種可以多次遍歷數據集的簡單方法(`count`參數)。 現在我們已經建立了數據集,我們可以使用`tf.data.Iterators`進行迭代并從中提取元素。 同樣,有幾種不同的迭代器可供使用,但是我們將使用的最簡單的迭代器是一發迭代器。 該迭代器僅支持一次瀏覽數據集,但是設置非常簡單。 我們通過在數據集上調用`make_one_shot_iterator()`方法并將結果分配給變量來創建它。 然后,我們可以在創建的迭代器上調用`get_next()`,并將其分配給另一個變量。 現在,無論何時在會話中運行此操作,我們都將遍歷數據集一次,并將提??取一個新批次以使用: ```py def train(self, save_dir='./save', batch_size=500): # Use keras to load the complete cifar dataset on memory (Not scalable) (x_train, y_train), (x_test, y_test) = cifar10.load_data() # Convert class vectors to binary class matrices. y_train = tf.keras.utils.to_categorical(y_train, 10) y_test = tf.keras.utils.to_categorical(y_test, 10) # Using Tensorflow data Api to handle batches dataset_train = tf.data.Dataset.from_tensor_slices((x_train, y_train)) dataset_train = dataset_train.shuffle(buffer_size=10000) dataset_train = dataset_train.repeat() dataset_train = dataset_train.batch(batch_size) dataset_test = tf.data.Dataset.from_tensor_slices((x_test, y_test)) dataset_test = dataset_test.repeat() dataset_test = dataset_test.batch(batch_size) # Create an iterator iter_train = dataset_train.make_one_shot_iterator() iter_train_op = iter_train.get_next() iter_test = dataset_test.make_one_shot_iterator() ``` ```py iter_test_op = iter_test.get_next() # Build model graph self.build_graph() ``` # 主要訓練循環 一旦檢索到數據并構建了圖,就可以開始我們的主要訓練循環,該循環將繼續進行 20,000 多次迭代。 在每次迭代中,都使用 CPU 設備獲取一批訓練數據,并調用`AdamOptimizer`對象的`__train_step.run`方法向前運行一次,向后運行一次。 每進行 100 次迭代,我們就會對當前的訓練和測試批次進行一次前向傳遞,以收集訓練和驗證損失以及其他匯總數據。 然后,`FileWriter`對象的`add_summary`方法將提供的 TensorFlow 摘要:`summary_1`和`summary_2`包裝在事件協議緩沖區中,并將其添加到事件文件中: ```py # Train Loop for i in range(20000): batch_train = self.__session.run([iter_train_op]) batch_x_train, batch_y_train = batch_train[0] # Print loss from time to time if i % 100 == 0: batch_test = self.__session.run([iter_test_op]) batch_x_test, batch_y_test = batch_test[0] loss_train, summary_1 = self.__session.run([self.__loss, self.__merged_summary_op], feed_dict={self.__x_: batch_x_train, self.__y_: batch_y_train, self.__is_training: True}) loss_val, summary_2 = self.__session.run([self.__loss_val, self.__val_summary], feed_dict={self.__x_: batch_x_test, self.__y_: batch_y_test, self.__is_training: False}) print("Loss Train: {0} Loss Val: {1}".format(loss_train, loss_val)) # Write to tensorboard summary self.__writer.add_summary(summary_1, i) self.__writer.add_summary(summary_2, i) # Execute train op self.__train_step.run(session=self.__session, feed_dict={ self.__x_: batch_x_train, self.__y_: batch_y_train, self.__is_training: True}) ``` 訓練循環結束后,我們將最終模型存儲在帶有`op __saver.save`的檢查點文件中: ```py # Save model if not os.path.exists(save_dir): os.makedirs(save_dir) checkpoint_path = os.path.join(save_dir, "model") filename = self.__saver.save(self.__session, checkpoint_path) print("Model saved in file: %s" % filename) ``` # 模型初始化 隨著我們向模型中添加越來越多的層,使用反向傳播訓練它們的難度越來越大。 通過模型傳遞回去以更新權重的誤差值隨著我們的深入而變得越來越小。 這被稱為消失梯度問題。 因此,在開始訓練模型之前,需要注意的重要一件事是將權重初始化為什么值。 錯誤的初始化會使模型收斂非常慢,或者甚至根本不會收斂。 盡管我們不確切知道訓練后我們的權重最終會變成什么樣的值,但我們可以合理地預期,其中的一半將為正值,而另一半將為負值。 # 不要用零初始化所有權重 現在,我們可能傾向于認為將所有權重設置為零將實現最大的對稱性。 但是,這實際上是一個非常糟糕的主意,并且我們的模型永遠不會學到任何東西。 這是因為當您進行前向通過時,每個神經元都會產生相同的結果。 因此,在反向傳播步驟中,所有權重將以相同的方式更新。 這意味著模型永遠無法學習豐富的功能,因此請不要像這樣初始化。 # 用均值為零的分布初始化 一個更好的主意是使用所有以零為中心的較小隨機值初始化權重。 為此,我們可以使用均值為零和單位方差為零的正態分布的隨機值,然后將其按某個較小的值進行縮放,例如 0.01。 這樣做會破壞權重的對稱性,因為它們都是隨機且唯一的,這是一件好事。 計算向前和向后通過時,我們的模型神經元現在將以不同的方式進行更新。 這將使他們有機會學習許多不同的功能,這些功能將作為大型神經網絡的一部分協同工作。 然后唯一需要擔心的是我們設定的權重值有多小。 如果設置得太小,反向傳播更新也將非常小,這可能會在更深層的網絡中消失梯度問題。 下圖顯示了權重的要求之一(零均值): ![](https://img.kancloud.cn/23/d1/23d1f4911d802012a57dd80f43093a26_965x516.png) # Xavier-Bengio 和初始化器 在了解*訓練深度前饋神經網絡*的難度時,Xavier Glorot 和 Yoshua Bengio 表明,如果從均勻分布`U ~ [-1/√n, 1/√n]`初始化每一層的權重,其中`n`是上一層中的大小,對于 Sigmoid 激活函數,頂層(更靠近輸出)的神經元迅速飽和為 0。我們知道,由于 Sigmoid 函數的形式,激活值 0 表示權重非常大,并且反向傳播的梯度接近零。 較小的梯度值會減慢學習過程,因為早期層中的權重停止更新或停止學習。 因此,我們想要的是使權重在最初確定的時間間隔內均勻分布,也就是說,權重的方差應該在我們從底層移動到頂層時保持不變。 這將使誤差平穩地流到頂層,從而使網絡在訓練期間收斂更快。 為了實現這一點,Glorot 和 Bengio 證明了對于單位導數為 0 的對稱激活函數`f`,每一層的權重方差必須如下: ![](https://img.kancloud.cn/e9/21/e921ad37ffc2864d3af6f96d5142623a_1800x440.png) 在此,`n[in]`是到所討論的層的單元數,`n[out]`是在下一層的單元數。 這意味著權重必須從以下均勻分布中采樣: ![](https://img.kancloud.cn/dd/9f/dd9fc6a1d6b97e5c167b602ba1768754_3160x600.png) 我們還可以從零均值和前面的方差的正態分布中采樣權重。 對于 ReLu 激活函數,He 等人證明了這一點。 方差應該改為: ![](https://img.kancloud.cn/bb/30/bb301e89ed134d7fd63c54bd78596271_1260x440.png)。 因此,作者使用零均值高斯分布初始化其權重,其標準差(STD)為`√(2/n[in])`。 然后將該初始化稱為 He 初始化。 默認情況下,TensorFlow 的大部分`tf.layers`都使用 Glorot(xavier)初始化器,但是我們可以覆蓋它并指定我們自己的初始化。 在這里,我們展示了一個如何覆蓋`conv2d`層的默認初始化器的示例: ```py conv1 = tf.layers.conv2d(inputs=self.__x_, filters=64, kernel_size=[5, 5], padding="same", activation=None, kernel_initializer=tf.truncated_normal_initializer(stddev=0.01), bias_initializer=tf.zeros_initializer()) ``` # 通過規范化來提高泛化能力 到目前為止,在本章中,我們已經看到了如何使用 TensorFlow 訓練卷積神經網絡來完成圖像分類任務。 訓練完模型后,我們將其遍歷測試集,并從一開始就將其存儲起來,以查看其在從未見過的數據上的表現如何。 在測試集上評估模型的過程向我們表明了在部署模型時模型將泛化的程度。 能夠很好地泛化的模型顯然是理想的屬性,因為它可以在許多情況下使用。 我們使用的 CNN 架構是可以提高模型泛化能力的方法之一。 要記住的一種簡單技術是從設計模型開始就盡可能簡單地使用很少的層或過濾器。 由于非常小的模型很可能無法適應您的數據,因此您可以慢慢增加復雜性,直到不再發生適應不足的情況為止。 如果您以這種方式設計模型,則將限制過擬合的可能性,因為您不允許自己擁有的模型對于數據集而言過大。 但是,在本節中,我們將探索我們可以做的其他一些事情,以建立更好的機器學習模型,以及如何將它們納入我們的訓練過程中。 以下方法旨在防止過擬合,并通過這樣做,有助于創建更強大的模型,并能更好地進行泛化。 防止模型過擬合的過程稱為**正則化**。 另一個可能也會發生并且看起來與過擬合非常相似的問題是,如果您的訓練數據集沒有捕獲您想要分類的所有事物。 例如,如果您正在訓練狗分類器,但是您的訓練圖像僅包含貴賓犬的圖像。 如果要在 Labradors 上測試此訓練有素的分類器,則可能無法分類。 這種數據不平衡是一個單獨的問題,將在后面的章節中解決。 # L2 和 L1 正則化 創建更強大模型的第一種方法是使用 L1 或 L2 正則化。 到目前為止,這些是最常見的正則化方法。 基本思想是在訓練模型時,我們積極嘗試使用這些權重的 L1 或 L2 范數對模型權重的值施加一些約束。 為此,我們在使用的任何損失函數中增加了一個額外的項。 對于 L1 正則化,我們添加的項是`λ |w|`,對于 L2 正則化,我們添加的項是`0.5 λ w^2`。 在前面的項中,`w`是我們網絡中的所有權重,`λ`是稱為**正則化強度**的超參數。 通過添加該項,我們可以防止權重值變得太大。 因此,為了 L1 正則化,第 1 章,“TensorFlow 簡介和設置”的 SVM 損失函數,即: ![](https://img.kancloud.cn/18/12/1812f52a04c0f2ec546fdf0524fcd321_2830x610.png) 變為: ![](https://img.kancloud.cn/dc/8b/dc8be079e8dd51531647548a0a2b361c_4700x610.png) 在這里,我們添加了對網絡所有權重求和的正則化項。 此處,`l`是層索引,`m, n`是每一層的權重矩陣的索引。 L2 正則化的表達式看起來類似。 對于 L1 正則化,此額外項鼓勵權重向量變得稀疏,這意味著許多權重值變為零。 結果,該模型變得不受噪聲輸入的影響,因為權重向量將僅使用重要輸入的子集,這有助于避免過擬合。 對于 L2 正則化,除了保持權重之和較低之外,這個額外的項還強制權重值均勻分布在權重向量上,以便模型稍微使用所有權重,而不是大量使用權重。 由于輸入和權重之間的乘法交互作用,從直覺上講,這是一個理想的屬性,可幫助模型避免過擬合。 L2 正則化有時也稱為權重衰減; 這是因為在訓練期間,您的所有權重都會因該項(L2 正則化項的導數)而線性減少或“衰減”。 請注意,在正則化期間我們不包括偏差項,而僅包括權重。 這是因為偏差項并不會真正影響模型的過擬合,因為它們以累加的方式影響輸出,只是向上或向下移動而不是改變函數的形狀。 包含它們沒有害處,但是也沒有好處,因此包含它們沒有意義。 在下圖中,您可能會注意到增加正則強度`λ`會減少過擬合。 高正則化項意味著網絡變得接近線性,并且無法塑造復雜的決策邊界。 ![](https://img.kancloud.cn/8e/78/8e788f4f9e3752e4494c8731747d273d_673x223.png) 我們可以通過獲取所有權重并對每個權重應用 l2 范數,然后將它們全部加在一起來手動實現 L2/L1 正則化,但是這對于大型模型來說很快就變得很乏味。 幸運的是,如果我們使用`tf.layers`,那么 TensorFlow 中有一種更簡單的方法。 首先,我們設置了正則化器,如下所示: ```py l2_reg = tf.contrib.layers.l2_regularizer(scale=0.001) ``` `scale`參數是我們通常需要通過交叉驗證找到并設置自己的`λ`。 如果將其設置為 0,則不會進行任何正則化。 現在,當我們創建任何層時,我們會將正則化函數作為參數傳遞。 TensorFlow 將進行計算以獲取我們需要添加到損失函數中的所有正則化項: ```py # Example of adding passing regularizer to a conv layer. reg_conv_layer = tf.layers.conv2d( inputs, filters, kernel_size, kernel_regularizer=l2_reg) ``` 要添加我們的正則化項,我們首先需要將它們全部收集起來。 幸運的是,TensorFlow 會自動為我們將所有正則化項放到一個集合中,以便我們可以輕松訪問它們。 TensorFlow 在`tf.GraphKeys`內存儲一些與您創建的圖相關的重要集合,例如可訓練變量,匯總和正則化損失。 我們可以使用`tf.get_collection()`訪問這些集合,并提供要獲取的集合的名稱。 例如,為了得到正則化損失,我們將編寫以下內容: ```py reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES) ``` 這將返回一個列表,其中包含此集合中存儲的所有張量。 您也可以使用`tf.get_collection(key='my_collection')`創建自己的集合,然后使用`tf.add_to_collection(name='my_collection', value=some_variable_to_add)`向其中添加變量。 如果使用提供的鍵已存在一個集合,則`tf.get_collection`將返回該集合而不是創建它。 現在我們有了正則化損失項,我們可以像這樣將它們添加到我們通常的訓練損失中,然后優化組合損失: ```py train_loss=[...] # Training loss combined_loss = tf.n_add(train_loss, reg_losses) ``` # 退出 我們將要討論的另一種用于正則化的技術是一種稱為丟棄的東西。 丟棄法是由 G.E. Hinton 于 2012 年提出的,它是一種簡單的正則化方法,可帶來很好的效果。 丟棄法背后的想法是,在每次訓練迭代中,一層中的所有神經元都可以以隨機概率(通常為 50%)打開和關閉。 這種打開和關閉迫使網絡學習與往常相同的概念,但是要通過多個不同的路徑。 訓練后,所有神經元都保持打開狀態,這些路徑的行為就像是多個網絡的集合,用于平均最終結果,從而提高了泛化能力。 它迫使權重分布在整個網絡中,并且像正則化一樣將權重保持在較低水平。 理解這個概念的另一種方法是建立一個由多個人共享相似知識的團隊。 他們每個人都會對如何解決特定問題有自己的想法,這些經驗的結合提供了解決問題的更好方法: ![](https://img.kancloud.cn/de/2a/de2aa098744c3c05f43ca96c85aea86f_642x231.png) 在下圖中,我們顯示了模型測試誤差。 顯然,通過丟棄法,測試集上的誤差會減少。 請記住,與所有正則化一樣,與不使用正則化相比,使用丟棄法會使您的訓練損失增加,但是到最后,我們只對模型測試錯誤率降低(泛化)感興趣: ![](https://img.kancloud.cn/ac/5b/ac5b3b950248ad1d1e91d611a5474630_747x557.png) 通常,丟棄法僅適用于全連接層,但也可以適用于卷積/池化層。 如果這樣做,則將使用較低的`p`(掉線的可能性),接近 0.2。 同樣,您將丟棄層放置在激活層之后。 要在 TensorFlow 模型中使用丟棄法,我們在希望將丟棄法應用到的輸入層上調用`tf.layers.dropout()`。 我們還必須指定我們要使用的丟棄率,更重要的是,使用布爾值讓 TensorFlow 知道我們的模型是否在訓練中。 請記住,當我們在測試時使用模型時,我們會關閉丟棄,而這個布爾值將為我們做到這一點。 因此,帶有丟棄的代碼將如下所示: ```py # Fully connected layer (in tf contrib folder for now) fc1 = tf.layers.dense(fc1, 1024) # Apply Dropout (if is_training is False, dropout is not applied) fc1 = tf.layers.dropout(fc1, rate=dropout, training=is_training) ``` # 批量規范層 之前,我們已經完成了權重的初始化工作,以使梯度下降優化器的工作更加輕松。 但是,好處僅在訓練的早期階段才能看到,并不能保證在后期階段有所改善。 那就是我們轉向另一個稱為批量規范層的偉大發明的地方。 在 CNN 模型中使用批量規范層產生的效果與第 2 章,“深度學習和卷積神經網絡”中看到的輸入標準化大致相同。 現在唯一的區別是,這將在模型中所有卷積層和完全連接層的輸出處發生。 批量規范層通常將附加到每個完全連接或卷積層的末端,但是在激活函數之前,它將對層輸出進行規范化,如下圖所示。 它通過獲取層輸出(一批激活)并減去批次平均值并除以批次標準差來執行此操作,因此層輸出具有零均值和單位標準差。 請注意,在激活函數之前或之后放置批量規范化是一個引起激烈爭論的話題,但是兩者都應該起作用。 ![](https://img.kancloud.cn/26/28/26282ee11950542b581ab4a3b9d56fb3_836x109.png) 進行此標準化之后,批量規范層還具有兩個可學習的參數,這些參數將按比例縮放標準化的激活并將其轉移到模型認為最有助于其學習的內容。 整個過程通過消除消失的梯度問題來幫助訓練。 反過來,這又允許模型在訓練時使用更高的學習率,因此可以減少迭代次數。 在訓練過程中,記錄平均值和標準差值的移動平均值。 然后在測試時使用這些值,而不是計算批次統計信息。 批量規范層的一些優點如下: * 改善梯度流動,允許訓練更深層的網絡(解決消失的梯度問題) * 允許更高的學習率,使訓練更快 * 減少對良好權重初始化的依賴(更簡單的隨機初始化) * 給您的模型某種正則化效果 * 使得可以使用飽和非線性,例如 Sigmoid 對于更多的數學讀者,可以在批量規范論文《批量規范化:通過減少內部協變量偏移來加速深層網絡訓練》中找到更為正式的定義,這是一篇寫得很好的論文,易于理解和解釋。 更詳細的概念。 如果假設我們有一個僅具有全連接層的簡單神經網絡,則正如我們在第 1 章,“TensorFlow 簡介和設置”中所看到的,每一層的激活將是`s = f(x; W, b) = W · x + b`表格。 假設`g(·)`是非線性的,例如 Sigmoid 或 ReLU,然后將批量歸一化`BN(·)`直接應用于每個單元,例如: ![](https://img.kancloud.cn/74/3b/743b6490f7db772e61c72023f06fc40f_1770x220.png) 在這里,可以忽略偏差,因為它將通過均值減法消除。 如果我們的批次大小為`m`,則標準化激活`s[i]^(BN)`的計算如下: ![](https://img.kancloud.cn/13/b4/13b42f14f3fbfd4db088643a32503b3c_1190x540.png) ![](https://img.kancloud.cn/ca/1f/ca1fd69e02bbc8d86552420656cf0572_1800x540.png) ![](https://img.kancloud.cn/18/10/18107f95c9dbf49f93cf8550bc1f06ca_1110x450.png) ![](https://img.kancloud.cn/10/e5/10e5f657d6a55eee68ee0acc24d0329a_1160x240.png) 其中`γ`和`β`是可學習的參數,它們將縮放并移動您的標準化激活。 網絡可以使用這些參數來決定是否需要標準化以及需要多少標準化。 這是正確的,因為如果我們設置`β = μ`和`γ = √(δ^s + ε)`,則設置`s[i]^(BN) = s[i]`。 最后,這是一個如何在本章開始的分類示例代碼中使用批量規范層的示例。 在這種情況下,我們將批量規范化層放在卷積層之后和激活函數之前: ```py conv3 = tf.layers.conv2d(inputs=pool2, filters=32, kernel_size=[5, 5],padding="same", activation=None) conv3_bn = tf.layers.batch_normalization(inputs=conv3, axis=-1, momentum=0.9, epsilon=0.001, center=True,scale=True, training=self.__is_training, name='conv3_bn') conv3_bn_relu = tf.nn.relu(conv3_bn) pool3 = tf.layers.max_pooling2d(inputs=conv3_bn_relu, pool_size=[2, 2], strides=2) ``` # 總結 在本章中,我們了解了 CNN 模型的構建方式,包括使用哪些損失函數。 我們研究了 CIFAR 和互聯網數據集,并了解了如何訓練 CNN 來對 CIFAR10 數據集進行分類。 為此,我們被引入了 TensorFlow 數據 API,這使加載和轉換數據的任務變得更加容易。 最后,我們討論了通過談論初始化和正則化的不同方法來提高訓練模型的質量的方法。 在下一章中,我們將解決更困難的對象檢測,語義和實例分割任務。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看