python 圖像分類問題 (cifar10)


python圖形分類問題(cifar10數據)

數據來源天池。

1.導入數據,查看數據

import pickle #用pickle來讀取文件 import tensorflow as tf import matplotlib.pyplot as plt import numpy as np import pandas as pd from scipy import ndimage #用ndimage來處理圖像 from tensorflow.keras import layers #layers用來定義層 #定義打開文件的函數,得到一個字典 def unpickle(file):   with open(file, 'rb') as fo:     dict = pickle.load(fo, encoding='bytes')   return dict #定義從文件讀取(不標准化x,不One-Hot編碼y)數據的函數,得到x,y def load_rawdata(file): dic = unpickle(file) data = pd.DataFrame.from_dict(dic,orient = 'index') #第一行為標簽數據,第二行為圖片數據(1000x3027) x = data.iloc[2][0].reshape(10000,32,32,3,order ='F') #數據里每張照片為一維數據需要reshape處理得到真實的照片數據 y = data.iloc[1][0] #也可以直接用字典的key讀,這里只是為了熟悉pandas操作 #得到標簽數據, (0~9),如果是文本標簽還需要轉成數字 return x, y
#定義從文件讀取並預處理數據的函數,得到一組xdata,ydata數據(均預處理過) def load_data(file): xdata,ydata
= load_rawdata(file) xdata = xdata.astype('float32')/255 #標准化 ydata = tf.keras.utils.to_categorical(label_data) #One-Hot編碼 return xdata,ydata #查看標簽列表,在batches.meta文件中 names = unpickle('batches.meta') species = names[b'label_names'] def tostr(species): for i in range(len(species)): species[i] = species[i].decode() 將每個二進制編碼的標簽轉為utf8編碼的標簽 tostr(species) print(species) #定義查看圖片的函數,最多可以查看25個 def plot_images_labels_prediction(images,labels,prediction,idx,num=10): fig=plt.gcf() fig.set_size_inches(10,10) if num>25: num=25 for i in range(num): ax = plt.subplot(5,5,i+1) img = ndimage.rotate(images[idx],-90) ax.imshow(img,cmap='binary') title= str(idx)+' '+species[labels[idx]] #顯示數字對應的類別 if len(prediction)>0: title+= '=>'+ species[prediction[idx]] #顯示數字對應的類別 ax.set_title(title,fontsize=10) ax.set_xticks([]) ax.set_yticks([]) idx+=1 plt.show()
#我們可以先試着打開第一個文件,並讀取前十張圖片並展示 images,labels = load_rawdata('data_batch_1') plot_images_labels_prediction(images,labels,[],0,10)

2.讀取全部數據(data_batch_i),加在一起作為訓練數據def get_all_data():

for i in range(5): file = 'data_batch_'+str(i+1) if i==0: x,y = load_data(file) else: t = load_data(file) x = np.concatenate((x,t[0])) y = np.concatenate((y,t[1])) return x,y
x_train,y_train = get_all_data()
print(x_train.shape,y_train.shape) #因為文件有五個data_batch所以需要加在一起形成一個大的數據集,然后在進行訓練

3.搭建模型

model = tf.keras.models.Sequential()
model.add(layers.Conv2D(filters=32,kernel_size=(3,3),padding='same',input_shape=(32,32,3),activation='relu'))
model.add(layers.Dropout(0.1))
model.add(layers.Conv2D(filters=32,kernel_size=(3,3),padding='same',activation='relu'))
model.add(layers.MaxPooling2D(pool_size=(2,2)))
model.add(layers.Conv2D(filters=64,kernel_size=(3,3),activation='relu',padding='same'))
model.add(layers.Dropout(0.3))
model.add(layers.Conv2D(filters=64,kernel_size=(3,3),padding='same',activation='relu'))
model.add(layers.MaxPooling2D(pool_size=(2,2)))
model.add(layers.Conv2D(filters=128,kernel_size=(3,3),activation='relu',padding='same'))
model.add(layers.Dropout(0.3))
model.add(layers.Conv2D(filters=128,kernel_size=(3,3),padding='same',activation='relu'))
model.add(layers.MaxPooling2D(pool_size=(2,2)))
model.add(layers.Flatten())
model.add(layers.Dropout(0.3))
model.add(layers.Dense(2000,activation='relu'))
model.add(layers.Dropout(0.3))
model.add(layers.Dense(1000,activation='relu'))
model.add(layers.Dropout(0.3))
model.add(layers.Dense(10,activation='softmax'))
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])

 模型較長,這是部分截圖,可以看出參數量特別大,所以盡量用Maxpooling減少計算量,不然后面會等好久才會出結果。

 剛剛開始訓練時候可以把參數調很多(增大filter數目,增加層數),過擬合也不要緊,主要是看訓練集上能否達到要求,如果過擬合都達不到要求,那么可能是模型選擇的問題。

3.開始訓練,得到訓練結果

model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy']) train_history=model.fit(x_train,y_train,validation_split=0.2,epochs=10,batch_size=128,verbose=1)
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') plt.ylabel(train) plt.legend(['train','validation'],loc='upper left')
  plt.xticks([x for x in range(len(history[train])+ 1)if x % 2 == 0]) # x標記step設置為2
  
show_train_history(train_history,
'accuracy','val_accuracy') show_train_history(train_history,'loss','val_loss')

從圖中可以看出訓練集上正確率上升明顯,但是測試集上正確率隨着迭代次數增加而趨於平緩,在后面訓練集上的測試結果明顯優於測試集說明了此時已經過擬合了

在迭代8次左右的時候已經接近過擬合,之后模型在測試集上訓練結果不會變好甚至可能會變差。

4.測試集上測試,對比訓練集上的結果

x_test,y_test = load_data('test_batch') print(x_test.shape,y_test.shape) loss, acc = model.evaluate(x_test, y_test,verbose=2) prediction = model.predict_classes(x_test) print(prediction.shape) #prediction是np.ndarray類型 x_testimg,y_testlabel = load_rawdata('test_batch') table = pd.crosstab(np.array(y_testlabel),prediction,rownames=['label'],colnames=['predict']) #而y_testlabel為list類型所以要強轉一下 table

 

從這個表我們可以看出來哪些label容易被認錯,在測試集上我們的正確率只有0.785,損失值為0.69,

相比訓練集,正確率差了將近百分之10。(過擬合導致的還是模型設計不足導致的呢?)

5.結果評估+進一步改進+驗證猜想

如果想知道是否是模型的原因導致正確率上不去,我們需要重新構建新的模型,然后重新訓練,浪費大量的時間和精力。

我們姑且先相信自己的模型,然后

1.方案一:迭代次數過多導致的過擬合:我們人為的進行early_stop,就是epochs設為7,8,9時候看模型在測試集上的結果對比0.785

2.方案二:數據不足導致的過擬合:我們將50000張訓練集上的圖片進行旋轉翻折操作,旋轉角度設為30,60,90,45的操作得到4*50000張新的數據,

  上下翻折,左右翻折得到2*50000張新數據,這樣我們多了6*50000張的新數據。

5.1 迭代次數過多導致的過擬合

我們現在使用early_stopping的策略來減少模型的過擬合。

# early stoppping
from tensorflow.keras.callbacks import EarlyStopping
early_stopping = EarlyStopping(monitor='val_loss', patience=5, verbose=1)
# 訓練
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
history = model.fit(x_train, y_train, epochs=50, batch_size=100, validation_split = 0.2, verbose=1, shuffle=True, callbacks=[early_stopping])
#train_history2=model.fit(x,y,validation_split=0.2,epochs=20,batch_size=1000,verbose=1)

我們使用下面增加dropout並且減少模型參數后的模型,用小數據50000個數據來訓練我們的model,得到結果:

 

 可以看出來模型在第18次迭代后停止,因為模型測得第19次的val_loss會上升從右圖可以看出來;

測試集上正確率為75.71%比之前反而少了3個百分點,說明導致過擬合的原因並不是正確的模型訓練過度,

而是因為模型本身參數錯誤(導致很容易在沒有達到最優點前就過擬合)(模型誤差)以及數據量過小(系統誤差,可以人為增強數據來解決)的問題;

下面一個方案可以較好地解決這一問題。

 5.2 數據不足導致的過擬合

model = tf.keras.models.Sequential()
model.add(layers.Conv2D(filters=32,kernel_size=(3,3),padding='same',input_shape=(32,32,3),activation='relu'))
model.add(layers.Dropout(0.4))
model.add(layers.Conv2D(filters=32,kernel_size=(3,3),padding='same',activation='relu'))
model.add(layers.MaxPooling2D(pool_size=(2,2)))
model.add(layers.Conv2D(filters=64,kernel_size=(3,3),activation='relu',padding='same'))
model.add(layers.Dropout(0.3))
model.add(layers.Conv2D(filters=64,kernel_size=(3,3),padding='same',activation='relu'))
model.add(layers.MaxPooling2D(pool_size=(2,2)))
model.add(layers.Conv2D(filters=64,kernel_size=(3,3),activation='relu',padding='same'))
model.add(layers.Dropout(0.3))
model.add(layers.Conv2D(filters=64,kernel_size=(3,3),padding='same',activation='relu'))
model.add(layers.MaxPooling2D(pool_size=(2,2)))
model.add(layers.Flatten())
model.add(layers.Dropout(0.3))
model.add(layers.Dense(2000,activation='relu'))
model.add(layers.Dropout(0.3))
model.add(layers.Dense(1000,activation='relu'))
model.add(layers.Dropout(0.3))
model.add(layers.Dense(10,activation='softmax'))

因為前面分析我們知道了先前的模型存在過擬合的問題,所以我們增加了Dropout的值,並且減少了第三層的filter數目,從128減小至64,減少模型參數也有利於防止過擬合。

#對圖片進行旋轉操作,得到新圖片加在原來圖片數據集中得到更大的訓練集(之后人為還需要做個預處理) def rotate(images,angle): temp = images.copy() for i in range(len(images)): temp[i] = ndimage.rotate(temp[i],angle,reshape = False,mode = 'nearest') return temp def rotall(xdata,ydata,angles): tempx = xdata.copy() tempy = ydata.copy() for i in range(len(angles)): tempx = np.concatenate((tempx,rotate(xdata,angles[i])),axis = 0) tempy.extend(ydata) return tempx,tempy def flipall(xdata,ydata):#返回原來數據上下,左右翻折后的數據,之后與前面得到的旋轉數據集合並 tempx = np.concatenate((xdata,xdata),axis = 0) tempy = ydata+ydata l = len(xdata) for i in range(l): tempx[i] = np.flip(xdata[i],axis = 0) tempx[i+l] = np.flip(xdata[i],axis = 1) return tempx,tempy

#定義get_all_rawdata函數獲得所有初始圖片信息
def get_all_rawdata():
   for i in range(5):
      file = 'data_batch_'+str(i+1)
      if i==0:
        x,y = load_rawdata(file)
 else:
        t = load_rawdata(file)
x = np.concatenate((x,t[0]))
         y.extend(t[1])
    return x,y

xdata,ydata = get_all_rawdata()
xdata_new, ydata_new = rotall(xdata,ydata,[30,60,90,45])
tx,ty = flipall(xdata,ydata)
xdata_new = np.concatenate((xdata_new,tx))
ydata_new.extend(ty)

#保存新的數據,因為數據量很大,丟了又得弄好久。

output = open('new_data','wb')
pickle.dump((xdata_new,ydata_new),output)
output.close()

#合並后還需要shuffle一下才能保證新加入的數據與原來數據混合均勻,進而減小混合不均勻帶來的誤差

 r = np.arange(len(ydata_new))
 np.random.shuffle(r)
x = xdata_new[r,:]
y = np.array(ydata_new)[r]
x = x.astype('float32')/255 #標准化
y = tf.keras.utils.to_categorical(y)
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy']) train_history2=model.fit(x,y,validation_split=0.2,epochs=50,batch_size=1000,verbose=1)

 

很明顯現在的測試數據上的正確率從0.79提高到了0.85,增強數據的方法對數據過少而導致的過擬合很管用。

並且從圖表可以看出依然沒有到達過擬合點,還可以繼續訓練,此時測試集上的正確率有0.8015. 

因此我在這個基礎上對模型再繼續訓練20次后觀察結果:

可以很明顯的看出過擬合了,測試數據上的正確率和loss均在上下波動,不平滑,但是其中也會有比較好的點,

例如最好的一次正確率達到了86.28%比之前的85.14%高了許多,

最后我們還得測試一下這個模型現在在測試集上的結果,得到:80.25%的正確率,比之前多了0.1個百分點。

可見對數據進行預處理並且增強數據可以達到事半功倍的效果。

 

機器學習問題可以大致分為回歸問題(預測一個值),分類問題,聚類問題,決策問題,數據分析相關問題。

機器學習的是規律、關系,學不出方法的,學習的方法來源於數學,可以說上述問題都可以轉化為數學建模問題。

未來更新

隱馬爾可夫模型(Hidden Markov Model, HMM)、馬爾可夫隨機場(Markov Random Field, MRF)和馬爾可夫決策(Markov decision process, MDP)等馬氏理論相關的總結。

 

 

 


免責聲明!

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



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