python手寫數字識別(從導入數據到導出模型) 總結


python 手寫數字識別 (mnist庫)

import numpy as np
import pandas as pd
import tensorflow as tf
from tensorflow.keras import layers
import matplotlib.pyplot as plt

'''1.打開數據集文件,並且讀取mnist數據'''
data = np.load('mnist.npy')
print(data.files)
x_train = data['x_train']
y_train = data['y_train']
x_test = data['x_test']
y_test = data['y_test']
print(x_train.shape)

'''
對數據進行預處理,將圖像數據轉成四維數據,第一維度為batch_size,第二三維度為圖片大小,
第四個維度表示通道數目,mnist數據集為單色灰度圖像,所以值為1,如果是彩色則為3。
'''

'''2.數據預處理'''
x_train4D = x_train.reshape(x_train.shape[0],28,28,1).astype('float32') #shape[0]表示有多少張圖片,即batch_size
x_test4D = x_test.reshape(x_test.shape[0],28,28,1).astype('float32')
#然后對x數據標准化處理,使取值范圍在0,1之間; 對y數據進行One-Hot編碼,將0~9映射成一組相同長度的01編碼
x_train4D = x_train4D/255
x_test4D = x_test4D/255
y_train01 = tf.keras.utils.to_categorical(y_train)
y_test01 = tf.keras.utils.to_categorical(y_test)
'''3.搭建模型'''
model = tf.keras.models.Sequential() #選擇keras模型庫中的線性堆疊模型Sequential()
model.add(layers.Conv2D(filters=16, kernel_size=(5,5), padding='same', input_shape=(28,28,1),activation='relu'))
model.add(layers.MaxPooling2D(pool_size=(2,2))) #每一層卷積層后面一定要池化,不然起不到降維度的作用,無參數,這一步只是為了濃縮數據
model.add(layers.Conv2D(filters=32, kernel_size=(5,5),padding='same', activation='relu'))
#后面層其實可以根據前面層的輸出得到input_shape,所以可以不寫,但是每一個卷積層都必須寫激活函數和padding,
# 因為后面層需要知道輸出數據的規模shape是多少
model.add(layers.MaxPooling2D(pool_size=(2,2))) # 此時28x28的圖片現在只有7x7
model.add(layers.Dropout(0.3)) #使百分之30的神經元失活,避免過擬合
model.add(layers.Flatten()) #轉為一維向量輸入
model.add(layers.Dense(128,activation='relu'))
#后面再加隱藏層,128代表這一層有128個神經元,input_shape依然不用填,Dense()全連接層,只適用於一維數據
#所以前面需要加一個Flatten()層來講7x7的二維數據轉為一維數據輸入
model.add(layers.Dropout(0.5)) #卷積層和隱藏層都各需要一個dropout函數來防止過擬合。
model.add(layers.Dense(10, activation='softmax')) #輸出層,多分類問題用'softmax'激活函數,二分類問題用'sigmoid'激活函數
print(model.summary())

'''4.訓練'''
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
#交叉熵在多分類問題中常用,可以把細小的概率變化放大,而mse均方差適合擬合回歸,不適合分類問題,評估方式這里用的是准確度
train_history = model.fit(x=x_train4D,y=y_train01,validation_split=0.2,epochs=5,batch_size=300,verbose=2)
#將百分之20的數據划分為測試數據,每批處理300張圖片,verbose = 2顯示訓練過程
#訓練時候需要輸入為4維的數據,評價或者預測時候,也需要輸入時四維的數據
'''5.顯示訓練結果'''
print(train_history.history) #可知里面包含四個組數據,分別是loss, accuracy, val_loss, val_accuracy
#自定義一個顯示隨着迭代次數增加,正確率變化的圖表
def show_train_history(train_history, train, validation):
    plt.plot(train_history.history[train])  # 繪制訓練數據的執行結果
    plt.plot(train_history.history[validation])  # 繪制驗證數據的執行結果
    plt.title('Train History')  # 圖標題
    plt.xlabel('epoch')  # x軸標簽
    plt.ylabel('accuracy')  # y軸標簽
    plt.legend(['train', 'validation'], loc='upper left')  # 添加左上角圖例
show_train_history(train_history, 'accuracy', 'val_accuracy')
'''6.測試'''
#倒數第二步,在測試集上面進行測試
scores = model.evaluate(x_test4D, y_test01)
print('accuracy=', scores[1]) #scores[0] 表示loss值, scores[1]表示正確率
#用模型在測試集上進行預測
prediction = model.predict_classes(x_test4D)
#predict_classes會將預測的One-hot編碼轉化為一個數值0~9,得到的是一組預測值

#可視化結果
def plot_image_labels_prediction(images, labels, prediction, idx, nums=25):
fig = plt.figure(figsize=(12,14)) # 設置圖表大小
if nums > 25: nums = 25 # 最多顯示25張圖像
for i in range(0, nums):
ax = fig.add_subplot(5, 5, 1 + i) # 子圖生成
ax.imshow(images[idx], cmap='binary') # idx是為了方便索引所要查詢的圖像
title = 'label=' + str(labels[idx]) # 定義title方便圖像結果對應
if (len(prediction) > 0): # 如果有預測圖像,則顯示預測結果
title += ' prediction=' + str(prediction[idx])
ax.set_title(title, fontsize=10) # 設置圖像title
ax.set_xticks([]) # 無x刻度
ax.set_yticks([]) # 無y刻度
idx += 1
plt.show()
plot_image_labels_prediction(x_test,y_test,prediction,idx = 400)

下面一行表示的是測試集上的結果,可以看出每次訓練相同數據可能得到不同的參數,可能是因為dropout層的原因,

但是准確率都是98%以上,波動不會很大。我們訓練相同數據時候准確率達到95%就已經很高了,過高可能會存在過擬合問題。 

后面是對結果的評價:(最后保存模型,方便下次再使用該模型)

#查找預測錯誤點
pretb = pd.crosstab(y_test,prediction,rownames=['label'],colnames=['predict'])
df = pd.DataFrame({'label':y_test,'predict':prediction})
error = df[df['label']!=df['predict']]
print(error)

#最后一步,根據結果調整參數,或者調整網絡結構,重復以上操作直到結果滿意(前提是模型正確)

可以看到這個模型在測試集上有122個數據預測錯誤,但是准確度很高,達到98.9%,准確度能否真實的反映模型好壞嗎?

我們需要看測試集上數據的分布,因此我們有了下一步:

freq = pd.Series(y_test).value_counts()/len(y_test)
print(freq) #得到0~9在測試集上出現的頻率,看是否數據不均衡

可以看出數據是十分均衡的。

另外我們繼續統計測試集上每個數字出錯的頻率,看哪一個數字預測錯的概率最大。

from scipy.stats import pearsonr,norm
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns data
= pd.read_csv('out.csv') print(data) sns.set_context('paper') ax = sns.jointplot('label','predict',data = data,marginal_kws=dict(bins = 10, kde = True, fit = norm, fit_kws = {'color': 'r'}, rug = True),stat_func = pearsonr, space = 0, color = 'g',kind = 'scatter')
#kind = 'scatter', 'reg', 'resid', 'kde', 'hex' ax.plot_joint(sns.kdeplot, zorder = 0, n_levels = 6)#在前面一個圖的基礎上在加上核密度估計的聯合密度分布圖,通過plot_joint()實現。 plt.xticks(np.linspace(-2,12,15)) plt.yticks(np.linspace(-2,12,15)) plt.suptitle('label&predict\'s error') plt.show()

可以看出來在標簽為7,8,9的數字更容易識別錯,

錯看成0,1的概率最小,看成其他數字的概率基本一樣(見綠色kde曲線)。

我們可以后續在識別7,8,9這三個數字上對模型做進一步優化。

如何導出模型數據呢?

'''7.修改模型或者保存模型便於下次用新數據訓練'''
model.save('model.h5') #保存后會在生成一個model.h5文件
from tensorflow.keras.models import load_model
model2 = load_model('model.h5') 
#對測試數據預處理后,再用這個模型進行評價
loss,accuracy = model2.evaluate(x_test4D, y_test01)

# 保存參數,載入參數
model2.save_weights('my_model_weights.h5')
model2.load_weights('my_model_weights.h5')
# 保存網絡結構,載入網絡結構
from keras.models import model_from_json
json_string = model2.to_json()
model2 = model_from_json(json_string)
print(json_string) #json_string的值就是該網絡的結構,載入的時候只需要調用model_from_json(s) s為字符串可以通過讀json文件來得到

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM