iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 2
11
AI & Machine Learning

以100張圖理解 Neural Network -- 觀念與實踐系列 第 2

Day 02:撰寫第一支 Neural Network 程式 -- 阿拉伯數字辨識

入門

照理講,我們應該先了解『神經網路』(Neural Network)概念,再談如何寫程式,但是,概念介紹內容有點硬,為了提高學習興趣,避免一開始就搞一堆數學公式,造成讀者跑光光,所以,還是柿子挑軟的吃,先從簡單的開始,與 Neural Network 先培養感情,如果您是條硬漢,可以等看完下一篇後,再回頭看這一篇。

我們就先來寫一支程式,目標是『辨識阿拉伯數字(0~9)』。

開發環境建置

首先是選擇開發環境,一般而言,Python 及 R 都有很好的支援及大量的函數庫(Library/Toolbox),而 Python 的框架較易於系統整合(Web、Mobile),因此我選擇 Python,但支援 Python 的 Neural Network 框架(Framework)也很多,參見下圖,要選擇哪一個呢?
https://ithelp.ithome.com.tw/upload/images/20171203/20001976VpzqjUYZm2.png
圖. Machine Learning 框架(Framework)GitHub評比,資料來源:【AI關鍵技術】三大熱門深度學習框架新進展
其中,TensorFlow 網路聲量最高,因此,我們採用它作為程式開發的基礎,Keras 則是支援TensorFlow的更高階函數庫(Meta Framework),可以用很簡潔的程式碼完成一個 Neural Network 模型,非常適合入門學習,因此我們就從 Keras 開始學起。

首先我們要建構開發環境,筆者以 Windows 環境為例,依序安裝以下軟體:

  1. 安裝 Anaconda: 它包含 Python 及常用的套件(Packages),例如NumPy、Pandas等矩陣運算的套件,Python V2 與 V3 不相容,我們選 V3,除非你以前曾大量使用 V2。
  2. 安裝 Tensorflow:可以選擇CPU或GPU版,安裝CPU版,直接在 DOS 下,輸入 pip install tensorflow。
  3. 安裝 Keras:在 DOS 下,輸入 pip install keras。

就是這麼簡單,當然,為了加速運算,你也可以安裝支援GPU版本的Tensorflow,NVidia支援CUDA的顯示卡請參考這裡 ,相關安裝程序請參考tensorflow官網,如果要在 Linux 環境開發也行,安裝內容不變, 請參考這裡

以我的電腦為例,配備如下圖,GPU顯示卡為NVIDIA GeForce GTX 750(1GB memory),實際安裝 Tensorflow GPU 版本的程序如下:
https://ithelp.ithome.com.tw/upload/images/20171204/20001976pi6nK0fKPH.png

  1. 下載 CUDA Toolkit,注意,須依據Tensorflow官網下載正確的版本,通常不是最新版,下載需先至nVidia官網建立帳號,再至 https://developer.nvidia.com/cuda-toolkit-archive 下載。
  2. 安裝完 CUDA Toolkit 後,再下載 cuDNN v6.x ,並將壓縮檔解開,複製到 CUDA Toolkit 8.0 安裝目錄下同名子目錄下。
  3. 將 CUDA Toolkit 8.0 安裝目錄下bin子目錄 放到 環境變數 Path 中,在 DOS 中執行Tensorflow時,才找的到相關 Dll。
  4. 安裝 Tensorflow GPU 版本,執行
    pip install --ignore-installed --upgrade tensorflow-gpu
  5. 在 DOS 中執行 python,接著輸入下列程式,應該就會有相關訊息出現。
    import tensorflow as tf
    hello = tf.constant('Hello, TensorFlow!')
    sess = tf.Session()
    print(sess.run(hello))

實際測試簡單的程式,確實快很多,但是記憶體太小,遇到複雜的程式,例如後續的CNN程式,需要儲存大量矩陣時,就GG了,所以,奉勸各位,要學 Neural Network,還是要花錢買張新一點的顯示卡,才能省去執行時去泡茶、喝咖啡的時間。

如果一切順利完成,就可以開始寫程式了。等一下,那 IDE 呢? 你可以用記事本、NodePad++、或者PyCharm,我則是使用 VS 2017 Community 版本,它也是一個很不錯的選擇喔,可以像 C# 一樣的除錯。另外,使用 Jupyter Notebook,可以讓你像作筆記一樣的寫程式,總之,戲法人人會變,端看你熟悉甚麼樣的環境與工具。

程式撰寫

撰寫 Keras 程式,我們需要了解簡單的 Python 語法,建議快速瀏覽『Introducing Python』這本書的第二~四章就夠了,它不只有中文版,也有免費的PDF電子書喔。

以下範例主要是利用 MNIST 資料集的訓練資料,建立單一隱藏層(Hidden Layer)的 Neural Network 模型,以預測實際影像是哪一個阿拉伯數字,如下圖:
https://ithelp.ithome.com.tw/upload/images/20171203/20001976IzRKKAP2I5.png
圖. 阿拉伯數字(0~9)辨識的流程

流程步驟如下:

  1. 先讀入訓練資料,本例為 60,000 筆資料,每筆資料是一個 28 * 28 的點矩陣圖形。
  2. 圖形的每一點都當成一個輸入變數(X),乘以一個權重W(i,j),向隱藏層(Hidden Layer)傳導,隱藏層的每一個節點會得到輸入變數的加權總和(W * X)。
  3. 再如法炮製,向輸出層傳導,輸出層的每一個節點會得到隱藏層的加權總和,將輸出層的每一個節點化為機率,就得到一個預測模型了。
  4. 之後我們將新資料輸入模型,就會得到 0~9 的機率,最大的機率對應的數字就是我們的預測值了。
  5. 權重(W)是唯一未知的變數,他們等於多少呢? 這就是 Neural Network 厲害的地方,它透過優化(Optimization)計算,就可以求出 W 的最佳解,構築出模型公式了。

程式很簡單,先看註解(#開頭),即可了解整個流程:

  1. 導入(import)要使用的函式庫,包括 NumPy(矩陣運算)、Keras、matplotlib(繪圖)。
  2. 從網路載入 MNIST 資料集,請 Keras 自動分為『訓練組』及『測試組』資料,MNIST 是由 AI 大師 Yann LeCun 所建立的手寫阿拉伯數字資料集(Dataset)。
  3. 建立最簡單的線性模型(Sequential),就是一層層往下執行,沒有分叉(If),也沒有迴圈(loop),這裡只設一層隱藏層(Dense)。
  4. 選擇損失函數(crossentropy)及優化方法(adam)及成效衡量方式(accuracy),就可以開始訓練。
  5. 執行模型評估,計算模型參數,即上圖的W(i,j)及W(j,k),模型就算完成了。
  6. 接著就可以使用這個模型,預測新資料了。
# 導入函式庫
import numpy as np  
from keras.models import Sequential
from keras.datasets import mnist
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.utils import np_utils  # 用來後續將 label 標籤轉為 one-hot-encoding  
from matplotlib import pyplot as plt

# 載入 MNIST 資料庫的訓練資料,並自動分為『訓練組』及『測試組』
(X_train, y_train), (X_test, y_test) = mnist.load_data()


# 建立簡單的線性執行的模型
model = Sequential()
# Add Input layer, 隱藏層(hidden layer) 有 256個輸出變數
model.add(Dense(units=256, input_dim=784, kernel_initializer='normal', activation='relu')) 
# Add output layer
model.add(Dense(units=10, kernel_initializer='normal', activation='softmax'))

# 編譯: 選擇損失函數、優化方法及成效衡量方式
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) 

# 將 training 的 label 進行 one-hot encoding,例如數字 7 經過 One-hot encoding 轉換後是 0000001000,即第7個值為 1
y_TrainOneHot = np_utils.to_categorical(y_train) 
y_TestOneHot = np_utils.to_categorical(y_test) 

# 將 training 的 input 資料轉為2維
X_train_2D = X_train.reshape(60000, 28*28).astype('float32')  
X_test_2D = X_test.reshape(10000, 28*28).astype('float32')  

x_Train_norm = X_train_2D/255
x_Test_norm = X_test_2D/255

# 進行訓練, 訓練過程會存在 train_history 變數中
train_history = model.fit(x=x_Train_norm, y=y_TrainOneHot, validation_split=0.2, epochs=10, batch_size=800, verbose=2)  

# 顯示訓練成果(分數)
scores = model.evaluate(x_Test_norm, y_TestOneHot)  
print()  
print("\t[Info] Accuracy of testing data = {:2.1f}%".format(scores[1]*100.0))  

# 預測(prediction)
X = x_Test_norm[0:10,:]
predictions = model.predict_classes(X)
# get prediction result
print(predictions)

執行方法很簡單,在DOS執行Python,接著將以上程式一段段貼上即可,我們就可以觀察每段程式的用途,要看變數內容,只要輸入變數名稱即可,全部執行完,可以看到準確率有 85%,夠神奇吧,畢竟我們只寫了10多行的程式(不含註解)。

要確認預測是否正確,可以再貼上下列程式,查看影像:

# 顯示 第一筆訓練資料的圖形,確認是否正確
plt.imshow(X_test[0])
plt.show() 

https://ithelp.ithome.com.tw/upload/images/20171203/20001976kEZTQPNLK8.png

如果我們要看優化的過程,可以輸入以下程式,結果如下圖:

plt.plot(train_history.history['loss'])  
plt.plot(train_history.history['val_loss'])  
plt.title('Train History')  
plt.ylabel('loss')  
plt.xlabel('Epoch')  
plt.legend(['loss', 'val_loss'], loc='upper left')  
plt.show() 

https://ithelp.ithome.com.tw/upload/images/20171203/20001976W0a3Pg6qsb.png
圖. 優化過程的損失函數(Loss)的變化

進行到這裡,我們已經跨出了一小步,後續我們接著抽絲剝繭,好好研究它為什麼可以這麼厲害。
相關程式請至這裡 下載,本範例為0.py。


上一篇
Day 01:以100張圖理解 Neural Network -- 觀念與實踐
下一篇
Day 03:Neural Network 的概念探討
系列文
以100張圖理解 Neural Network -- 觀念與實踐31
4
linchiupo
iT邦新手 4 級 ‧ 2017-12-12 13:00:36

很期待你這一系列的文章, 謝謝。

感恩喔,謝謝你的寒冬送暖。

0
kulius
iT邦新手 5 級 ‧ 2018-03-21 11:09:35

感謝您用簡單、程式人員看的懂的方式,指導入門!!

  1. 因為我也是programmer,喜歡自己能夠實作/應用。
  2. 太高深的統計/數學,我也不在行,還是由學校的教授搞定就夠了。

不過,最近考慮打掉重練,從基礎紮根,畢竟學了6年統計,不用可惜。
謝謝您的鼓勵。

1
黑修斯
iT邦新手 5 級 ‧ 2018-10-08 22:38:44

老師您好,我是您艾鍗生醫AI班近期的學生,上完老師的課,意猶未盡,有聽到老師說可以來挑戰IT邦鐵人賽,我也來參戰了,不過要介紹的東西是全端的框架,希望可以達到跟老師一樣的文章品質。

讚喔,加油 !!
/images/emoticon/emoticon78.gif

1
gavinsu
iT邦見習生 0 級 ‧ 2018-11-13 21:25:14

老師好...看不懂這一句"安裝目錄下同名子目錄下", 請您再說明詳細, 感恩.

2.安裝完 CUDA Toolkit 後,再下載 cuDNN v6.x ,並將壓縮檔解開,複製到 CUDA Toolkit 8.0 安裝目錄下同名子目錄下。

現在不用那麼麻煩了,只要用下列指令安裝即可。
pip uninstall tensorflow
conda install tensorflow

參考 https://towardsdatascience.com/stop-installing-tensorflow-using-pip-for-performance-sake-5854f9d9eb0c

0
wythe30910
iT邦新手 5 級 ‧ 2019-02-18 14:28:54

版主您好
我是程式語言的新手
想請教一下下則敘述的詳細操作過程

將 CUDA Toolkit 8.0 安裝目錄下bin子目錄 放到 環境變數 Path 中,在 DOS 中執行Tensorflow時,才找的到相關 Dll。

查詢過網上相關文章也不明所以
煩請版主為小弟解惑

現在不用那麼麻煩了,只要用下列指令安裝即可。
pip uninstall tensorflow
conda install tensorflow

0
smithychuang
iT邦新手 5 級 ‧ 2019-02-18 16:00:26

求助
TypeError Traceback (most recent call last)
in ()
4 model.add(Dense(units=256, input_dim=784, kernel_initializer='normal', activation='relu'))
5 # Add output layer
----> 6 model.add(Dense(units=10, input_dim=784,kernel_initializer='normal', activation='softmax'))

~\Anaconda3\lib\site-packages\keras\engine\sequential.py in add(self, layer)
179 self.inputs = network.get_source_inputs(self.outputs[0])
180 elif self.outputs:
--> 181 output_tensor = layer(self.outputs[0])
182 if isinstance(output_tensor, list):
183 raise TypeError('All layers in a Sequential model '

~\Anaconda3\lib\site-packages\keras\engine\base_layer.py in call(self, inputs, **kwargs)
455 # Actually call the layer,
456 # collecting output(s), mask(s), and shape(s).
--> 457 output = self.call(inputs, **kwargs)
458 output_mask = self.compute_mask(inputs, previous_mask)
459

~\Anaconda3\lib\site-packages\keras\layers\core.py in call(self, inputs)
881 output = K.bias_add(output, self.bias, data_format='channels_last')
882 if self.activation is not None:
--> 883 output = self.activation(output)
884 return output
885

~\Anaconda3\lib\site-packages\keras\activations.py in softmax(x, axis)
29 raise ValueError('Cannot apply softmax to a tensor that is 1D')
30 elif ndim == 2:
---> 31 return K.softmax(x)
32 elif ndim > 2:
33 e = K.exp(x - K.max(x, axis=axis, keepdims=True))

~\Anaconda3\lib\site-packages\keras\backend\tensorflow_backend.py in softmax(x, axis)
3229 A tensor.
3230 """
-> 3231 return tf.nn.softmax(x, axis=axis)
3232
3233

TypeError: softmax() got an unexpected keyword argument 'axis'

編譯: 選擇損失函數、優化方法及成效衡量方式

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

將 training 的 label 進行 one-hot encoding,例如數字 7 經過 One-hot encoding 轉換後是 0000001000,即第7個值為 1

y_TrainOneHot = np_utils.to_categorical(y_train)
y_TestOneHot = np_utils.to_categorical(y_test)

將 training 的 input 資料轉為2維

X_train_2D = X_train.reshape(60000, 2828).astype('float32')
X_test_2D = X_test.reshape(10000, 28
28).astype('float32')

x_Train_norm = X_train_2D/255
x_Test_norm = X_test_2D/255

進行訓練, 訓練過程會存在 train_history 變數中

train_history = model.fit(x=x_Train_norm, y=y_TrainOneHot, validation_split=0.2, epochs=10, batch_size=800, verbose=2)

ValueError Traceback (most recent call last)
in ()
1 # 進行訓練, 訓練過程會存在 train_history 變數中
----> 2 train_history = model.fit(x=x_Train_norm, y=y_TrainOneHot, validation_split=0.2, epochs=10, batch_size=800, verbose=2)

~\Anaconda3\lib\site-packages\keras\engine\training.py in fit(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, **kwargs)
950 sample_weight=sample_weight,
951 class_weight=class_weight,
--> 952 batch_size=batch_size)
953 # Prepare validation data.
954 do_validation = False

~\Anaconda3\lib\site-packages\keras\engine\training.py in _standardize_user_data(self, x, y, sample_weight, class_weight, check_array_lengths, batch_size)
787 feed_output_shapes,
788 check_batch_axis=False, # Don't enforce the batch size.
--> 789 exception_prefix='target')
790
791 # Generate sample-wise weight values given the sample_weight and

~\Anaconda3\lib\site-packages\keras\engine\training_utils.py in standardize_input_data(data, names, shapes, check_batch_axis, exception_prefix)
136 ': expected ' + names[i] + ' to have shape ' +
137 str(shape) + ' but got array with shape ' +
--> 138 str(data_shape))
139 return data
140

ValueError: Error when checking target: expected dense_13 to have shape (256,) but got array with shape (10,)

0
smithychuang
iT邦新手 5 級 ‧ 2019-02-18 16:02:23

求助

TypeError Traceback (most recent call last)
in ()
4 model.add(Dense(units=256, input_dim=784, kernel_initializer='normal', activation='relu'))
5 # Add output layer
----> 6 model.add(Dense(units=10, input_dim=784,kernel_initializer='normal', activation='softmax'))

~\Anaconda3\lib\site-packages\keras\engine\sequential.py in add(self, layer)
179 self.inputs = network.get_source_inputs(self.outputs[0])
180 elif self.outputs:
--> 181 output_tensor = layer(self.outputs[0])
182 if isinstance(output_tensor, list):
183 raise TypeError('All layers in a Sequential model '

~\Anaconda3\lib\site-packages\keras\engine\base_layer.py in call(self, inputs, **kwargs)
455 # Actually call the layer,
456 # collecting output(s), mask(s), and shape(s).
--> 457 output = self.call(inputs, **kwargs)
458 output_mask = self.compute_mask(inputs, previous_mask)
459

~\Anaconda3\lib\site-packages\keras\layers\core.py in call(self, inputs)
881 output = K.bias_add(output, self.bias, data_format='channels_last')
882 if self.activation is not None:
--> 883 output = self.activation(output)
884 return output
885

~\Anaconda3\lib\site-packages\keras\activations.py in softmax(x, axis)
29 raise ValueError('Cannot apply softmax to a tensor that is 1D')
30 elif ndim == 2:
---> 31 return K.softmax(x)
32 elif ndim > 2:
33 e = K.exp(x - K.max(x, axis=axis, keepdims=True))

~\Anaconda3\lib\site-packages\keras\backend\tensorflow_backend.py in softmax(x, axis)
3229 A tensor.
3230 """
-> 3231 return tf.nn.softmax(x, axis=axis)
3232
3233

TypeError: softmax() got an unexpected keyword argument 'axis'

0
smithychuang
iT邦新手 5 級 ‧ 2019-02-18 16:03:40

求助

TypeError Traceback (most recent call last)
in ()
4 model.add(Dense(units=256, input_dim=784, kernel_initializer='normal', activation='relu'))
5 # Add output layer
----> 6 model.add(Dense(units=10, kernel_initializer='normal', activation='softmax'))

~\Anaconda3\lib\site-packages\keras\engine\sequential.py in add(self, layer)
179 self.inputs = network.get_source_inputs(self.outputs[0])
180 elif self.outputs:
--> 181 output_tensor = layer(self.outputs[0])
182 if isinstance(output_tensor, list):
183 raise TypeError('All layers in a Sequential model '

~\Anaconda3\lib\site-packages\keras\engine\base_layer.py in call(self, inputs, **kwargs)
455 # Actually call the layer,
456 # collecting output(s), mask(s), and shape(s).
--> 457 output = self.call(inputs, **kwargs)
458 output_mask = self.compute_mask(inputs, previous_mask)
459

~\Anaconda3\lib\site-packages\keras\layers\core.py in call(self, inputs)
881 output = K.bias_add(output, self.bias, data_format='channels_last')
882 if self.activation is not None:
--> 883 output = self.activation(output)
884 return output
885

~\Anaconda3\lib\site-packages\keras\activations.py in softmax(x, axis)
29 raise ValueError('Cannot apply softmax to a tensor that is 1D')
30 elif ndim == 2:
---> 31 return K.softmax(x)
32 elif ndim > 2:
33 e = K.exp(x - K.max(x, axis=axis, keepdims=True))

~\Anaconda3\lib\site-packages\keras\backend\tensorflow_backend.py in softmax(x, axis)
3229 A tensor.
3230 """
-> 3231 return tf.nn.softmax(x, axis=axis)
3232
3233

TypeError: softmax() got an unexpected keyword argument 'axis'

0
smithychuang
iT邦新手 5 級 ‧ 2019-02-20 13:23:08

issue solved. thanks

0
olddunk
iT邦新手 5 級 ‧ 2019-06-05 11:14:48

版主您好:
  我是個python和keras新手,拜讀您的文章,實測了0.py程式,卻出現了下列訊息
AttributeError: module 'h5py' has no attribute 'File'
求助於版主,謝謝!

看更多先前的回應...收起先前的回應...

在我的PC上測試並無問題,可以參考下列網址測試看看:
https://blog.csdn.net/wangdong2017/article/details/82883044

olddunk iT邦新手 5 級‧ 2019-06-06 14:38:38 檢舉

留言前有試過這個方法,但是錯誤訊息仍然出現。

olddunk iT邦新手 5 級‧ 2019-06-06 14:53:12 檢舉

我把spyder關掉後,重新執行已經OK, 謝謝版主。

/images/emoticon/emoticon08.gif

我要留言

立即登入留言