從數據結構到Python實現:如何使用深度學習分析醫學影像

從數據結構到Python實現:如何使用深度學習分析醫學影像

來自專欄人工智慧特訓營1 人贊了文章

運用深度學習技術進行圖像和視頻分析,並將它們用於自動駕駛汽車、無人機等多種應用場景中已成為研究前沿。近期諸如《A Neural Algorithm of Artistic Style》等論文展示了如何將藝術家的風格轉移並應用到另一張圖像中,而生成新的圖像。其他如《Generative Adversarial Networks》(GAN)以及「Wasserstein GAN」等論文為開發能學習生成類似於我們所提供的數據的模型做了鋪墊。因此,它們在半監督學習領域打開了新世界的大門,也為將來的無監督學習奠定了基礎。

儘管這些研究領域處於通用圖像層面,但我們的目標是將它們應用於醫學圖像以輔助醫療。我們需要從基礎開始。本文第一部分將從圖像處理的基礎、醫學圖像格式化數據的基礎以及一些可視化的醫療數據談起。而後一部分文章將深入探究卷積神經網路,並使用 Keras 來預測肺癌。

基本圖像處理

(利用 python 實現)

圖像處理庫有很多,但 OpenCV(開源計算機視覺庫,open computer vision)憑藉其廣泛的支持且可用於 C++、java 和 python 的優點而成為主流。而我更偏向於使用 jupyter notebook 導入 OpenCV。

你可以使用 pip install opencv-python,也可以從 opencv.org 網站直接進行安裝。

安裝 opencv

現在打開 Jupyter notebook 並確認能夠導入 cv2。你還需要 numpy 和 matplotlib 庫來在 notebook 內查看圖片。

現在來檢查能否打開並通過鍵入下述代碼在筆記本上查看圖像。

通過 OpenCV 進行圖像載入的示例

基本人臉識別

我們來做點有意思的事情吧,比如人臉識別。我們將使用一種最初由 Rainer Lienhart 開發的正面人臉識別器,它使用了基於開源 xml 殘基(stump-based)的 20x20 柔和 adaboost 演算法。

關於 Haar-cascade 檢測的詳細範例:docs.opencv.org/trunk/d

使用 OpenCV 進行人臉識別

在文檔區使用 opencv 進行圖像處理的例子不勝枚舉。

我們已經了解了圖像處理的基礎,下面來了解醫學圖像格式吧。

醫學圖像數據格式

醫學圖像以數字成像和通信(DICOM)為存儲與交換醫學圖像數據的標準解決方案。該標準的第一版發佈於 1985 年,之後有少許修改;它使用了文件格式和通信協議如下。

  • 文件格式:所有患者的醫療圖像都以 DICOM 文件格式進行保存。該格式不僅具有與圖像相關的數據(如用於捕獲圖像的設備和醫療處理情境),還具有關於患者的 PHI (受保護的健康信息,protected health information),如姓名、性別、年齡等。醫療影像設備可以創建 DICOM 文件,而醫生可以使用 DICOM 查看器以及可顯示 DICOM 圖像的計算機應用程序來讀取並診斷從圖像獲得的結果。
  • 通信協議:DICOM 通信協議用於搜索檔案中的成像研究,並將成像研究恢復到工作站來顯示。連接到醫院網路的全部醫學成像應用程序都使用 DICOM 協議來交換信息,其中大部分信息是 DICOM 圖像,但還包括患者和手術信息。此外還有更先進的網路命令用於控制並跟蹤治療、調整進程、報告狀態,並在醫生和成像設備之間共享工作負載。

現有篇博文很細緻地描述了 DICOM 標準,此處為鏈接:dicomiseasy.blogspot.com

分析 DICOM 圖像

Pydicom 是一個 python 包,它很適合分析 DICOM 圖像。本節將闡述如何在 Jupyter notebook 上呈現 DICOM 圖像。

安裝 Pydicom 使用:pip install pydicom。

安裝 pydicom 包之後,回到 Jupyter notebook 進行操作。在 notebook 中導入 dicom 包以及其他包,如下所示:

我們也能使用 pandas、scipy、skimage 以及 mpl_toolkit 等其他的包來進行數據處理與分析。

你可以在線獲得很多免費的 DICOM 數據集,但下述數據集在入門階段定能有所幫助:

  • Kaggle Competitions and Datasets:它是我的最愛。請查閱肺癌競爭和糖尿病視網膜病變的數據:kaggle.com/c/data-scien
  • Dicom Library:面向教育和科學的 DICOM 庫,其提供免費的在線醫療 DICOM 圖像或視頻文件共享服務。
  • Osirix Datasets:提供通過各種成像模式獲取的大量人類數據集。
  • Visible Human Datasets:在這裡可視化人類計劃的一部分數據可以免費利用,這很奇怪,因為獲取這些數據既不免費也不輕鬆。
  • The Zubal Phantom:該網站免費提供 CT 和 MRI 這兩種男性的多個數據集。

請下載 dicom 文件並載入到 jupyter notebook 中。

現在將 DICOM 圖像載入到列表中。

第一步:在 Jupyter 中進行 DICOM 圖像的基本查看操作

在第一行載入第一個 DICOM 文件來提取元數據,這個文件將賦值為 RefDs,其文件名會列在 lstFilesDCM 列表的頂端。

然後來計算 3D NumPy 數組的總維度,它等於在笛卡爾坐標軸中(每個切片的像素行數*每個切片的像素列數*切片數)。最後,使用 PixelSpacing 和 SliceThickness 屬性來計算三個軸之間的像素間距。我們將把數組維度儲存在 ConstPixelDims 中,把空間儲存在 ConstPixelSpacing [1] 中。

第二步:查看 DICOM 格式的細節

CT 掃描中的測量單位是亨氏單位(Hounsfield Unit,HU),它是輻射強度的度量。CT 掃描儀經過高度校準以精確測量。

每個像素都被分配了一個數值(CT 號),它是包含在相應體素(corresponding voxel)中的所有衰減值的平均數。將這個數字與水的衰減值進行比較,並以任意單位中的亨氏單位(HU)為刻度進行顯示。

這個刻度將水的衰減值(HU)標為 0。CT 數量的範圍是 2000HU,但一些現代掃描儀具有較高的 HU 範圍,最高可達 4000。每個數字表示在光譜的其中一端會出現+1000(白色)和-1000(黑色)的灰色陰影。

一些掃描儀具有圓柱形掃描範圍,但其輸出圖像卻是矩形。落在這些邊界之外的像素具有-2000 的固定值。

第一步通常是將這些值設置為 0。接著,通過與重新縮放的斜率相乘並添加截距來返回到亨氏單位(斜率和截距均很方便地存儲在掃描的元數據中!)。

下部分將會用到 Kaggle 的肺癌數據集,並使用 Keras 卷積神經網路進行建模;它將以上文所提供的的信息為基礎。

在上一部分文章中,我們介紹了一些使用 OpenCV 進行圖像處理的基礎知識,以及 DICOM(醫學數字影像和通訊)圖像基礎。下面我們將從卷積神經網路(Convolutional Neural Nets)的視角來談一談深度學習基礎。在第三部分文章里,我們將以 Kaggle 的肺癌數據集為實例,來研究一下在一個肺癌 DICOM 圖像中要尋找的關鍵信息,並使用 Kera 開發出一個預測肺癌的模型。

卷積神經網路 (CNN) 基礎

為了理解卷積神經網路的基礎,我們首先要搞清楚什麼是卷積。

什麼是卷積?

那麼在泛函分析中,卷積(Convolution)是通過兩個函數 f 和 g 生成第三個函數的一種數學運算元,表徵函數 f 與 g 經過翻轉和平移的重疊部分的面積。所以在簡單定義下,設 f(x)、g(x) 是 R 上的兩個可積函數,作積分:

則代表卷積。理解這個定義的簡單方式就是把它想像成應用到一個矩陣上的滑動窗方程。

有著 3×3 過濾器的卷積

在上面的圖片中,應用到矩陣上的滑動窗是綠色,而滑動窗矩陣則是紅色。輸出就是卷積特徵矩陣。下面的圖片顯示了兩個矩形脈衝(藍色和紅色)的卷積運算及其結果。

Jeremy Howard 在他的 MOOC 上用一個電子表格解釋了卷積,這是理解基礎原理的一種很好的方式。現在有兩個矩陣,f 和 g。f 和 g 進行卷積運算的結果,是第三個矩陣「Conv layer 1」,它由兩個矩陣的點積給出。如下所示,這兩個矩陣的點積是一個標量。

兩個矩陣的點積

現在讓我們按照 Jeremy 的建議用電子表格來演示一下,輸入矩陣是函數 f(),滑動窗矩陣是過濾器方程 g()。那麼這兩個矩陣元素的乘積和就是我們要求的點積,如下所示。

讓我們把這個擴展到一個大寫字母「A」的圖片。我們知道圖片是由像素點構成的。這樣我們的輸入矩陣就是「A」。我們選擇的滑動窗方程是一個隨機的矩陣 g。下圖顯示的就是這個矩陣點積的卷積輸出。

什麼是卷積神經網路 (CNN) ?

在我看來,一個簡單的卷積神經網路 (CNN) 就是一系列神經網路層。每一層都對應著一個特定的函數。每個卷積層是三維的(RGB),所以我們用體積作為度量。更進一步的,CNN 的每一層都通過一個微分方程向另一層傳遞一個體積量的激活。這個微分方程被稱為激活函數或傳遞函數。

CNN 的實體有多種:輸入,濾波器(或核函數)、卷積層、激活層、池化層、以及批量歸一化層。這些層在不同排列和不同規則下的組合形成了不同的深度學習演算法。

輸入層:通常一個 CNN 的輸入是一個 n 維陣列。對於一個圖像來說,就是三個維度的輸入——長度,寬度和深度(即顏色通道)。

過濾器或核函數:正如下面這張來自 RiverTrail 的圖像所示,一個過濾器或核函數會滑到圖像的每個位置上並計算出一個新的像素點,這個像素點的值是它經過的所有像素點的加權和。在上面的電子表格例子中,我們的過濾器就是 g,它經過了 f 的輸入矩陣。

卷積層:輸入矩陣和過濾器的點積形成的一個新矩陣,稱為卷積矩陣或卷積層。

下面的網址中有一個很好的解釋填補、跨步和轉置是如何工作的視覺圖表。

激活層:

激活函數可根據是否飽和分為兩種類型。

飽和激活函數都是雙曲型和雙曲正切型,而非飽和激活函數都是修正線性單元(ReLU)及其變體函數。使用非飽和激活函數有兩方面的優勢:

  • 第一是可以解決所謂的「爆炸/消失梯度(exploding/vanishing gradient)」。
  • 第二是可以加快函數收斂速度。

雙曲函數:把一個實數值輸入擠壓到 [0,1] 區間範圍內

σ(x) = 1 / (1 + exp(?x))

雙曲正切函數:把一個實數值輸入擠壓到 (-1,1)區間內

tanh(x) = 2σ(2x) ? 1

ReLU

ReLU 代表單調線性單元(Rectified Linear Unit)。它是輸入為 x 的最值函數 (x,0),比如一個卷積圖像的矩陣。ReLU 接著把矩陣 x 中的所有負值置為零,並保持所有其他值不變。ReLU 是在卷積之後計算出來的,因此會出現一個非線性的激活函數,如雙曲正切或雙曲函數。Geoff Hinton 在他的 nature 論文里第一次討論這個問題。

ELUs

指數線性單元(Exponential linear units)試圖使平均激活接近於零,這樣就能加速學習。ELUs 也能通過正值認定避免消失梯度的出現。研究顯示,ELUs 比 ReLUs 有更高的分類準確性。

來源:image-net.org/challenge[(1×96×6, 3×512×3, 5×768×3, 3×1024×3, 2×4096×FC, 1×1000×FC) 層 × 單元 × 接受域或完全連接(FC)的堆疊構成的 15 層 CNN。2×2 的最大池化,每次堆疊後有 2 步幅,第一個 FC 之前有 3 層的空間金字塔池。]

來源:維基百科

滲漏 ReLU

ReLU 中的負值部分完全被拋棄,與之相反,滲漏 ReLU 給負值部分賦了一個非零斜率。滲漏修正線性激活在聲子模型(Maas et al., 2013)中第一次被引入。數學上,我們有

來源:卷積網路中修正激活的經驗主義演化

其中 ai 是在 (1, 正無窮) 區間內的固定參數。

參數化修正線性單元 (PReLU)

PReLU 可被視為滲漏 ReLU 的一個變體。在 PReLU 中,負值部分的斜率是從數據中學習得來的,而非預先定義好的。PReLU 的創作者們聲稱它是 ImageNet 歸類(Russakovsky et al., 2015) 任務中(機器)超越人類水平的關鍵因素。它與滲漏 ReLU 基本相同,唯一的區別就是 ai 是通過反向傳播訓練學習到的。

隨機滲漏修正線性單元 (RReLU)

隨機滲漏單調線性單元 (RReLU) 也是滲漏 ReLU 的一種變體。在 RReLU 中,負值部分的斜率是在給定訓練範圍內的隨機取值的,然後在測試中固定下來。RReLU 最顯著的特徵是在訓練過程中,aji 是一個從一致分布 U(l,u) 上取樣得到的隨機數。正式數學表達如下:

下面顯示了 ReLU, 滲漏 ReLU, PReLU 和 ReLU 的對比。

ReLU, 滲漏 ReLU, PReLU 和 ReLU,對於 PReLU,ai 是學習到的;而對於滲漏 ReLU,ai 是固定的。對於 RReLU,aji 是一個在給定區間內取樣的隨機變數,在測試中保持不變。

雜訊激活函數

這些都是拓展後包括了 Gaussian 雜訊(Gaussian noise)的激活函數。

來源:維基百科

池化層

池化層的目標是逐漸地減少矩陣的尺寸,以減少網路中參數的數量和計算,這樣也就能控制過擬合。池化層在輸入的每個深度切片上獨立操作,並使用最大化和平均運算來重置其空間尺寸。最常見的形式,一個採用了步幅 2,尺寸 2x2 過濾器的池化層,同時沿著寬度和高度,以幅度 2 將輸入中的每個深度切片向下取樣,丟棄了激活值的 75%。在此情況下,每個最大值運算都取了 4 個數字(某些深度切片中的小 2x2 區域)的最大值。深度方向的維度保持不變。更一般的來說,池化層就是:

注意:這裡我們把 2 x 2 window 滑動了兩個細胞(也被叫做步幅),並取了每個區域的最大值。

批歸一化層:

批歸一化是歸一化每個中間層的權重和激活函數的有效方式。批歸一化有兩個主要的好處:

1. 對一個模型加入批歸一化能使訓練速度提升 10 倍或更多

2. 由於歸一化極大降低了偏遠輸入的小數字過度影響訓練的能力,它也能降低過擬合。

全連接層:

全連接層是一個傳統的多層感知器(Multi Layer Perceptron),它在輸出層使用了一個Softmax 函數。「全連接」這個術語就說明了前一層和後一層的每個神經元都是連接起來的。Softmax 函數即對數函數(logistic function)的一般化情況,它把一個取值區間為任意實數的 K 維向量「擠壓」成一個取值區間在(0,1)內且和為1的 K 維向量。

Sofxmax 激活一般被用於最終的完全連接層,隨著它的值在 0 和 1 之間不停變化,得到概率。

現在我們對 CNN 中不同的層次都有了一定的概念。運用這些知識我就能開發出肺癌探測所需的深度學習演算法。

第三部分

在最後一部分中,我們將透過卷積神經網路討論一些深度學習的基礎知識。在本文中,我們將側重於使用 Keras 和 Theano 的基礎深入學習。我們將給出兩個範例,一個使用 Keras 進行基本預測分析,另一個使用 VGG 的圖像分析簡單樣例。

我已經意識到這個話題的廣度和深度,它需要更多的文章來解讀。在之後的文章中,我們將討論處理中 DICOM 和 NIFTI 在醫學成像格式中的不同,進一步擴大我們的學習範圍並對如何對 2 維肺分割分析使用深度學習進行討論。然後轉到 3 維肺分割。我們同樣會討論如何在深度學習之前進行醫學圖像分析以及我們現在可以如何做。我非常開心也非常感謝我的新合作夥伴將這一切聚在一起——Flavio Trolese(4Quant 的合作夥伴)、 Kevin Mader(4Quant 的聯合創始人)以及 Cyriac Joshy(瑞士蘇黎世聯邦理工的講師)。

在本文中,我們將要討論 Keras 並使用兩個範例來展示如何使用 Keras 進行簡單的預測分析任務以及圖像分析。

什麼是 Keras?

Keras 網站是這麼介紹的——Keras 是 Theano 和 TensorFlow 的深度學習庫。

Keras API 在 Theano 和 TensorFlow 之上運行

Keras 是高級的神經網路 API,由 Python 編寫並可以在 TensorFlow 和 Theano 之上運行。其開發目的是使快速實驗成為可能。

什麼是 Theano 和 TensorFlow?

James Bergstra 博士等人在 Scipy 2010 發布的 Theano 是一個 CPU 和 GPU 數學表達式編譯器。它是一個 Python 庫,允許你有效地定義、優化和評估涉及多維數組的數學表達式。Theano 由 Yoshua Bengio 等一些高級研究員和蒙特利爾學習演算法研究所(MILA)共同完成。在 Scipy 2010 上一個非常棒的 Theano 教程。下圖顯示了截至 2010 年,Theano 在 GPU 和 CPU 與其他工具的對比。該結果最初在《Theano: A CPU and GPU Math Compiler in Python》一文中發表。

還有一些在 Theano 之上的建立其它的庫,包括 Pylearn2 和 GroundHog(同樣由 MILA 開發)、Lasagne、Blocks 和 Fuel.

TensorFlow 由 Google Brain 團隊的研究員與工程師開發。其被開發用於進行機器學習和深度神經網路研究,但是該系統也足以適用於其它領域。如其網站介紹的那樣,TensorFlow 是一個使用數據流圖的數值計算開源軟體庫。圖中的節點表示數學運算,圖的邊表示在其之間傳遞的多維數據數組(張量)。代碼的可視化如下圖所示。

TensorFlow:在異構分散式系統上的大規模機器學習

使用 Keras 的預測分析示例

在這個示例中,我們將使用 UCI 網站的 Sonar 數據集構建一個簡單的預測模型。在下面的代碼中,我們將會直接從 UCI 網站中得到數據並以 60:40 的比例將其分為訓練集與測試集。我們在預測模型中使用 Keras 並在標籤編碼中使用 sklearn。

在下一段代碼中,我們讀取數據集,並使用上面定義的函數查看數據。我們列印數據集,並找出需要編碼的因變數。

我們使用 scikit-learn 中的 LabelEncoder 進行標籤編碼,將 R 和 M 隱藏到數字 0 和 1 中。這樣的操作被稱為 one-hot 編碼。one-hot 編碼可將分類特徵轉換為對演算法更友好的格式。在這個示例中,我們使用使用「R」值 和「M」值分類我們的 Y 變數。使用標籤編碼器,它們分別被轉換為「1」和「0」。

scikit-learn 中的 LabelEncoder

然後使用 Keras 創建模型:

無預處理的簡單模型的精確度為 81.64%

使用 Keras 的圖像分析示例

為了用 Keras 解釋圖像處理,我們將使用來自 Kaggle 比賽的數據——狗和貓(kaggle.com/c/dogs-vs-ca)。該比賽的目的在於開發一種演算法以區分圖像是否包含狗或貓。這個任務對人、狗和貓來說很簡單,但是計算機卻很難做到。在該項挑戰中,有 25,000 張標記狗和貓的照片可用於訓練,並且在測試集中有 12,500 張照片,我們必須在挑戰中嘗試為其加上標籤。根據 Kaggle 網站,當該項比賽開始時(2013 年底):

當前最佳:目前的文獻顯示,機器分類器在該任務上可以達到 80% 以上的準確度。那麼如果我們能夠超過 80%,我們將在 2013 年處於最前沿。」

我強烈推薦觀看 Fast.ai 的 MOOC 以了解更多的細節,學習下一步和深度學習的前沿研究。我已經在下列代碼中引用 fast.ai,這是一個很好的起點,鏈接:fast.ai/

步驟 1:設置

從 Kaggle 網站下載狗和貓的數據,並存入你的筆記本電腦。本文中的示例均在 Mac 上運行。

基礎設置

Jeremy Howard 在他的班上提供了一個實用的 Python 文件,該文件有助於封裝基本函數。對於開始部分,我們將使用此實用文件。點擊下載:github.com/fastai/cours。當我們深入到更多細節時,我們將解壓該文件並查看其背後的內容。

步驟 2:使用 VGG

我們的第一步簡單地使用已經為我們創建好的模型,它可以識別許多類別的圖片(1,000 種)。我們將使用『VGG』,它贏得了 2014 年 ImageNet 比賽,是一個非常簡單的創造理解的模型。VGG ImageNet 團隊創造了更大、更慢、準確性略有提高的模型(VGG 19)和更小、更快的模型(VGG 16)。我們將使用 VGG 16,因為 VGG 19 過慢的性能與其在準確度上的微小提升不對等。

我們創建了一個 Python 類,Vgg16,這使得使用 VGG 16 模型非常簡單。Vgg 16 同樣可從 fast.ai 的 GitHub 中獲得:github.com/fastai/cours

步驟 3:實例化 VGG

Vgg16 構建在 Keras 之上(我們將在短時間內學到更多內容),Keras 是一個靈活易用的深度學習庫,該軟體庫是基於 Theano 或 Tensorflow 的一個深度學習框架。Keras 使用固定的目錄結構在批量讀取圖像和標籤組,每個類別的圖像必須放在單獨的文件夾中。

我們從訓練文件夾中獲取批量數據:

步驟 4:預測狗 vs 貓

步驟 5:總結並編碼文件

總結一下這篇文章,我推薦的狗和貓分類方法為:

總結

如果讀者跟著我們走到了這一步,那麼其實已經實現了上一部分文章中討論過的理論,並做了一些實際的編程工作。如果讀者們按照上述說明實現了這兩個案例,那麼就已經完成了使用 Keras 的第一個預測模型,也初步實現了圖像分析。由於代碼的長度,我們不在這裡討論細節只給出了鏈接。如果你查看鏈接有任何疑問,請聯繫 fast.ai。

至此,我們從最開始的資料庫安裝到醫學圖像數據格式的解釋,已經有了醫學影像處理的基本知識。隨後我們從卷積的定義到 CNN 的詳細構架與原理,進一步實踐的理論基礎已經完成了累積。最後一部分對前面的理論知識進行實踐,用 Python 實現了這一令人激動的模型。因此,我們希望讀者朋友能在這一循序漸進的過程中真正感受到醫學影像處理的樂趣。


推薦閱讀:

AI+醫學影像:第一步,電子膠片
《影像醫師和放射科面試題庫及崗位介紹》
霸氣總結!一句話看懂17種肺部感染的#醫學影像#診斷!
醫學影像行業科普

TAG:Python | 醫學影像 | 人工智慧 |