學習環境
電腦Windows10,環境anaconda3,開發語言Python3.7
目標
用tensorflow訓練MNIST手寫數據,並進行測試
實現方法(兩種方法)
一、用tensorflow構建一個基本的神經網絡,用於預測手寫數字。
本例爲3層神經網,包括一個輸入層,兩個隱層,一個輸出層。
輸入數據大小:28*28dpi的手寫圖片,即(28*28)的二維數組,展平後爲長度784的數組。
直接上代碼,代碼後面有解析:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets
import os
import cv2
# 利用Tensorflow2中的接口加載mnist數據集
(x_train, y_train), (x_test, y_test) = datasets.mnist.load_data()
# 對數據進行預處理
def preprocess(x, y):
x = tf.cast(x, dtype=tf.float32) / 255.
y = tf.cast(y, dtype=tf.int32)
return x,y
# 構建dataset對象,方便對數據的打亂,批處理等超操作
train_db = tf.data.Dataset.from_tensor_slices((x_train,y_train)).shuffle(1000).batch(128)
train_db = train_db.map(preprocess) # 使用map操作,對單值調用preprocess方法
test_db = tf.data.Dataset.from_tensor_slices((x_test,y_test)).batch(128)
test_db = test_db.map(preprocess) # 使用map操作,對單值調用preprocess方法
#導入一張測試圖片(28*28dpi的手寫數字)請修改自己的圖片路徑
check_x = cv2.imread("D:\\tensorflow\\mnist_0_001.png", cv2.IMREAD_GRAYSCALE)
check_y = 0
check_x,check_y = preprocess(check_x,check_y)
check_x = tf.reshape(check_x, [-1, 28*28])
# 構建模型中會用到的權重,3層有3組w和b
w1 = tf.Variable(tf.random.truncated_normal([784, 256], stddev=0.1))
b1 = tf.Variable(tf.zeros([256]))
w2 = tf.Variable(tf.random.truncated_normal([256, 128], stddev=0.1))
b2 = tf.Variable(tf.zeros([128]))
w3 = tf.Variable(tf.random.truncated_normal([128, 10], stddev=0.1))
b3 = tf.Variable(tf.zeros([10]))
# 學習率
lr = 0.01
# epoch表示整個訓練集循環的次數 這裏循環10次,次數越多學習時間越長,準確率越高
for epoch in range(10):
# step表示當前訓練到了第幾個Batch
for step, (x, y) in enumerate(train_db): #enumerate() --以索引-值對的形式迭代序列
# 把訓練集進行打平操作
x = tf.reshape(x, [-1, 28*28])
# 構建模型並計算梯度
with tf.GradientTape() as tape: # tf.Variable
# 三層非線性模型搭建
h1 = x@w1 + tf.broadcast_to(b1, [x.shape[0], 256])
h1 = tf.nn.relu(h1)
h2 = h1@w2 + b2
h2 = tf.nn.relu(h2)
out = h2@w3 + b3
# 把y轉化成one_hot編碼,當depth=10,y=3時,y變換爲[0,0,0,1,0,0,0,0,0,0]
y_onehot = tf.one_hot(y, depth=10)
# 計算MSE
loss = tf.square(y_onehot - out)
loss = tf.reduce_mean(loss)
# 計算梯度
grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3])
# w = w - lr * w_grad
# 利用上述公式進行權重的更新
w1.assign_sub(lr * grads[0]) #tf.assign_sub(ref, value, use_locking=None, name=None),變量 ref 減去 value值,即 ref = ref - value
b1.assign_sub(lr * grads[1])
w2.assign_sub(lr * grads[2])
b2.assign_sub(lr * grads[3])
w3.assign_sub(lr * grads[4])
b3.assign_sub(lr * grads[5])
# 每訓練100個Batch 打印一下當前的loss
if step % 100 == 0:
print(epoch, step, 'loss:', float(loss))
# 每訓練完一次數據集 測試一下準確率
total_correct, total_num = 0, 0
for step, (x,y) in enumerate(test_db):
x = tf.reshape(x, [-1, 28*28])
h1 = tf.nn.relu(x@w1 + b1)
h2 = tf.nn.relu(h1@w2 + b2)
out = h2@w3 +b3
# 把輸出值映射到[0~1]之間
prob = tf.nn.softmax(out, axis=1)
# 獲取概率最大值得索引位置
pred = tf.argmax(prob, axis=1) #根據axis取值的不同返回每行(0)或者每列(1)最大值的索引
pred = tf.cast(pred, dtype=tf.int32) #tf.cast()函數的作用是執行tensorflow中張量數據類型轉換
correct = tf.cast(tf.equal(pred, y), dtype=tf.int32)
correct = tf.reduce_sum(correct)
# 獲取每一個batch中的正確率和batch大小
total_correct += int(correct)
total_num += x.shape[0]
# 計算總的正確率
acc = total_correct / total_num
print("y:",y, "predict:",pred)
# predict check_x,利用獲得的權重w和偏置b對手寫數字圖片數據進行預測
h1 = tf.nn.relu(check_x@w1 + b1)
h2 = tf.nn.relu(h1@w2 + b2)
out = h2@w3 +b3
# 把輸出值映射到[0~1]之間
prob = tf.nn.softmax(out, axis=1)
# 獲取概率最大值得索引位置
pred_check = tf.argmax(prob, axis=1)
pred_check = tf.cast(pred_check, dtype=tf.int32)
print('test acc:', acc,"pred=",pred_check,"acc=",tf.equal(check_y ,pred_check))
二、直接用tensorflow的Keras創建神經網絡模型。
這裏利用tensorflow 的Keras.model 類創建模型,練習模型,評估模型,預測模型。
本例創建了2層神經網絡,其中隱層有512個神經元,輸出層爲10。模型完成學習後,將通過model.save_weights()保存訓練好的權重和偏置參數,之後就可以通過model.load_weights()導出已存的權重和偏置,然後就可以用學習好的模型進行預測了。
訓練數據的保存還有一種方法:model.save()和model.load()。具體可以在網上找到,這裏不再演示。
模型創建/訓練/評估代碼如下:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets, layers, optimizers
# step1 加載訓練集和測試集合
mnist = tf.keras.datasets.mnist
(x_train, y_train),(x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
# step2 創建模型
def create_model():
return tf.keras.models.Sequential([ #堆疊式創建層
tf.keras.layers.Flatten(input_shape=(28, 28)), #將28*28的輸入數組展平爲784的一維數據
tf.keras.layers.Dense(512, activation='relu'), #512個神經元,激活函數:relu
tf.keras.layers.Dropout(0.2), #設定20%的隨機扔掉,爲避免過擬合
tf.keras.layers.Dense(10, activation='softmax') #輸出層爲10,激活函數:softmax
])
model = create_model()
# step3 編譯模型 主要是確定優化方法,損失函數等
model.compile(optimizer='adam', #優化器:'adam',損失函數:'sparse_categorical_crossentropy'
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# step4 模型訓練 訓練1個epochs ,batch_size=256(可以爲None),可以自己修改數字,看看運行結果及耗時情況
model.fit(x=x_train,
y=y_train,
epochs=1,
batch_size=256
)
# step5 用model.evaluate進行模型測試
loss, acc = model.evaluate(x_test, y_test) #model.evaluate輸入數據(data)和金標準(label),然後將預測結果與金標準相比較,得到兩者誤差並輸出.
print("train model, accuracy:{:5.2f}%".format(100 * acc))
# step6 保存模型的權重和偏置
model.save_weights('./save_weights/my_save_weights')
# step7 刪除模型
del model
# step8 重新創建模型
model = create_model()
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# step9 恢復權重
model.load_weights('./save_weights/my_save_weights')
# step10 測試模型
loss, acc = model.evaluate(x_test, y_test)
print("Restored model, accuracy:{:5.2f}%".format(100 * acc))
以下代碼爲檢測圖片,輸入圖片文件名,輸出預測結果。注意:輸入的手寫體如果與MNIST的圖片類似識別率還是很高的,如果是自己用畫圖軟件做的手寫數字則識別成功率很低。
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets, layers, optimizers
import cv2
# step1 重新創建模型
def create_model():
return tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(512, activation='relu'),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(10, activation='softmax')
])
model = create_model()
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# step2 恢復權重
model.load_weights('./save_weights/my_save_weights')
imgDir = "D:\\tensorflow\\"
imgFile = input("請輸入待檢測的文件名:")
while imgFile != "q": #輸入q則退出程序
#導入測試圖片
check_x = cv2.imread(imgDir+imgFile, cv2.IMREAD_GRAYSCALE)
check_x = check_x / 255.0
check_x = tf.reshape(check_x, [1, 28, 28])
#用model.predict對一個手寫圖片進行預測,顯示預測結果
prob = model.predict(check_x, batch_size=None) #model.predict輸入數據(data),輸出預測結果
pred_check = tf.argmax(prob, axis=1)
pred_check = tf.cast(pred_check, dtype=tf.int32)
print(imgFile,"預測結果:",pred_check)
imgFile = input("請輸入待檢測的文件名:")