第 15 章:深度學習

此章節主要介紹 SAS 平台如何進行深度學習的應用

基本介紹

首先深度學習主要能夠從多個隱藏層的神經網路中導出的預測資訊,以利有效解決問題,同時與傳統神經網路在於隱藏層激活函數、權重初始化、正則化技術、基於梯度學習優化以及分散式處理運算,這五大方面有不同的差別,請參考下表。

五大方面

傳統神經網路

深度學習

隱藏層激活函數

Hyperbolic Tangent (tanh)

Rectified Linear (RELU)

權重初始化

Constant Variance

Normalized Variance

正規化技術

Stopped Training、L1 和 L2

Stopped Training、L1、L2、Dropout 和 Batch Normalization

基於梯度學習優化

Batch GD 和 BFGS

Stochastic GD、Adam 和 LBFGS

分散式處理運算

CPU

GPU

接著我們能夠透過 SAS Viya 平台中的強大記憶體分散式運算的雲端分析服務之基礎架構上執行深度學習的功能,其中主要有三個深度學習的模型類型,分別為:

  1. 深度神經網路 (DNN):主要用於解決詐欺偵測等分類問題。

  2. 卷積神經網路 (CNN):主要用於建立影像識別等分類問題。

  3. 遞歸神經網路 (RNN):主要用於解決文字分析和時間序列等問題。

再來我們能夠使用多種程式語言進行 SAS Viya 平台中深度學習的應用操作,包括 Python 和 R,建議參與「Using SAS Viya REST APIs with Python and R」免費官方課程,當然也能夠付費參與每週測驗和上機練習操作,以利獲取官方徽章,其中課程內容主要有包括透過 Python 和 R 使用 SAS Viya 平台進行深度學習相關技術應用,像是文字分析、影像辨識、時間序列、… 等。

最後我們還能夠透過 SAS DLPy Python API 套件有效應用深度學習功能來解決電腦視覺,自然語言處理,預測和語音處理等實務應用的問題,同時 SAS DLPy Python API 套件主要是根據 Keras API 所建立的,因此對於 Keras 使用者非常容易上手。以及在 SAS DLPy Python API 套件中許多模型皆有預先定義的深度網路架構能夠直接使用,像是 LeNet、VGG、ResNet、DenseNet、Darknet、Inception、YOLOv2、Tiny YOLO、… 等,此外我們更能夠使用 DLPy 匯入和匯出深度學習開放式神經網路交換 (ONNX) 格式的深度學習模型。

卷積神經網路

首先卷積神經網路 (CNN) 主要由多個神經元和多個層所組成,主要有五個圖層類型,分別為輸入層、卷積層、池化層、完全連接層和輸出層所組成,不同圖層皆有不同的特定屬性和功能,像是透過卷積層和池化層提高運算的效率,並且增強模型能夠捕獲輸入分佈中的重要特徵點,更進一步增加模型的成效,因此卷積神經網路主旨在於將影像資料作為輸入,假設允許使用結構資訊,以利有更佳的運算效率,並且使用更少的儲存空間,至於卷積神經網路主要應用於影像分類和目標偵測,像是自動駕駛汽車、故障檢測、聊天機器人、…等其它實務應用。

接著輸入層主要儲存要進行分類的影像原始像素值,像是以常見的 CIFAR-10 資料集中的每個影像的高度為 32,寬度為 32,深度為三個顏色的通道,分別為藍色、綠色和紅色,所謂 CIFAR-10 資料集主要是由 10 個分類中的 60,000 個 32 x 32 彩色影像所組成,每個類別有 6,000 個影像,並且其中有 50,000 個訓練影像和 10,000 個測試影像,同時資料集分為五個訓練批次和一個測試批次,每個批次有 10,000 個影像,測試批次包含來自每個類別的 1,000 個隨機選擇的影像,而訓練批次包含隨機順序的剩餘影像,但是一些訓練批次可能包含來自一個類別的更多影像而不是另一個類別,以及訓練批次包含來自每個類別的 5,000 個影像。此外輸出層本上是與特定損失函數相關聯的完全連接層,其與特定的計算預測中誤差的損失函數相關聯,像是分類的 cross-entropy ,並且輸出層主要會進行類別的評分,以 CIFAR-10 資料集類別為例,其結果大小為 1 x 1 x 10,其中第三維度中的 10 個數字中的每一個都對應一個類別評分,並且類別評分將會對應至 CIFAR-10 中的 10 個影像類別集。

再來卷積層主要將卷積運算應用於輸入,並且計算連接至輸入中局部區域的神經元輸出,同時卷積也能夠反應出單個神經元對視覺刺激的反應,所以卷積層主要執行卷積神經網路的繁重工作任務,而卷積層主要使用過濾器來測量輸入與特徵的相似程度,過濾器主要是在訓練期間學習的權重向量,卷積神經網路學習過濾器,其精通於檢測特定類型的視覺特徵,像是直線、半圓、… 等,而過濾器的濾鏡通常具有較小的寬度和高度,並且與輸入有相同的深度,像是維度為 5 x 5 x 3 的濾鏡,濾鏡寬度為 5 像素,濾鏡高度為 5 像素,並且對於藍色、綠色和紅色的 3 色通道的濾鏡深度為 3。此外當我們使用 SAS 深度學習套件建立卷積神經網路時,無需為卷積層或池化層指定填充,系統將自動計算填充,以利在使用步幅 (Stride) 為 1 時,則會使得輸出影像大小不會被改變,也就是卷積之後的大小與卷積之前的大小完全相同,而當步幅 (Stride) 為 2 時,則會使得輸出影像大小的大小是輸入影像大小二分之一,而當步幅 (Stride) 為 3 時則會使得輸出影像大小的大小是輸入影像大小三分之一,依此類推,至於輸出影像大小的計算公式則為 ( ( ( n + 2p - f ) / s ) + 1 ) x ( ( n + 2p - f ) / s ) + 1 ),其中 n 為輸入維度、p 為填充維度、f 為濾鏡維度以及 s 為步幅 (Stride) 數。

最後池化層主要透過逐漸減小影像的空間維度和相關參數來簡化卷積層的輸出,池化層通常會放置在連續的卷積層之間,其中池化層的高度和寬度大小會進行最大值、最小值和平均值的計算減小影像表示的大小,因為池化顯著減少了要分析的參數數量,因此減少了計算數量,將也更有助於緩解過度擬合,這也是卷積神經網路的常見挑戰。此外最大池化是最受歡迎的池化層計算方式,最大池化在二維空間中應用移動窗口以選擇最大值,當使用大小為 2 x 2 的過濾器使用步幅 (Stride) 為 2 的池化層可減少 75% 的傳入資訊量,因此透過池化層產生的輸出資訊將有助於摘要,簡單來說,建模者更關心物件是否存在,而不是其確切的位置。

遞歸神經網路

首先遞歸神經網路 (RNN) 主要是用於建立序列資料的框架,在某些情況下回應變數可能為連續,並且按照時間序列中的索引變數的順序進行記錄觀察,並且針對目前觀察進行預測,如今遞歸神經網路已被廣泛和成功地用於時間序列預測、語音分析和文字分析。而遞歸神經網路被稱為循環,主要是因為其對於序列的每個元素執行相同的任務,每個元素的輸出取決於其前面元素的計算,而序列資料是許多應用程式的基礎,該序列資料主要由一系列標記組成,其中標記是對應於特定時間或序列索引的數字向量,像是假設一個序列由溫度,氣壓,濕度和降水量度組成,則每小時在特定的地理位置。在這種情況下,標記將是四個浮點數的向量,表示每小時的這些測量。

接著原始遞歸神經網路在架構上非常簡單,但是對於長序列來說可能很難訓練,原始遞歸神經網路主要有兩種常見的類型,分別為 LSTM 和 GRU,在 SAS 深度學習操作中主要支持所有三種遞歸神經網路模型的類型,分別為 RNN,LSTM,GRU,而我們主要會使用遞歸神經網路進行以下應用程序的相關應用,分別為:

  1. 預測時間序列:輸入為數值序列資料的應用程式,輸出為單個數值或目標值。

  2. 自動語音識別:輸入是數值序列資料的應用程序,輸出是輸入序列的標籤。

  3. 文本情感分析:輸入是文本資料的應用程式,輸出是單個數值或目標值。

再來建立遞歸神經網路模型主要可以描述為多對多的網路,也就是有多個輸入和多個輸出,並且按順序記錄的目標值,而另一種的遞歸神經網路為多對一的網路,不論是多對多或多對一的網路,其中主要皆有四種圖層類型,分別為:

  1. 輸入層 (必要)

  2. 遞歸層 (必要)

  3. 完全連接層 (非必要)

  4. 輸出層 (必要)

此外在遞歸神經網路模型中的資訊除了設定單向流動之外,當前和過去的資訊確定當前隱藏單元與目標的關係,更能夠設定以雙向流動,在確定當前隱藏單元時考慮過去,當前和未來資訊與目標的關係。此外權重是遞歸神經網路常見的功能,主要為在兩個圖層之間的連接方式,輸入給定序列的變數或使用權重向量,以利將時間間隔組合在對應的隱藏單元中,像是間隔時間的應用就很常見,每層中的資訊和共同權重組合,將有助於產生對於目標當前值的預測。

最後基本遞歸神經網路的框架可能會遇到問題,當產生資料包含具有相當長記憶的元件時,此外為了學習這些元件將會有數學挑戰性,反向傳播的過程涉及到評估損失函數的導數或梯度相對於每個中的權重值,而對於遞歸神經網路中較長的儲存過程,這些梯度必須在很多階段傳播或間隔,這可能會導致梯度形式的優化過程出現問題發生消失或爆炸,此時我們就會使用 LSTM 和 GRU 來解決基本遞歸神經網路的框架所面臨的問題,其中 LSTM 明確地設計用於學習長期存在各種各樣問題的依賴關係,如今被廣泛使用。

超參數調整

首先機器學習演算法建立預測模型的成效取決於訓練過程和各種超參數 (Hyperparameters) 選項,並且透過搜索深度神經網路的超參數空間訓練每個超參數組合直到演算法收斂,此外訓練模型的優化演算法有時需要選擇影響優化程式效能的參數值,其中每選擇的參數值將會對優化程式的效能產生影響,而這種影響可能是針對特定問題,此時在 SAS 深度學習中更支持自動執行超參數調整過程的方法這個過程稱為超參數調整 (Hyperparameter Tuning),其就是一個選擇優化演算法參數的過程,以利提高針對特定問題實際的模型成效。

接著 SAS 深度學習主要使用 dlTune 演算法執行超參數調整,所謂 dlTune 演算法本質上主要是一個排序和選擇函數,每個參數集皆訓練幾個 Epochs,並且進行驗證模型錯誤的比較,並且保留最佳效能的參數集,至於其它參數集則會為更進一步進行訓練幾個 Epochs 之後,更進一步的減少不適合的參數集,此外我們也能夠在每個超參數調整週期中配置要評估和保留的參數集數量,並且在每個超參數調整週期中設定要評估和保留的參數集數量,其中針對超參數調整方法的要求,其主要執行以下步驟,分別為:

  1. 指定要調整的超參數。

  2. 指定每個超參數集合理範圍。

  3. 指定需要評估超參數集的數量。

  4. 使用稱為超參數集中超參數值訓練模型。

  5. 使用模型訓練效能標準來評估其它超參數集的成效。

  6. 從分析空間中刪除效能最差的超參數集。

其中在調整超參數之前,我們必須確定要調整哪些超參數以及這些參數的合理範圍值,SAS 深度學習主要使用內部衍生的演算法,其主要針對機器學習問題使用基於隨機優化演算法來選取需要的超參數,當然也能夠繼續使用調整的超參數進行訓練,直到訓練過程完成,達到模型的品量適合,通常會以錯誤分類率 (Misclassification Rate) 或錯誤率 (Error Rate) 用於確定超參數值。此外每個超參數集皆會針對少量 Epochs 進行訓練,並且針對效能最佳的超參數集進行刪除,而剩餘的參數集被進一步進行評估直到指定完成調整迭代次數或直到只剩下最佳的超參數集為止,以及透過快速丟棄效能較差的超參數集,將能夠用較少的計算資源來識別效能較佳的超參數集。

再來在 SAS 深度學習使用 dlTune 演算法執行超參數調整,將會先使用 Latin Hypercube 選擇超參數集搜尋空間的樣本,此方式允許在每個超參數的整個範圍內進行收斂,並且與簡單的隨機樣本進行相比,將可能會找到每個超參數的良好值,因此被認為產生了相當均勻的抽樣空間,所以如果為了避免過度擬合,則建議使用保留樣本來評估每組超參數。但是為何使用 Latin Hypercude 選擇超參數集搜尋空間的樣本呢?因為即使具有機器學習演算法及其參數的專業知識,超參數的最佳設定也會隨著不同的數據而變化,並且根據經驗這部份很難預測,此時為了探索適當超參數設定通常會執行 Grid Search ,但是其會隨著參數的數量和每個的離散等級別的數量呈指數增長,所以將無法識別改進的模型設定,而 Random Search 對於相同數量的樣本將會更好的針對空間進行抽樣,但是因為取決於樣本的大小和均勻性,若分配不適當也可能錯過良好的超參數值和組合,所以最佳的方法就是使用 Latin Hypercude 進行抽樣,此時在每個超參數上是完全一致的,但是在組合中是隨機的,這種方法更有可能找到每個超參數的最佳值,然後更進一步識別最佳的超參數集,像是建議調整有關學習率的超參數值,以利輕鬆獲得模型成效的提升,或者如果模型的成效明顯更好,則建議調整有關有關正則化 (L1、L2 和 Dropout) 的超參數值進行更佳的模型訓練。

最後在 SAS 深度學習中使用 dlTune 演算法主要是根據 Hyperband 演算法按比例分配每組抽樣超參數給指定模型對應的資源,然後針對模型進行多個期間的訓練,並且從訓練過程中移除執行的模型,以及針對表現不佳模型的資源將會被重新進行重新分配,直到確保模型的準確性,如果僅是為了找到最優的超參數集而不考慮其它的因素,則就透過窮舉法就能夠將所有超參參數組合皆嘗試直到找到最佳,但是若是考慮時間因素和計算資源,則就能夠透過 Hyperband 演算法進行超參數集數量和資源分配進行權衡 (Tradeoff),也就是透過盡可能多的超參數集數量和資源分配,以利盡可能的找到最佳超參數,至於針對 Hyperband 演算法影響 dlTune 演算法的優化屬性值,分別為:

  1. Max epochs:主要是設定在刪除超參數集合之前需要執行的 Epochs 數。

  2. Tune iterations:主要是設定超參數調整的次數。

  3. Number of trials:主要設定在一開始嘗試處理的超參數集數量。

  4. Tune retention:主要設定在每次調整超參數迭代之後保留的比例。

總結超參數的優化調整通常可以非常快地導致模型誤差比這些參數的設定減少幾個百分點,而除了深度學習之外,超參數的優化調整的概念也用於機器學習演算法,像是決策樹能夠設定深度數量,樹的數量和在隨機森林或梯度提升樹中分割的變數數量,而所謂自動化機器學習最主要的關鍵差別就在於自動化超參數的優化調整,這也是 SAS 深度學習最關鍵優勢之一。

整合應用

DLPy

首先卷積神經網路 (Convolutional neural network,CNN),簡稱 CNN,主要是屬於神經網路的類別之一,其廣泛用於電腦視覺中的影像識別和分類,CNN 主要是由多個圖層和多個神經元所組成,其主要將影像資料作為輸入,並且 CNN 允許使用結構化資訊,以利在計算上更有效,並且使用更少的記憶體,典型的 CNN 將由許多不同的層組成,每種類型的層皆有特定的屬性和功能,其中主要的圖層類型,分別為:

  1. 輸入層 (Input Layer):主要儲存分類影像的原始像素值,像是 CIFAR-10 資料集中的每個影像的高度為 32,寬度為 32,深度為 3 個顏色通道,分別為藍色,綠色和紅色。

  2. 卷積層 (Convolution Layer):主要將卷積運算應用於輸入,其還計算連接到輸入中的局部區域的神經元的輸出,卷積反映了單個神經元對視覺刺激的反應,請注意卷積層執行繁重工作,並且需要大量且複雜的運算。此外卷積層能夠使用濾波器來測量輸入路徑與特徵的相似程度,所謂過濾器則是在訓練期間學習的權重向量,過濾器通常具有較小的寬度和高度,當然其與輸入具有相同的深度,像是對於彩色影像而言,對於藍色,綠色和紅色的三個顏色通道,濾鏡深度為 3,而對於黑白圖像,濾鏡深度為 1,同時我們以一定的步幅 (Stride) 在圖像上滑動濾鏡合併點積運算,所謂步幅就是我們每次移動窗口時滑動的像素數,並且透過點積運算建立一個稱為卷積特徵,像是激活圖譜 (Activation map) 或特徵圖譜 (Feature map) 的新矩陣。

  3. 池化層 (Pooling Layer):主要匯整通過逐漸減小影像表達的空間維度,此時需要計算參數來簡化卷積層的輸出,其通常放置在連續的卷積層之間,並且沿高度和寬度大小進行下取樣操作,以利減小影像呈現時的大小,透過池化層將能夠明顯減少了需要分析的參數數量,從而減少了計算數量,同時更有助於緩解過度擬合的常見挑戰,其中最大池化是最受歡迎的池化方案,因此搭配使用卷積層和池化層來加速運算效率。

  4. 完全連接層 (Fully Connected Layer):主要傳統的分層連接進行對應,透過矩陣乘法計算激活和偏移,以及為每個傳入的資訊附加參數,此外完全連接層中的神經元與前一層中的所有激活具有完全連接,所以包括大量的參數。

  5. 輸出層 (Output Layer):主要與特定的損失函數相關聯,損失函數計算預測中的誤差,其中的神經元連接到前一層中的所有激活,就像神經網路中的完全連接層一樣。此外其中的 softmax 函數主要採用分數向量,並且將其轉換為 0 至 1 之間的值的向量,其總和為 1 ,因此應用 softmax 作為激活函數將能夠確保來自完全連接層的輸出機率之和為 1。

然而除了上述主要的圖層類型之外,在 SAS 深度學習應用中還有支援不同圖層應用,像是 Batch Normalization Layer、Concatenation Layer、Residual Layer、Keypoints Layer、Object Detection Layer、Projection Layer、Segmentation Layer、Shuffle Layer、Transposed Convolution Layer、… 等圖層,其中透過批次正規化圖層 (Batch Normalization Layer) 主要能夠解決收斂問題,此外合併層 (Concatenation Layer) 和剩餘層 (Residual Layer) 又稱為跳過層 (Skip-Layer) 連接主要能夠幫助神經網路記住早期學到的潛在特徵,至於詳細資訊請參考官方文件

接著在 SAS Viya 分析平台中,除了提供 SAS Scripting Wrapper for Analytics Transfer (SWAT) 用於各種通用程式語言使用 SAS Viya 函數的軟體套件,更提供深度學習套件功能的進階 Python 函式庫 DLPy,所謂 DLPy 主要提供了一種方便的方法應用深度學習的功能來解決電腦視覺文字分析時間序列預測的問題,至於其特色主要為:

  1. 載入和建構影像,文字,語音和時間序列資料的深度學習模型。

  2. 提供 Keras 風格和 PyTorch 風格的進階 API 使用語法。

  3. 提供了許多預定義的神經網絡架構模型和提供預先訓練過的權重,像是 LeNet、VGG、ResNet、DenseNet、Darknet、Inception、YoloV2 、Tiny_Yolo、… 等。

  4. 提供內建模型分析和資料視覺化工具,像是熱圖和特徵圖,以利使用者能夠針對深度學習模型進行分析,更深入了解電腦視覺問題中黑匣子的神秘面紗。

  5. 提供深度學習相關任務的多種支援功能,像是神經網路視覺化、特徵圖、預測結果、資料分割、資料增強、模型部署、… 等。

  6. 輕鬆建立特定任務的模型,每個深度學習套件皆需要很多技巧來定義不同任務的模型,因此為特定任務創建了預先定義模型,以利盡最大努力消除這種負擔。

  7. 輕鬆整合開源程式碼的套件,將能夠讓使用者從 SAS Viya 分析平台獲取資料至本地客戶端,並且將其轉換為流行的資料格式,像是 DataFrame,轉換之後的資料就能夠無縫的應用至其它開源程式碼套件中的模型,像是 scikit-learn。

  8. 提供匯入和匯出 ONNX 格式的深度學習模型。

再來透過 SAS 官方影片的展示,我們能夠初步了解資料科學家將能夠透過 DLPy 套件完成以下任務,分別為:

  1. 建立用於特徵提取的複雜模型,像是 ResNet 模型。

  2. 使用模型用於影像分割和物體追蹤,像是 U-Net 模型。

  3. 使用模型用於物體檢測,像是 R-CNN 模型 (VGG16)。

  4. 使用模型用於影像分類,像是 ShuffleNet 模型和 MobileNet 模型。

  5. 使用多任務模型用於影像標記的深度學習,以利獲得產品推薦。

然而 DLPy 深度學習套件能夠將對所建立用於電腦視覺問題的模型匯入和匯出為 ONNX 格式的深度學習模型,至於相關操作,請參考 SAS 官方影片

最後為什麼要使用卷積神經網路呢?相較於傳統前饋神經網路不能很好地擴展到完整影像,而透過 CNN 的容量將能夠透過改變其深度和寬度來控制 CNN 對影像的性質,像是統計的平穩性和像素依賴性的局部性做出了強有力且大多數正確的假設。此時與具有相似大小的層的標準前饋神經網路相比,其主要能夠透過影像輸入的架構來最小化神經元的數量,與完全連接的網路相比,其僅將每個神經元連接到輸入的局部區域,此時局部區域的大小主要由濾波器大小定義,因此與完全連接的網絡相比,使用較小的局部區域,將能夠顯著減少神經元連接和權重的數量,所以更容易訓練並且它們的效能不會更差,像是在 20 世紀後期 Yann LeCun 和其同事開發一種名為 LeNet 的卷積神經網路來識別手寫數字,其中 LeNet 在前兩層的不同位置對相同的濾波器使用相同的權重,與完全連接的神經網路相比,卷積神經網路明顯的減少了需要學習的參數數量。

ONNX

首先 ONNX (Open Neural Network Exchange) 主要是一套開放神經網路交換格式,由 Facebook 和 Microsoft 結盟所推出,並且獲得 SAS、Amazon、IBM、Intel、AMD、ARM、… 等廠商的支援,我們將能夠透過 ONNX 格式來使用所選擇的任何深度學習框架來設計、訓練和部署深度學習模型,同時 ONNX 格式能夠在不同深度學習框架格式之間輕鬆進行互通,以利讓不同標準的深度學習模型可以互相轉換,加快人工智慧的發展,像是 Google 的 TensorFlow、Amazon 的 MXNet、Facebook 的 Caffe2/PyTorch、Microsoft 的 CNTK、… 等等。至於 SAS Viya 平台目前也能夠透過 SAS Python DLPy 套件匯入和匯出 ONNX 格式的模型,同時更能夠將匯入的深度學習模型儲存為 SAS SAS ASTORE 分析儲存檔,以利能夠透過 REST API 部署至應用程式中,至於目前已經有哪些 ONNX 格式的模型能夠使用呢?我們則能夠透過 ONNX Model Zoo 網站 下載最新的深度學習模型,並且已經測試為目前最優的效能。

接著我們將能夠透過官方所提供的 Python 範例程式碼 將其中一種 ONNX 格式的模型匯入至 SAS Viya 平台中進行影像辨識的操作,以下將進行詳細說明。至於所謂 ONNX 格式主要定義了一種可擴充套件的計算圖模型,並且內建運算單元和標準資料類型,以及每一個計算流程圖皆定義為由節點所組成的列表,同時建立為有向無循環圖,其中每一個節點皆有一個或多個輸入和輸出,而每一個節點也稱為運算單元,此外更進一步 ONNX 格式採用的是 Protobuf 序列化資料結構協定用於儲存神經網路的權重資訊,然後能夠讓各種程式語言進行讀取和寫入,因此 ONNX 格式為一種通用的計算圖,能夠讓不同深度學習框架所建立的計算圖皆能夠進行轉換。

下載 ONNX 格式的 ResNet50 模型檔案

$ wget https://s3.amazonaws.com/onnx-model-zoo/resnet/resnet50v1/resnet50v1.onnx

初始化設定操作環境

import os
os.environ['CAS_CLIENT_SSL_CA_LIST']='/opt/sas/viya/config/etc/SASSecurityCertificateFramework/cacerts/vault-ca.crt'
import swat, onnx, warnings
from dlpy import Model
from dlpy.images import ImageTable
warnings.filterwarnings('ignore')
s = swat.CAS("<SAS Viya URL>", 5570, "<Username>", "<Password>")

載入和匯入 ONNX 格式模型

主要指定用於訓練此模型的預處理,像是比例和偏移。

onnx_model = onnx.load_model('resnet50v1.onnx')
model1 = Model.from_onnx_model(s, 
                               onnx_model,
                               output_model_table='resnet50v1',
                               scale=0.0173611,
                               offsets=[2.155, 2.027, 1.804])
NOTE: Successfully written weights file as /data/resnet50v1_weights.onnxmodel.h5
NOTE: Model table is attached successfully!
NOTE: Model is named to "resnet50v1" according to the model name in the table.
NOTE: Successfully imported ONNX model.

若是沒有 SAS Visual Data Mining and Machine Learning 的授權碼,則在執行時就會出現以下錯誤。

NOTE: Successfully written weights file as /data/resnet50v1_weights.onnxmodel.h5
ERROR: Could not find extension: (tkdl)
ERROR: Action set 'deeplearn' was not loaded due to errors.
ERROR: The action stopped due to errors.

取得深度學習模型基本資訊

model1.get_model_info()

Descr

Value

0

Model Name

resnet50v1

1

Model Type

Convolutional Neural Network

2

Number of Layers

126

3

Number of Input Layers

1

4

Number of Output Layers

1

5

Number of Convolutional Layers

53

6

Number of Pooling Layers

2

7

Number of Fully Connected Layers

0

8

Number of Batch Normalization Layers

53

9

Number of Residual Layers

16

model1.print_summary()

取得深度學習模型摘要資訊

Layer Id

Layer

Type

Kernel Size

Stride

Activation

Output Size

Number of Parameters

0

data

input

None

(224, 224, 3)

(0, 0)

1

resnetv17_conv0_fwd

convo

(7, 7)

2

Identity

(112, 112, 64)

(9408, 0)

2

resnetv17_batchnorm0_fwd

batchnorm

Rectifier

(112, 112, 64)

(0, 128)

3

resnetv17_pool0_fwd

pool

(3, 3)

2

Max

(56, 56, 64)

(0, 0)

4

resnetv17_stage1_conv3_fwd

convo

(1, 1)

1

Identity

(56, 56, 256)

(16384, 0)

5

resnetv17_stage1_batchnorm3_fwd

batchnorm

Identity

(56, 56, 256)

(0, 512)

121

resnetv17_stage4_conv9_fwd

convo

(1, 1)

1

Identity

(7, 7, 2048)

(1048576, 2048)

122

resnetv17_stage4_batchnorm9_fwd

batchnorm

Identity

(7, 7, 2048)

(0, 4096)

123

resnetv17_stage4__plus2

residual

Rectifier

(7, 7, 2048)

(0, 0)

124

resnetv17_pool1_fwd

pool

(7, 7)

1

Mean

(1, 1, 2048)

(0, 0)

125

resnetv17_dense0_fwd

output

(2048, 1000)

Softmax

1000

(2048000, 1000)

再來當我們將 ONNX 格式的 ResNet50 模型匯入至 SAS Viya 平台中,下一步就能夠透過進行影像辨識的預測分析,但是需要先從 ONNX 模型所產生的 H5 檔案中載入權重資訊至 SAS Viya 平台的深度學習模型中。至於所謂 H5 檔案主要是階層資料格式 (Hierarchical Data Format, HDF) 用來設計儲存和組織大量資料的一組檔案格式,其最初開發於美國國家超級計算應用中心,現今則用於儲存科學資料的檔案格式。此外 H5 檔案在記憶體佔用、壓縮大小和存取速度方面皆有非常優秀的特性,因此在工業領域和科學領域皆有很多應用,而 H5 檔案內部的資源主要是透過類似 POSIX 的語法進行存取 (/path/to/resource),並且簡化檔案結構僅包括兩種主要的物件類型,分別為資料集和群組,資料集主要是同質類型的多維陣列,群組主要是持有資料集和其他群組的容器結構,同時中繼資料是由使用者定義,以命名屬性的形式附加至資料集和群組中,因此任何複雜的儲存格式皆能夠透過資料集、群組和屬性來建立,當然由於使用 B-tree 來進行索引表格物件,所以更有效應用於時間序列資料,大量資料直接進入表格物件中,因此可以比 SQL 資料庫的欄位儲存方式有更佳的存取效能。

載入權重資訊至深度學習模型

model1.load_weights('/data/resnet50v1_weights.onnxmodel.h5', labels=True)
NOTE: Cloud Analytic Services made the uploaded file available as table NEW_LABEL_TABLE_HAMREG in caslib CASUSER(sasdemo01).
NOTE: The table NEW_LABEL_TABLE_HAMREG has been created in caslib CASUSER(sasdemo01) from binary data uploaded to Cloud Analytic Services.
NOTE: no dataspec(s) provided - creating image classification model.
NOTE: Model weights attached successfully!

下載需要進行辨識的照片檔案

$ cd /data/images
$ wget https://upload.wikimedia.org/wikipedia/commons/6/66/An_up-close_picture_of_a_curious_male_domestic_shorthair_tabby_cat.jpg

透過深度學習模型進行影像辨識

img = ImageTable.load_files(s, path='/data/images')
img.resize(224)
model1.evaluate(img)
model1.plot_evaluate_res()

最後匯出模型檔案的格式主要有三種,分別為 SAS 資料集格式、SAS 分析儲存格式和 ONNX 格式,所謂 SAS 資料集格式就是儲存至 SAS Viya 平台中的 CAS 資料表,所謂 SAS 分析儲存格式就是儲存為 SAS ASTORE 分析儲存檔,可用於 SAS Event Streaming Processing 即時串流分析之即時評分的情境應用,所謂 ONNX 格式就是用於儲存模型的開放神經網路交換格式。因此建模人員能夠 SAS 官方所提供的 DLPy 套件匯入針對經典情境預先訓練的 ONNX 模型,並且匯出成 SAS ASTORE 分析儲存檔,更進一步用於 SAS Event Streaming Processing 進行即時串流分析之即時評分的情境應用。

匯出模型檔案為 SAS ASTORE 分析儲存檔

model1.deploy(path='/tmp', output_format='astore')
NOTE: Model astore file saved successfully.

開始使用

  • 登入【Jupyter】,輸入【SAS Profile 電子郵件】和【SAS Profile 密碼】,按下【Sign In】鈕。

  • 開始使用 Jupyter。

  • 按下【New】鈕,點選【Folder】。

  • 按下【Rename】鈕。

  • 輸入【深度學習】,按下【OK】。

  • 點選【深度學習】。

  • 按下【New】鈕,點選【Python 3】。

  • 點選【Untitiled】標題。

  • 輸入【MNIST】,按下【OK】。

  • 載入 Python 相關套件。

from swat import *
import numpy as np
import pandas as pd
from matplotlib import pylab as plt
from matplotlib import image as mpimg
import time
  • 設定 CAS 伺服器連線資訊。

cashost='localhost'
casport=5570
useremail='SAS PROFILE 電子郵件'
userpassword='SAS PROFILE 密碼'
casauth='~/.authinfo'
  • 連線 CAS 伺服器。

s = CAS(cashost, casport, useremail, userpassword, caslib="casuser")
  • 設定 CAS 伺服器連線工作階段逾時時間為一年。

s.sessionprop.setsessopt(caslib='casuser', timeout=31535000)
  • 載入【image】和【deepLearn】的 CAS 行為。

s.loadactionset('image')
s.loadactionset('deepLearn')
  • 在 Jupyter 中,點選【Upload】鈕,上傳 CSV 檔案。

  • 選取【fashion-mnist_train.csv】檔案,按下【開啟】鈕。

  • 按下【OK】鈕。

  • 按下畫面下方的【SAS Drive】鈕。

  • 輸入【SAS Profile 電子郵件】和【SAS Profile 密碼】,按下【登入】鈕。

  • 點選【略過設定】。

  • 選取【管理資料】。

  • 點選【匯入】頁籤,點選【本機檔案】,選取【fashion-mnist_train.csv】的 CSV 檔案。

  • 按下【選取目的地】鈕。

  • 選取【CASUSER (xxx)】,按下【選取】鈕。

  • 輸入【名稱】為【FASHION_MNIST】,點選【取代檔案】,按下【匯入項目】。

  • 正在匯入檔案。

  • 成功匯入檔案,按下畫面下方的【Jupyter】鈕。

  • 點選【深度學習】資料夾。

  • 開啟【MNIST.ipynb】記事本檔案。

  • 建立 CAS 資料表。

castblfashionmnist = s.CASTable("FASHION_MNIST", replace=True); 
  • 將 CAS 資料表轉換為 DataFrame 資料格式。

df_fashionmnist = castblfashionmnist.to_frame()
df_fashionmnist
  • 查看目前的目錄位置。

!pwd
!rm -rf /home/leoyeh.me/深度學習/訓練圖檔
!mkdir /home/leoyeh.me/深度學習/訓練圖檔
for i in range(10):
    !mkdir /home/leoyeh.me/深度學習/訓練圖檔/{i}
from PIL import Image
for i in range(len(df_fashionmnist)):
    pixels = df_fashionmnist.iloc[i][1:]
    pixels = pixels.as_matrix() 
    pixels = np.reshape(pixels, (-1, 28))
    array = np.array(pixels, dtype=np.uint8)
    img = Image.fromarray(array)
    img.save('/home/leoyeh.me/深度學習/訓練圖檔/' + str(int(df_fashionmnist.iloc[i][0])) + '/' + str(i) + '.png')
!ls /home/leoyeh.me/深度學習/訓練圖檔/0
s.table.addcaslib(lib='深度學習', datasource={'srcType': 'path'}, path="/home/leoyeh.me/深度學習/",subdirectories=True)
s.image.loadimages(caslib='深度學習',casout=dict(name='訓練圖檔', blocksize='128',replace=True), recurse=True, decode=True,distribution="RANDOM", labelLevels=1, path='訓練圖檔')
s.image.summarizeimages(imagetable='訓練圖檔')['Summary']
s.loadactionset('sampling')
s.stratified(display={'names':'STRAFreq'},
                 output={'casOut':{'name':'訓練圖檔', 'replace':True}, 'copyVars':'ALL'},
                 samppct=20, partind=True, seed=10,
                 table={'name':'訓練圖檔'},
                 outputTables={'names':{'STRAFreq'},'replace':True})
trainTbl = s.CASTable('訓練圖檔', replace=True)
trainTbl.shuffle(casout=dict(name=trainTbl,replace=True), table=trainTbl)
trainTbl.columninfo()
trainTbl.freq(inputs='_PartInd_')
trainTbl.head()
s.buildmodel(model=dict(name='lenet',replace=True),type='CNN')
s.addlayer(model='lenet', name='data', replace=True,
          layer=dict(type='input',nchannels=1, width=28, height=28, scale=0.004, std='STD'))
s.addLayer(model='lenet', name='conv1', replace=True, 
           layer=dict(type='convolution',act='relu', nFilters=32, width=5, height=5, stride=1, init='xavier'), 
           srcLayers=['data']) 
s.addLayer(model='lenet', name='pool1', replace=True,
           layer=dict(type='pooling', width=2, height=2, stride=2, pool='max'), 
           srcLayers=['conv1'])
s.addLayer(model='lenet', name='conv2', replace=True,
           layer=dict(type='convolution',act='relu', nFilters=64, width=5, height=5, stride=1, init='xavier'), 
           srcLayers=['pool1'])
s.addLayer(model='lenet', name='pool2', replace=True, 
           layer=dict(type='pooling',width=2, height=2, stride=2, pool='max'), 
           srcLayers=['conv2'])
s.addLayer(model='lenet', name='fc1',  replace=True,
           layer=dict(type='fullconnect',n=1024, act='relu', init='xavier',dropout = 0.4), 
           srcLayers=['pool2'])
s.addLayer(model='lenet', name='outlayer', replace=True,
           layer=dict(type='output',n=10,act='softmax', init='xavier'), 
           srcLayers=['fc1'])
s.modelInfo(model='lenet')
sw.options.cas.print_messages = True
trainTbl.shuffle()
lenet = s.dltrain(model='lenet',
              table=dict(name=trainTbl, where='_PartInd_ = 0.0'),
              validTable=dict(name='訓練圖檔', where='_PartInd_ = 1.0'),
              seed=54321, 
              nthreads=2,
              inputs=['_image_'], 
              target='_label_', 
              modelWeights=dict(name='LeNet_Weights', replace=True),
              optimizer=dict(mode=dict(type='synchronous'),
                             miniBatchSize=64,
                             algorithm=dict(method='momentum',learningRate = 1e-2,),
                             maxEpochs=5, 
                             loglevel=1)
                 )
lenet
s_test = CAS(cashost, casport, useremail, userpassword, caslib="casuser")
s_test.sessionprop.setsessopt(caslib='casuser', timeout=31535000)
castblfashionmnist_test = s_test.CASTable("FASHION_MNIST_TEST", replace=True); 
df_fashionmnist_test = castblfashionmnist_test.to_frame()
df_fashionmnist_test
!rm -rf /home/leoyeh.me/深度學習/測試圖檔
!mkdir /home/leoyeh.me/深度學習/測試圖檔
for i in range(10):
    !mkdir /home/leoyeh.me/深度學習/測試圖檔/{i}
from PIL import Image
for i in range(len(df_fashionmnist_test)):
    pixels = df_fashionmnist_test.iloc[i][1:]
    pixels = pixels.as_matrix() 
    pixels = np.reshape(pixels, (-1, 28))
    array = np.array(pixels, dtype=np.uint8)
    img = Image.fromarray(array)
    img.save('/home/leoyeh.me/深度學習/測試圖檔/' + str(int(df_fashionmnist_test.iloc[i][0])) + '/' + str(i) + '.png')
!ls /home/leoyeh.me/深度學習/測試圖檔/0
s.image.loadimages(caslib='深度學習',casout=dict(name='測試圖檔', blocksize='128',replace=True), recurse=True, decode=True,distribution="RANDOM", labelLevels=1, path='測試圖檔')
s.image.summarizeimages(imagetable='測試圖檔')['Summary']
testTbl = s.CASTable('測試圖檔', replace=True)
testTbl.shuffle(casout=dict(name=testTbl,replace=True), table=testTbl)
testTbl.columninfo()
s.dlscore(model='lenet',
          initWeights='LeNet_weights',
          table=testTbl, 
          copyVars={'_label_','_image_'}, 
          layerImageType='jpg',
          layerOut=dict(name='layerOut', replace=True),
          casout=dict(name='LeNet_scored', replace=True)
          )

Last updated