本文第一部分是對資料處理中one-hot編碼的講解,第二部分是對二分類模型的程式碼講解,其模型的建立以及訓練過程與上篇文章一樣;在最後我們將訓練好的模型儲存下來,再用自己的資料放入儲存下來的模型中進行分類(在後面的文章中會詳細討論如何使用自己的資料去訓練模型,或者讓儲存下來的模型去處理自己的資料)。第三部分是多分類模型,多分類的過程和二分類很相似,只是在程式碼中有些地方需要做出調整。
第二部分是本文的重點。
一:one-hot編碼
通過第一篇文章我們知道,對於使用keras來進行深度學習網路的搭建,因為keras中已經把深度學習的步驟都打包成函式或者檔案,我們只需要呼叫即可,所以我們需要在資料的處理(怎麼把資料處理好放入框架裡面)和引數的調整上用功夫。在本文中,我們討論一種資料處理的方法:ong-hot編碼,即根據一個矩陣生成一個只有0和1的矩陣,0表示之前的矩陣在這個索引處沒有數值,1表示之前的矩陣在這個索引處有數值。比如說有一條資料是a=[1,8,4,2],使用one-hot編碼生成一行10列的矩陣,那麼將會是[0,1,1,0,1,0,0,0,1,0],表示這個矩陣表示分別在索引1,2,3,8處有數值,而其他地方沒有數值(one-hot矩陣的列數要大於a矩陣中數值的最大數)。
下面我們在pycharm中隨機生成一個[3,10],取值在0到20之間的矩陣,然後生成[3,20]的one-hot矩陣。
1.首先生成[3,10]的矩陣a:
import numpy as np c1 = np.random.choice(20,10,replace=False) #在0到20之間我們隨機選擇10不重複的數字,replace=False 表示選擇的數字不能有重複 c2 = np.random.choice(20,10,replace=False) c3 = np.random.choice(20,10,replace=False) a = np.array([c1,c2,c3]) #把前面三個[1,10]矩陣合成[3,10]矩陣
我們使用np.random.choice(20,10,replace=False)來生成10個在0到20之間不重複的數字,replace= False表示生成的數字不能有重複。
2.然後我們根據矩陣a生成one-hot矩陣b:
def one_hot(sample,dim=20): result = np.zeros((len(sample),dim)) for i,sample in enumerate(sample): #enumerate會給i和sample自動編組 result[i,sample] = 1. #索引在哪個位置有數值,就給賦值為1 return result b = ver(a)
(1)在第一行程式碼中,我們建立一個one-hot函式。
(2)第二行程式碼我們生成一個行是資料個數,列是20的全零矩陣result(列是矩陣a中數值最大的數)。
(3)第三行程式碼中的enumerate函式表示將一個可遍歷的資料物件(如列表)組合為一個索引序列,同時列出資料和資料下標。比如說我們選擇隨機給定一個矩陣:
list_a = [[2,3,4,5],[4,6,2,4]]
然後列印矩陣list_a和使用enumerate函式後的矩陣:
print('list_a為:',list_a) print('enumerate函式生成的矩陣為:',list(enumerate(list_a)))
輸出結果如下:
list_a為: [[2, 3, 4, 5], [4, 6, 2, 4]]
enumerate函式生成的矩陣為: [(0, [2, 3, 4, 5]), (1, [4, 6, 2, 4])]
整個過程:
list_a = [[2,3,4,5],[4,6,2,4]] print('list_a為:',list_a) print('enumerate函式生成的矩陣為:',list(enumerate(list_a))) 輸出結果: list_a為: [[2, 3, 4, 5], [4, 6, 2, 4]] enumerate函式生成的矩陣為: [(0, [2, 3, 4, 5]), (1, [4, 6, 2, 4])]
第四行程式碼表示在sample有數值的索引處賦值為1.
最後返回result矩陣,並且根據資料a生成one-hot矩陣b。
(4)列印矩陣a和b,顯示結果:
print(b) 結果: a= [[ 6 15 14 9 19 13 4 1 2 3] [ 9 11 14 17 13 12 2 10 4 18] [18 2 11 8 5 14 13 12 19 16]] b= [[0. 1. 1. 1. 1. 0. 1. 0. 0. 1. 0. 0. 0. 1. 1. 1. 0. 0. 0. 1.] [0. 0. 1. 0. 1. 0. 0. 0. 0. 1. 1. 1. 1. 1. 1. 0. 0. 1. 1. 0.] [0. 0. 1. 0. 0. 1. 0. 0. 1. 0. 0. 1. 1. 1. 1. 0. 1. 0. 1. 1.]]
3.總程式碼:
import numpy as np c1 = np.random.choice(20,10,replace=False) #在0到20之間我們隨機選擇10不重複的數字,replace=False 表示選擇的數字不能有重複 c2 = np.random.choice(20,10,replace=False) c3 = np.random.choice(20,10,replace=False) a = np.array([c1,c2,c3]) #把前面三個[1,10]矩陣合成[3,10]矩陣 def ver(sample,dim=20): result = np.zeros((len(sample),dim)) for i,sample in enumerate(sample): #enumerate會給i和sample自動編組 result[i,sample] = 1. #索引在哪個位置有數值,就給賦值為1 return result b = ver(a) print('a=',a) print('b=',b)
以上就是對資料處理的one-hot方法,下面我們詳細講解keras框架下的二分類問題。
二:二分類問題
對於二分類問題,我們這裡採用的資料集是IMDB,它包含50000條嚴重兩極分化的評論,資料集分為用於訓練的25000跳評論和用於測試的25000條評論,兩者都包含50%的證明評論和50%的負面評論。
1, 對於資料的預處理
首先我們使用keras匯入imdb資料集:
from keras.datasets import imdb (train_data,train_label),(test_data,test_label)=imdb.load_data(num_words=10000)
其過程和手寫體的匯入過程一樣,不過多了一個引數的匯入:num_woeds表示只保留訓練資料集前10000個最常出現的單詞,低頻率的單詞會被捨去(比如說人名)
在train_data內的資料是1到10000的數字,表示每個數字出現的頻率;而train_label是0或1的單個數字,0表示負面評論,1表示正面評論。比如說我們列印第一個資料以及它的標籤(列印過程程式碼不算入最後的總程式碼,只是檢視資料):
print(train_data[0]) print(train_label[0]) [1, 14, 22, 16, …19, 178, 32] 1
第一個資料一共包含218個單詞,而它的標籤是1。
這裡,你很可能會有問題,為什麼要把單詞變成數字?這樣深度學習的框架是怎麼學習到評論的好壞的?我的理解是比如說給我們人類一些數字,當給你的是單數的時候就給你糖,當是偶數的時候就給你一個懲罰,次數多了之後我們就知道了單數表示這有糖,是好的。類似於這個,負面評論中可能有很多負面的單詞,因為它們是編號碼的,所以負面的單詞不太可能出現在正面評論中,又或者出現的次數很少。所以我們把單詞變成純數字,交給深度學習去處理,從而得到一個正面或者負面評價的結果(讓單純的數值賦予其不同的意義)。
言歸正傳,在匯入資料後,我們需要對資料進行處理,在把資料給到深度學習的時候,我們需要把資料變成張量,把資料向量化(簡化資料)所以我們採用one-hot編碼的方法,把train_data和test_data中的資料變成元素只有0和1的矩陣,至於train_label和test_label,因為只有單獨的一個資料,我們用numpy把它進行one-hot編碼,具體程式碼如下:
def one_hot(sample,dim=10000): result = np.zeros((len(sample),dim)) for i,sample in enumerate(sample): result[i,sample] = 1 return result train_data = one_hot(train_data) test_data = one_hot(test_data) train_label = np.asarray(train_label).astype('float32') test_label = np.asarray(test_label).astype('float32')
one-hot的方法在上面已經詳細講述過了,所以這裡不詳細講解;在這裡我們定義一個one_hot函式,然後把train_data和test_data的資料放進去處理。我們列印train_data[0]和train_lable[0]看看資料是什麼樣的:
print(train_data[0]) print(test_label[0]) [0. 1. 1. ... 0. 0. 0.] 0.0
2.搭建網路框架
在這裡我們採用三層全連線層,其中前兩層是16個隱藏層,採用的函式時relu啟用函式;最後是一層輸出一個標量,表示評論的情感,採用sigmid啟用函式。程式碼如下所示:
from keras import models from keras import layers model = models.Sequential() model.add(layers.Dense(16,activation='relu',input_shape=(10000,))) model.add(layers.Dense(16,activation='relu')) model.add(layers.Dense(1,activation='sigmoid'))
對於全連線層(Dense)來說,16個隱藏單元,表示將輸入的資料投影到16維表示空間中。Relu函式是一個非線性的啟用函式,其函式為:。假設輸入的資料是X(單列矩陣),那麼假設經過16個隱藏單元的全連線層後的輸出為Z,則可以得到:Z=Relu(dot(W,X)+b)。其中W表示權重矩陣,其形狀是(X,16),b是偏執向量,含有16個數值。當隱藏單元個數越大時,網路越能學到複雜的表示,但是其計算代價也會更高。
3.編譯
model.compile( optimizer='rmsprop', #可以匯入包,自己定義 loss='binary_crossentropy', metrics=['accuracy'] )
在編譯中,我們可以自定義損失函式loss,通過匯入keras中的優化器,對其引數進行修改,從而使模型得到更好的效果。下面是匯入損失函式的程式碼(替換上面的程式碼):
from keras import optimizers model.copile( optimizer= optimizers.RMSprop(lr=0.001), #可以匯入包,自己定義 loss='binary_crossentropy', metrics=['accuracy'] )
為了更好的理解優化器的內容,我們可以在keras的檔案裡面找到optimizers.py檔案,裡面包含著很多個類,每個類都是一種優化器的具體過程。我們找到RMSprop優化函式:
然後檢視lr,如圖所示:
4.迴圈訓練
訓練開始前,我們需要說明一點,就是在訓練階段(先不去管測試集),我們需要在訓練集中分出一部分資料來進行檢測,檢測我們的模型泛化能力,也就是檢測模型遇到新資料時的表現(有時候雖然準確率高,其實是模型把資料和對應的標籤背了下來,對新資料的處理會十分差勁)所以,在我們開始迴圈時,我們在訓練集中抽一部分做驗證集,具體程式碼如下:
train_data_val = train_data[:10000] train_data = train_data[10000:] train_label_val = trian_label[:10000] train_label = trian_label[10000:]
其中在train_data和train_label中抽出10000個資料和標籤作為驗證集(validation),剩下的作為訓練集。注:[:10000]表示取0到9999索引的資料(也就是取10000個資料,但是不要10000)
下面我們把資料丟入網路中進行訓練以及驗證:
history=model.fit(train_data,train_label,epochs=100,batch_size=512,validation_data=(train_data_val,train_lable_val ))
其中,我們把模型訓練以及驗證得到的結果賦值給history(model.fit()中得到的結果是以字典的形式返回)這樣方便我們後面畫圖檢視模型訓練以及驗證的情況;上述函式fit中的引數於上文一致,不過多了一個驗證的過程,即:validation_data=(train_data_val,train_lable_val )
下面我們引用history中的引數,畫圖檢視訓練以及驗證的情況,程式碼如下:
history_dict=history.history loss_values = history_dict['loss'] val_loss_values=history_dict['val_loss'] epochs=range(1,len(loss_values)+1) plt.plot(epochs,loss_values,'bo',label='training loss') plt.plot(epochs,val_loss_values,'b',label='validation loss') plt.title('training and validation') plt.xlabel('epochs') plt.ylabel('loss') plt.legend() plt.show()
第一行程式碼表示,我們使用history函式呼叫history中的引數賦值給history_dict,第二行第三行表示取出訓練損失值和驗證損失值(在這裡,若是不知道history中有什麼值,可以在執行時檢視頁面,history會有四個key以及他們返回的value,其中key分別是:loss,val_loss,acc,val_acc;分別表示損失值,驗證損失值,準確率,驗證準確率)
第四行表示畫圖的橫座標.
第五到十一行是畫圖,執行以上程式碼,將會得到的圖形如圖所示:
圖中訓練集的損失值是不斷下降,逼近於零,但是當模型執行驗證集的時候,也就是遇見新的資料時,在大概第4個epoch的地方,其驗證集的損失值直線上升,出現過擬合現象。對於過擬合的處理有很多方法,會在以後的文章中闡述,這裡我們讓epochs為4,總共執行4論就結束,讓其處於在驗證集最佳的位置。(這裡只畫了損失值的影像,對於準確率(acc)的影像可以自行編寫程式碼檢視)我們最後更改把histor=model.fit()這句程式碼更改成如下程式碼:
history=model.fit(train_data,train_label,epochs=4,batch_size=512,validation_data=(train_data_val,train_lable_val ))
5.測試訓練的網路模型
result = model.evaluate(test_data,test_label) print(result)
最後使用evaluate函式進行模型的最後測試,並且把數值列印出來,檢視測試的結果。結果:[0.32555921449184416, 0.8685600161552429],準確率可到達86%。
7.總程式碼:
from keras.datasets import imdb from keras import models from keras import layers (train_data,train_label),(test_data,test_label)=imdb.load_data(num_words=10000) def one_hot(sample,dim=10000): result = np.zeros((len(sample),dim)) for i,sample in enumerate(sample): result[i,sample] = 1 return result train_data = one_hot(train_data) test_data = one_hot(test_data) train_label = np.asarray(train_label).astype('float32') test_label = np.asarray(test_label).astype('float32') model = models.Sequential() model.add(layers.Dense(16,activation='relu',input_shape=(10000,))) model.add(layers.Dense(16,activation='relu')) model.add(layers.Dense(1,activation='sigmoid')) model.compile( optimizer='rmsprop', #可以匯入包,自己定義 loss='binary_crossentropy', metrics=['accuracy'] ) train_data_val = train_data[:10000] #[:10000] train_data = train_data[10000:] train_label_val = trian_label[:10000] train_label = trian_label[10000:] history=model.fit(train_data,train_label,epochs=4,batch_size=512,validation_data=(train_data_val,train_lable_val )) history_dict=history.history loss_values = history_dict['loss'] val_loss_values=history_dict['val_loss'] epochs=range(1,len(loss_values)+1) plt.plot(epochs,loss_values,'bo',label='training loss') plt.plot(epochs,val_loss_values,'b',label='validation loss') plt.title('training and validation') plt.xlabel('epochs') plt.ylabel('loss') plt.legend() plt.show() result = model.evaluate(test_data,test_label) print(result)
8.用訓練好的模型處理自己的資料
在我們使用自己的資料識別的時候,我們先把自己的模型儲存下來,在程式碼的最後我們加上下面的程式碼儲存模型:
model.save('tow_classes.h5')
(類似的,我們可以在網上下載別人訓練好的模型然後進行自己資料的識別)我們會在旁邊檔案目錄裡面看到儲存的該模型:
然後我們在儲存的模型下檔案的同目錄下,重新建立一個.py檔案,進行自己資料的處理。
首先我們使用keras匯入自己的模型檔案:
from keras import models model=models.load_model('tow_classes.h5')
然後我們隨機制造一組資料放入模型中進行預測(可以套用到之前手寫體數字識別的模板中,不過得對圖片進行處理)
隨機生成資料a(這裡只做示範,資料沒有包含任何資訊):
a0 = np.random.choice(2000,200,replace=False) a1 = np.random.choice(2000,200,replace=False) a2 = np.random.choice(2000,200,replace=False) a3 = np.random.choice(2000,200,replace=False) a4 = np.random.choice(2000,200,replace=False) a = np.array([[a1],[a2],[a3],[a4]])
隨機生成4組資料,組成a矩陣
然後把矩陣a用one-hot編碼:
def one_hot(sample,dim=10000): result=np.zeros((len(sample),dim)) for i,sample in enumerate(sample): result[i,sample] = 1 return result b=one_hot(a)
最後把b矩陣丟入模型得到一個分類的概率值以及最後分類的結果:
predict = model.predict(b)
predict_class = model.predict_classes(b)
列印結果檢視:
print(predict) print(predict_class) [[0.02564826] [0.46361473] [0.98723143] [0.07622854]] [[0] [0] [1] [0]]
模型把第一、二、四組的資料分成第一類,把第三組資料分成第二類。
總程式碼:
from keras import models import numpy as np model=models.load_model('tow_classes.h5') a0 = np.random.choice(2000,200,replace=False) a1 = np.random.choice(2000,200,replace=False) a2 = np.random.choice(2000,200,replace=False) a3 = np.random.choice(2000,200,replace=False) a4 = np.random.choice(2000,200,replace=False) a = np.array([[a1],[a2],[a3],[a4]]) def one_hot(sample,dim=10000): result=np.zeros((len(sample),dim)) for i in enumerate(sample): result[i] = 1 return result b=one_hot(a) predict = model.predict(b) predict_class = model.predict_classes(b) print(predict) print(predict_class)
三、多分類問題
對於多分類問題,其模型框架與二分類十分類似,只是有幾個個地方稍有不同,在多分類模型中,我們採用路透社資料集,它包含許多短新聞以及其對應的主題。一共包含46個不同的主題,每組至少有10組樣本(資料集詳情,參考reuters中的原始碼)。
這裡直接上總程式碼:
from keras.datasets import reuters from keras import layers from keras import models from keras.utils import to_categorical import numpy as np #1對資料的處理 (train_data,train_labels),(test_data,test_labels) = reuters.load_data(num_words=10000) def ver(sequence,dim=46): resules=np.zeros((len(sequence),dim)) for i,sequence in enumerate(sequence): #編號 屬於哪個新聞社 resules[i,sequence]=1 #有資料的地方標1 return resules x_trian=ver(train_data) x_test=ver(test_data) one_hot_trian_labels = to_categorical(train_labels) one_hot_test_labels = to_categorical(test_labels) x_val=x_trian[:1000] partial_x_train=x_trian[1000:] y_val=one_hot_trian_labels[:1000] partial_y_train=one_hot_trian_labels[1000:] #2.搭建網路框架 model=models.Sequential() model.add(layers.Dense(64,activation='relu',input_shape=(10000,))) model.add(layers.Dense(64,activation='relu')) model.add(layers.Dense(46,activation='softmax')) #3.編譯 model.compile( optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'] ) #4.迴圈訓練 history=model.fit(partial_x_train,partial_x_train,epochs=9,batch_size=512,validation_data=(x_val,y_val))
#5.測試訓練的網路模型 result = model.evaluate(x_test,one_hot_test_labels) print(result)
多分類的資料處理和二分類一樣,但是有幾點不同:1.我們把驗證集的劃分移到了前面(dim變成了46,表示有46個報社)。2.在搭建網路框架中,第一二層全連線層的隱藏元個數變成了64,最後輸出變成了46,其啟用函式變成了softmax啟用函式。(注意:以上沒有寫出畫圖的程式碼,畫圖程式碼與二分類一樣,最後得到結果是模型到了第九次左右的效果為最佳;在畫圖前,可以先讓模型執行幾十次,如,讓epochs=30,最後得到影像後,根據影像獲得合適的epochs)
在最後我們可以與二分類模型一樣,儲存訓練好的模型,自己模擬一組資料放入模型中進行分類預測(在自己模擬資料放入模型預測之前,需要檢視資料的格式,如檢視訓練集資料的第一個資料格式:print(trian_data[0].size))。(在後續的文章中,我們會從零開始製作自己的資料集,然後利用深度學習模型來進行處理)