MLP多層感知器

      在〈MLP多層感知器〉中尚無留言

神經網路(Neural Network)

神經網路至少包含了二個層,即輸入層跟輸出層。

二層感知器

二層感知器中,一個輸入層,另一個為輸出層。比如 28*28的Mnist圖片,展開後就是784個像素。所以輸入層就有784個神經元,輸出層則有10個神經元。如下圖

多層感知器(MLP) Multilayer Perceptron

多層感知機是一種前向傳遞類神經網路,在輸入及輸出層外,中間多了至少一個隱藏層。也就是說包含了至少三層結構(輸入層、隱藏層和輸出層),並且利用到倒傳遞技術達到學習(model learning)的監督式學習。如下圖所示。

MLP預測手寫數字

底下代碼,實現手寫數字預測,比先前的模型更加準確。

不過在開始預測前,準備好的圖片最好為黑底白字,因為每個字的每個點~~都代表一個 “亮點”,黑色的神經元其值為 0,非黑色的部份其值介>0並且 <=1。然後由這些點計算出迴歸線,所以就有a及b二個參數(y=ax+b)。回歸線就是把二維的資料減少成一條線來表示。

摸型中先使用Flatten層將28*28二維陣列平化展開成一維具784個元素(特徵點)的陣列。輸出使用softmax()函數,將結果分散在0~9之間10個數字

tf.keras.losses.sparse_categorical_crossentropy稱為交叉熵,可計算其中的值離散程度。值愈大,離散愈大。是以log函數作為統計計算。

model.predict(new_img) 測出的數字為 n*10的陣列,如[0,0,1,0,0,0,0,0,0,0,0]代表為2,所以要使用np.argmax(new_pred, axis=-1)將之轉換成2

優化器選用Adam,相關資訊請參考優化器使用

y_pred=model(train_data_sigmoid),其實就是調用class Model 的 __call__()方法。那麼我們不就應該覆寫 __call__()嗎,怎麼覆寫 call()?? 因為 __call__()會先作一些事情,然後再調用 call()。所以我們只需覆寫 call() 即可。

#MLP : Multi Layer perceptron
#也可以用來偵測手寫數字
import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
import tensorflow as tf
import numpy as np
import pylab as plt
import keras#keras必需在tf之後,否則會出錯
class MLP(keras.Model):
    def __init__(self):
        super().__init__()
        self.flatten=keras.layers.Flatten()#將第一維(筆數)以外的維度展平成一維
        self.dense1 = keras.layers.Dense(units=500 , activation=tf.nn.relu)#資料由784會變成500個
        self.dense2 = keras.layers.Dense(units=400, activation=tf.nn.relu)
        self.dense3 = keras.layers.Dense(units=300, activation=tf.nn.relu)
        self.dense4 = keras.layers.Dense(units=200, activation=tf.nn.relu)
        self.dense5 = keras.layers.Dense(units=100, activation=tf.nn.relu)
        self.dense6 = keras.layers.Dense(units=10, activation=tf.nn.softmax)
    def call(self, inputs): #[60000,28,28]
        x=self.flatten(inputs) #[60000,784]
        x = self.dense1(x) #[60000,500]
        x = self.dense2(x) # [60000,400]
        x = self.dense3(x) # [60000,300]
        x = self.dense4(x) # [60000,200]
        x = self.dense5(x) # [60000,100]
        x = self.dense6(x) # [60000,10]
        return x
learning_rate=0.001
model=MLP()
mnist = tf.keras.datasets.mnist
(train_data, train_label), (test_data, test_label) = mnist.load_data()
optimizer=keras.optimizers.Adam(learning_rate=learning_rate)

#要圖形像素顏色由 0~255 歸一化成 0~1, 否則每次的損失函數值會不一樣
train_data_sigmoid = train_data.astype(np.float32) / 255.0
for i in range(200):#逼近200次, 也就是在訓練模習
    with tf.GradientTape() as f:
        y_pred=model(train_data_sigmoid)
        #crossentropy : 交叉熵
        loss=keras.losses.sparse_categorical_crossentropy(y_true=train_label, y_pred=y_pred)
        loss=tf.reduce_mean(loss)
    print(f"第{i+1:3d}次逼進: loss:{loss.numpy():.7f})
    y_grad=f.gradient(loss, model.variables)
    optimizer.apply_gradients(grads_and_vars=zip(y_grad, model.variables))

#訓練時顏色歸一化成 0~1, 偵測時也要歸一化
test_data_sigmoid=test_data.astype(np.float32)/255.0
sparse_categorical_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()#信心度測試器
#底下的 predict會列出符合每個數字的機率
predict=model.predict(test_data_sigmoid)#[[0.1,0.2,0,...0.4], [0.2,0.1,0.1,.0],....] label=np.argmax(predict, axis=-1)#轉成實際的數字 fig=plt.figure(figsize=(12,6)) for i in range(100): ax=fig.add_subplot(5,20,i+1) ax.imshow(test_data[i], cmap='gray') ax.set_title(label[i]) ax.axis("off") #底下計算精準度 # y_true : 是實際的數字[7 2 1 ...4 5 6] # y_pred : 是預測的機率值 [[0.5 0.6 0.2....], [0.3 0.4 0.1...], ......] sparse_categorical_accuracy.update_state(y_true=test_label, y_pred=predict) print(f'精準度 : {sparse_categorical_accuracy.result()*100:.2}%') plt.show() 結果 : 第198次逼進: loss 0.002727 第199次逼進: loss 0.002631 第200次逼進: loss 0.002539 313/313 [==============================] - 0s 766us/step 精準度 : 97.85%

隱藏層

隱藏層愈多愈精準,但會產生過渡擬合,所以需要加池化層。

Model 隱藏層愈多,GPU的效能就會突顯出來,所以GPU所耗資源就會變高。

相對的,如果 epoch 愈多,因為是使用 Python 的迴圈運作,那麼使用 CPU 的效能會高一點,當然CPU所耗資源也更高。

儲存模型

上面的程式碼,可以拆成下述二個部份。第一部份為儲存模型,第二部份為載入模型。

儲存模型完整代碼如下

import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
import tensorflow as tf
import numpy as np
import keras
class MLP(keras.Model):
    def __init__(self):
        super().__init__()
        self.flatten=keras.layers.Flatten()#將第一維(筆數)以外的維度展平成一維
        self.dense1 = keras.layers.Dense(units=500 , activation=tf.nn.relu)#資料由784會變成500個
        self.dense2 = keras.layers.Dense(units=400, activation=tf.nn.relu)
        self.dense3 = keras.layers.Dense(units=300, activation=tf.nn.relu)
        self.dense4 = keras.layers.Dense(units=200, activation=tf.nn.relu)
        self.dense5 = keras.layers.Dense(units=100, activation=tf.nn.relu)
        self.dense6 = keras.layers.Dense(units=10, activation=tf.nn.softmax)
    def call(self, inputs): #[60000,28,28]
        x=self.flatten(inputs) #[60000,784]
        x = self.dense1(x) #[60000,500]
        x = self.dense2(x) # [60000,400]
        x = self.dense3(x) # [60000,300]
        x = self.dense4(x) # [60000,200]
        x = self.dense5(x) # [60000,100]
        x = self.dense6(x) # [60000,10]
        return x
learning_rate=0.001
model=MLP()
mnist = tf.keras.datasets.mnist
(train_data, train_label), (test_data, test_label) = mnist.load_data()
optimizer=keras.optimizers.Adam(learning_rate=learning_rate)

#要圖形像素顏色由 0~255 歸一化成 0~1, 否則每次的損失函數值會不一樣
train_data_sigmoid = train_data.astype(np.float32) / 255.0
for i in range(200):#逼近200次, 也就是在訓練模習
    with tf.GradientTape() as f:
        y_pred=model(train_data_sigmoid)
        #crossentropy : 交叉熵
        loss=keras.losses.sparse_categorical_crossentropy(y_true=train_label, y_pred=y_pred)
        loss=tf.reduce_mean(loss)
        print("第%3d次逼進: loss %f" % (i + 1, loss.numpy()))
    y_grad=f.gradient(loss, model.variables)
    optimizer.apply_gradients(grads_and_vars=zip(y_grad, model.variables))
model.save("hand_number")
#tf.saved_model.save(model, "number")

載入模型

載入自定義模型只需使用 keras.models.load_model()即可,然後用 model.predict()即可偵測

import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
import tensorflow as tf#就算沒用,也一定要import, 防止 keras跑到上面
import numpy as np
import pylab as plt
import keras

mnist=tf.keras.datasets.mnist
(train_data, train_label), (test_data, test_label) = mnist.load_data()
model=keras.models.load_model("hand_number")
test_data_sigmoid=test_data.astype(np.float32)/255.0
sparse_categorical_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()#精準度測量器
predict=model.predict(test_data_sigmoid)
label=np.argmax(predict, axis=-1)#轉成實際的數字
fig=plt.figure(figsize=(12,6))
for i in range(100):
    ax=fig.add_subplot(5,20,i+1)
    ax.imshow(test_data[i].reshape(28,28), cmap='gray')
    ax.set_title(label[i])
    ax.axis("off")

sparse_categorical_accuracy.update_state(y_true=test_label, y_pred=predict)
print(f'精準度 : {sparse_categorical_accuracy.result()*100:.2f}%')
plt.show()

自定義層 — todo

如果要自定義層,可以繼承tf.keras.layers.Layer,然後覆寫 __init__, build及call三個方法,整個架構如下

import tensorflow as tf
class LinearLayer(tf.keras.layer.Layer):
def __init__(self, units):
super().__init__()
self.units=units
def build(self, input_shape):
self.w = self.add_variable(name='w',shape=[input_shape[-1], self.units], initializer=tf.zeros_initializer())
self.b = self.add_variable(name='b',shape=[self.units], initializer=tf.zeros_initializer())
def call(self, inputs):
y_pred = tf.matmul(inputs, self.w) + self.b
return y_pred
class LinearModel(tf.keras.Model):
def __init__(self):
super().__init__()
self.layer = LinearLayer(units=1)
def call(self, inputs):
output = self.layer(inputs)
return output

參數調整

常聽人說,模型有許多參數要調整。當我們自已建立模型,要改的東西就變的很有彈性,比如
1. 演算層,加入池化層
2. 損失函數 (Adam/SGD…)
3. learning_rate
4. epoch

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *