2020年2月20日 星期四

筆記:人工智慧的應用,雲象科技

組織辨識 連結google ai

最近這幾年,人工智慧相關議題火熱,市場上各家公司不管三七二十一,紛紛宣稱自家產品都內建有人工智慧功能,反正,誰也說不清人工智慧對產品能有何助益,更無法從外表判定一項產品內部是否有使用人工智慧技術。各說各話下,人工智慧在行銷上噱頭意義遠大於實際價值。

不過這當然是我井底之蛙的錯誤印象,台灣各行業人才濟濟,只要留心必定會發現,仍有許多結合實務與學術的應用。前些時間參加研討會,接觸到【雲象科技】,會中並介紹幾項他們已經開發或正在開發產品。


項目合作醫院資料標記正確率其他
鼻咽癌長庚360位6位醫師,每一個區域分別是何種細胞 時間:六個月97%醫學影像解析度非常高,逐一進行掃描,檔案超過GPU所能儲存的範圍。要解決類似問題必須將圖像切割儲存。再針對該問題繼續補上所需的病理切片影像進行訓練後,模型逐漸完備。
脊椎X光片長庚第一階段1500位X光
第二階段18000位
25個脊椎上特點進行標記,再由這些點計算出脊椎的角度。醫生不再需在X光片上畫圖測量角度。
骨髓抹片台大萬張影像,還要標註10萬多顆細胞12位時間:預估兩年骨髓抹片,就包含多種細胞,依其種類和成熟度來分,就有將近40種。

以軟體工程師來評價,這幾項產品都是百分之百以人工智慧為主的服務,經過查證,雲象科技的確也是科技部宣傳人工智慧業務的樣板廠商。所以未來當有人再提出,人工智慧是否能商品化這類質疑時,請他們去看雲象科技,就能得到答案。

--- 第一項收穫,台灣真的有人工智慧為核心的廠商

這些產品使用的技術是物件辨識技術,也是人工智慧各分支中的顯學,廣泛應用在人臉辨識、自動駕駛和生物組織比對,眾多天才投入,和各式各樣的學術競賽(kaggle連結),知名實驗室論文(google lab連結)推波助瀾下,正確率動輒達到百分之九十九,這些都讓局外人(例如我)以為只要用國外已經訓練好的資料庫,直接接入流程,就可以賣出軟體。

所以,當我首次聽到雲象科技介紹相關產品時,只覺得這一切是如此理所當然,只要從網路下載訓練資料,套用現有模型即可啊。演講者繼續在台上口沫橫飛介紹他們如何辛苦的進行資料標記,對坐在台下的我來說,完全無動於衷,甚至一頭霧水。

講者繼續解釋,對雲象科技來說,資料的取得和標記才是重點中的重點,或許因為法規限制,也或許因為解析度的需要,似乎每一個產品線都需要重新掃描取得原始資料。

數位病理的高解析度特性(1GB以上),對硬體的需求也隨之提高。最有挑戰性的狀況,是當一個100層的ResNet分析一張解析度為10,000×10,000的影像時,GPU甚至需要搭配600GB的系統記憶體才能運算,遠遠超過單張32GB內建記憶體的頂級GPU的能力。雖然能用影像切割訓練模型,但縮小影像仍會流失一些資訊。難怪他們也是國網中心的常客。

更重要的是,每個產品都需要投入大量人力進行標記,以月或是年為單位,更是家常便飯,為了加速整體開發時間,最近他們甚至將人才延攬對象擴展至醫生,要聘請醫生來負責不同程度的影像標註。希望能涵蓋專業醫科、醫學檢驗,以及護理相關等領域。

--- 第二項收穫,原始資料的取得和標記,更勝於人工智慧模型

經過前述掃描和標記過程,終於生成產品,這幾項產品的應用都很直覺易懂,所以想像中,當產品推出後,就會得到廣大迴響及高接受度。但演講者卻說,實際情況並非如此。並提出幾點原因。

一、醫療器材審核門檻高。
眾所周知,醫療器材的審核制度繁瑣且複雜,尤其人工智慧是新概念,缺乏可靠的風險評估機制,使得市場先行者必須承擔更多風險。自己從事TYPE C等級醫療產品開發,完全能體會這其中艱辛。

二、仍無法完全取代專業判斷。
目前人工智慧技術已可在大量組織標本中快速且正確圈選出小量癌細胞,對檢驗科醫師來說,這個工具的確能加速癌細胞辨識速度。但相反情況下,現有技術不可能找出全部的癌細胞,也會有錯誤的判斷,仍要人力介入。

三、產品難訂價。
對醫院來說,核磁共振機這類產品有明確的收益,成本和折舊都能精確量化,也容易比價。目前人工智慧服務都屬於附加功能,效益不明確,更難以訂價。

四、新產品難進入舊流程。
醫生大多不願意改變作業流程,且已經花大錢購買的設備,若沒有影像資料出口,也無法進行分析處理,若要進入現有流程,產品設計上就得成為獨立設備,並且重視資料傳輸能力。

--- 第三項收穫,好產品不等於好銷售額

根據資料,這家公司並非全新公司,五年前成立時,是提供玻片掃描、建立數位病理資料庫的服務,讓醫生不必再捧著一盒盒玻片到實驗室用顯微鏡觀察組織切片,當時宗旨是改造數位化程度落後的病理科,和人工智慧沒有關係。當時資本額為六十萬元。

創辦人(台大醫學系畢業,目前還在讀美國南加大博士)是在見識到深度學習在ImageNet影像辨識大賽的潛力後,轉型為醫療影像人工智慧公司,以原有的病理影像為基礎,結合理工科朋友,共同創業,才開啟他們多項人工智慧產品的研究與開發。參考公司登記資料(連結),目前公司資本額為六千萬元,並且朝更多領域擴展中。

--- 第四項收穫,即使有上述種種困難,但只要掛著人工智慧,公司估值的確會成長幾十倍啊(笑)


2020年2月7日 星期五

筆記:TensorFlow語法與使用


  • 深度學習概念(連結)
  • CNN及眾多應用(連結)
  • RNN及眾多應用(連結)
  • TensorFlow安裝(連結)
  • TensorFlow語法與使用(連結)
  • Porting AI ( QCS605 QCS603 Android )(連結)

基本語法

初始化

init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)

執行訓練

sess.run(tf.train.GradientDescentOptimizer(learning_rate = 0.5)
.minimize(tf.reduce_mean(tf.square(y - y_data))))
y_data是初始設定固定值,y是每次不同變數 可以由
tf.Variable(tf.random_uniform([1], -1.0, 1.0)) 變數會變動產生


取得運作資料

sess.run(W)  中間放入要取得的名稱


Graphs 建立運算元

繼續是引入layer也就是類神經網路的概念。
每一個層的單元類型如下
weights_l1 = tf.Variable(tf.random_normal([1, 10]))#10个神经元
biases_l1 = tf.Variable(tf.zeros([1, 10]))
wx_plust_b_l1 = tf.matmul(x, weights_l1) + biases_l1
l1 = tf.nn.sigmoid(wx_plust_b_l1)
程式化這範例,狀況如下

def add_layer(inputs, input_tensors, output_tensors, activation_function = None):
    W = tf.Variable(tf.random_normal([input_tensors, output_tensors]))
    b = tf.Variable(tf.zeros([1, output_tensors]))
    formula = tf.add(tf.matmul(inputs, W), b)
    if activation_function is None:
        outputs = formula
    else:
        outputs = activation_function(formula)
    return outputs
實際程式化,就如同下述
# 添加 1 個隱藏層  有十個節點,由一對應十
hidden_layer = add_layer(x_feeds, input_tensors = 1, output_tensors = 10, activation_function = None)
# 添加 1 個輸出層  有一個節點,由十對應一
output_layer = add_layer(hidden_layer, input_tensors = 10, output_tensors = 1, activation_function = None)
test5.py是test1.py的改編版本。只不過兩層的activation function都是null。
單純比較test1和test5會發現,兩者的梯度數字明顯不同,在同樣iterator下,正確率也不同。但已經可以驗證神經網路層是有用處的。

test3.py是莫凡test2.py的改編版本,兩者概念相似,只是第一層的activation function稍有不同 l1 = add_layer(xs, 1, 10, activation_function=tf.nn.relu)。




矩陣運算概念

test27.py


變數

(1) 常數張量:
一開始來看看「常數張量」,常數指的是在Model中不會改變的數值。
tensor = tf.constant([1, 2, 3, 4, 5, 6, 7], dtype=tf.int32)
Random的使用方式 test4.py

(2) 變數張量:
與常數截然不同的就是變數,「變數張量」是指在訓練當中可以改變的值,一般「變數張量」會用作於Machine Learning需要被訓練的參數,如果你沒有特別設定,在最佳化的過程中,Tensorflow會自動調整「變數張量」的數值來最佳化。
tensor = tf.Variable(tf.truncated_normal(shape=(3, 5)))
因為變數通常是未知且待優化的參數,所以我們一般會使用Initalizer來設定它的初始值,tf.truncated_normal(shape=(3,5))會隨機產生大小3x5的矩陣,它的值呈常態分佈但只取兩個標準差以內的數值。
如果今天你想要有一個「變數張量」但是又不希望它因為最佳化而改變,這時你要特別指定trainable為False。
tensor = tf.Variable(5, trainable=False)
Variable 則是在訓練神經網路時,儲存可更新參數的容器。

(3) 置放張量:
另外有一些張量負責擔任輸入窗口的角色,稱為Placeholder。
tensor = tf.placeholder(tf.float32, shape=(None, 1000))
因為我們在訓練之前還尚未知道Data的數量,所以這裡使用None來表示未知。tf.placeholder在Graph階段是沒有數值的,必須等到Session階段才將數值給輸入進去。
x_feeds = tf.placeholder(tf.float32, shape = [None, 1])
sess.run(train, feed_dict = {x_feeds: x_data, y_feeds: y_data})
test29.py
placeholder 是 TensorFlow 中一個很有趣的用法,可以預先在神經網路中 佔位,而不用事先定義好輸入的資料,這也是 TensorFlow 中計算圖(Computing Graph)的概念,之後再使用 Session 將資料傳入計算圖中。
在 placeholder 中定義節點的形狀 shape=(None, 2),None 的意思是二維矩陣的 row 事先尚未定義,也就是「不定義輸入資料的筆數」,隨著透過 Session 將資料傳入計算圖時,動態符合輸入資料的筆數。
往後在訓練神經網路時,可以透過設定超參數 batch_size 決定每次訓練資料的筆數,保持神經網路的彈性。
placeholder 用於在計算圖中傳入 feature 與 label

(4) 操作型張量:
這類張量並不含有實際數值,而是一種操作,常用的「操作型張量」有兩種,第一種是作為最佳化使用,
loss = ...
train_op = tf.train.GradientDescentOptimizer(learning_rate=0.5).minimize(loss)
選擇Optimizer和最佳化的方式來定義最佳化的操作方法,上述的例子是使用learning_rate為0.5的Gradient Descent來降低loss。
另外一種是初始化的操作,
init_op = tf.global_variables_initializer()
這一個步驟是必要的但常常被忽略,還記得剛剛我們定義「變數張量」時有用到Initalizer,這些Initalizer在Graph完成時還不具有數值,必須使用init_op來給予數值,所以記住一定要放init_op進去Graph裡頭,而且必須先定義完成所有會用到的Initalizer再來設定這個init_op。

命名

模組化的程式中使用 with tf.name_scope(): 為每個運算元命名(連結



TensorBoard


神經網絡運算初始之後,利用 tf.summary.FileWriter() 將視覺化檔案輸出。測試為test6.py。
產生的檔案位置在TensorBoard這個目錄中(自動生成的)
執行 $ tensorboard --logdir="TensorBoard/"


打開瀏覽器,在網址列輸入:http://localhost:6006,就可以在 Graphs 頁籤下看到神經網絡圖。


除了可以使用 with tf.name_scope(): 為每個運算元命名,我們還可以在使用 tf.Variable() 或者 tf.placeholder() 建立變數或輸入資料時,利用 name = 參數進行命名。test7.py

參考資料

我們利用 tf.summary.histogram() 與 tf.summary.scalar() 將訓練過程記錄起來,然後在 Scalars 與 Histograms 頁籤檢視。test8.py

Scalars

Histograms

資料儲存格式


TensorFlow 的模型格式有很多種,針對不同場景可以使用不同的格式,只要符合規範的模型都可以輕易部署到線上服務或移動裝置上,這裡簡單列舉。 
* Checkpoint: 用於儲存模型的權重,主要用於模型訓練過程中引數的備份和模型訓練熱啟動。 
* GraphDef:用於儲存模型的Graph,不包含模型權重,加上checkpoint後就有模型上線的全部資訊。 
* ExportModel:使用 exportor 介面匯出的模型檔案,包含模型 Graph 和權重可直接用於上線,但官方已經標記為 deprecated 推薦使用 SavedModel。 
* SavedModel:使用 saved_model 介面匯出的模型檔案,包含模型 Graph 和許可權可直接用於上線,TensorFlow 和 Keras 模型推薦使用這種模型格式。
 * FrozenGraph:使用 freeze_graph.py 對 checkpoint 和 GraphDef 進行整合和優化,可以直接部署到 Android、iOS 等移動裝置上。 
* TFLite:基於 flatbuf 對模型進行優化,可以直接部署到 Android、iOS 等移動裝置上,使用介面和 FrozenGraph 有些差異。
參考

PB

通常我們使用 TensorFlow時保存模型都使用 ckpt 格式的模型文件,使用類似的語句來保存模型
tf.train.Saver().save(sess,ckpt_file_path,...)
使用如下語句來恢復所有變量信息
saver.restore(sess,tf.train.latest_checkpoint('./ckpt'))
但是這種方式有幾個缺點,首先這種模型文件是依賴TensorFlow 的,只能在其框架下使用;其次,在恢復模型之前還需要再定義一遍網絡結構,然後才能把變量的值恢復到網絡中。
google推薦的保存模型的方式是保存模型為PB 文件,它具有語言獨立性,可獨立運行,封閉的序列化格式,任何語言都可以解析它,它允許其他語言和深度學習框架讀取、繼續訓練和遷移TensorFlow 的模型。它的主要使用場景是實現創建模型與使用模型的解耦, 使得前向推導 inference的代碼統一。另外的好處是保存為 PB 文件時候,模型的變量都會變成固定的,導致模型的大小會大大減小,適合在手機端運行。

根目錄執行test13.py,會執行train和test,並且在最後產生graph.pb檔案。並且用test14.py觀察graph.pb檔案(用TensorBoard打開它)
步驟一,python test14.py --model_dir graph.pb --log_dir tensor/logs/convnet/
步驟二,tensorboard --logdir=tensor/logs/convnet/
步驟三,由瀏覽器開啟http://localhost:6006/。透過瀏覽器的互動觀察,可發現pb檔案中,不僅包含input到conv2d等等的處理過程,也包括teain時設定的各層和降階方式。所以測試程式難度比想像要單純,會寫訓練程式就一定能寫測試程式。

以pb為資料中心的範例test22 這裡直接把資料存graph.pb讀取動作
python train.py  python test.py

這裡有三個項目要討論
1.pb格式, 都是binary形式,如果要產生成txt格式,可以用
tf.train.write_graph(graph, '.', 'graph.pbtxt', as_text=True)
讀取也能夠直接讀取txt但是要例外考慮一些技巧(連結

2.利用tf.train.write_graph()預設情況下只匯出了網路的定義(並沒有權重),上面test22是因為使用了convert_variables_to_constants,上面test13是因為各個變數都使用constant。如果沒有這兩種,就會變成只有一個空殼。 連結

資料正規化


已訓練完畢的資料集,動則數百M,資料傳送耗時,也無法放入IOT有限的記憶體中。所以第一步驟就是,以較小檔案容量,換取些微準確率。技術上來說,訓練時的確需要大量伺服器協助產生訓練資料。但在測試階段,卻可以藉由犧牲部份準確率,換取減少訓練資料一半以上,讓手持裝置也可以執行分析程式。