一维卷积网络多分类
剛剛接觸到深度學(xué)習(xí),前2個月的時間里,我用一維的卷積神經(jīng)網(wǎng)絡(luò)實(shí)現(xiàn)了對于一維數(shù)據(jù)集的分類和回歸。由于在做這次課題之前,我對深度學(xué)習(xí)基本上沒有過接觸,所以期間走了很多彎路。
在剛剛收到題目的要求時,我選擇使用TensorFlow來直接編寫函數(shù),結(jié)果由于沒有什么基礎(chǔ),寫了一個周我就放棄了,改用keras來完成我的任務(wù)。
用keras來搭建神經(jīng)網(wǎng)絡(luò)其實(shí)很簡單。我把自己的網(wǎng)絡(luò)模型和數(shù)據(jù)集分享給大家,一起交流一起進(jìn)步。
我們要完成的任務(wù)是對一些給定的濕度特征數(shù)據(jù)進(jìn)行分類,多分類就是最簡單最入門的深度學(xué)習(xí)案例。
我們要完成分類任務(wù)的數(shù)據(jù)集,247*900大小的一個矩陣,其中包含900條濕度信息,每條濕度信息由246位比特構(gòu)成,最后一位表示濕度的類別,數(shù)值1-9,也就是每種濕度有100條。大家可以點(diǎn)擊下邊的鏈接自取。
數(shù)據(jù)集-用做分類.csv
或者百度網(wǎng)盤:
鏈接:https://pan.baidu.com/s/1tZlpRVBjRoGe2jHTKwRm6A
提取碼:bm94
首先是數(shù)據(jù)的導(dǎo)入:
#載入數(shù)據(jù)
df = pd.read_csv(r"數(shù)據(jù)集-用做分類.csv")
X = np.expand_dims(df.values[:, 0:246].astype(float), axis=2)
Y = df.values[:, 246]
X是900條數(shù)據(jù)的特征信息,Y表示真實(shí)類別。特別注意:這里的X在讀取的時候矩陣增加了一維。使用一維卷積神經(jīng)網(wǎng)絡(luò)Conv1D的時候必須要事先這樣對數(shù)據(jù)集進(jìn)行處理,而只使用深度網(wǎng)絡(luò)Dense時不需要額外的增加一維。
具體細(xì)節(jié)大家可以看我之前寫過的一篇博客,比較細(xì)的區(qū)分了這兩種情況:
Conv1D層與Dense層的連接
沒有接觸過的讀者可以查一下只使用深度層和使用卷積層的區(qū)別。CNN眼里只有局部小特征,而DNN只有全局大特征。
最普通的深層神經(jīng)網(wǎng)絡(luò)包含多層神經(jīng)元,從輸入信號中提取信息。每個神經(jīng)元接受來自前一層神經(jīng)元的輸入,并通過權(quán)重和非線性將它們組合起來。與普通神經(jīng)網(wǎng)絡(luò)中每個神經(jīng)元與前一層的所有神經(jīng)元連接不同,CNN中的每個神經(jīng)元只與前一層的少數(shù)神經(jīng)元局部連接。而且,CNN同一層的所有神經(jīng)元都有相同的權(quán)重。CNN層中神經(jīng)元的值通常被稱為特征映射(features maps),因?yàn)樗鼈兛梢员豢醋魇菍?yīng)于輸入的不同部分的特征。卷積神經(jīng)網(wǎng)絡(luò)可以很好地捕獲出原數(shù)據(jù)中的局部簡單特征,隨著層數(shù)的增加,產(chǎn)生的特征映射會捕獲輸入信號越來越多的全局和更復(fù)雜的屬性。它可以在一條數(shù)據(jù)中較短的片段中獲得感興趣的特征,并且該特征在該數(shù)據(jù)片段中的位置不具有高度相關(guān)性
緊接著是對濕度類別的編碼:
# 濕度分類編碼為數(shù)字
encoder = LabelEncoder()
Y_encoded = encoder.fit_transform(Y)
Y_onehot = np_utils.to_categorical(Y_encoded)
Y_onehot編碼是為了讓程序自己來區(qū)分每一條特征信息而添加的編碼。一個簡單的例子解釋一下:
這一步轉(zhuǎn)化工作我們可以利用keras中的np_utils.to_categorical函數(shù)來進(jìn)行。
“one-hot編碼。one-hot編碼的定義是用N位狀態(tài)寄存器來對N個狀態(tài)進(jìn)行編碼。比如[0,…,0],[1,…,1],[2,…,2]有3個分類值,因此N為3,對應(yīng)的one-hot編碼可以表示為100,010,001。”
我們將原始的數(shù)據(jù)集劃分為訓(xùn)練集和測試集:
# 劃分訓(xùn)練集,測試集
X_train, X_test, Y_train, Y_test = train_test_split(X, Y_onehot, test_size=0.3, random_state=0)
我這里把原始數(shù)據(jù)集以7:3的比例劃分為訓(xùn)練集和測試集。訓(xùn)練集用來逼近真實(shí)類別,測試集來測試效果。
接著的是網(wǎng)絡(luò)模型的搭建:
# 定義神經(jīng)網(wǎng)絡(luò)
def baseline_model():model = Sequential()model.add(Conv1D(16, 3, input_shape=(246, 1)))model.add(Conv1D(16, 3, activation='tanh'))model.add(MaxPooling1D(3))model.add(Conv1D(64, 3, activation='tanh'))model.add(Conv1D(64, 3, activation='tanh'))model.add(MaxPooling1D(3))model.add(Conv1D(64, 3, activation='tanh'))model.add(Conv1D(64, 3, activation='tanh'))model.add(MaxPooling1D(3))model.add(Flatten())model.add(Dense(9, activation='softmax'))plot_model(model, to_file='./model_classifier.png', show_shapes=True) # 保存網(wǎng)絡(luò)結(jié)構(gòu)為圖片,這一步可以直接去掉print(model.summary()) # 顯示網(wǎng)絡(luò)結(jié)構(gòu)model.compile(loss='categorical_crossentropy',optimizer='adam', metrics=['accuracy'])return model
這一步往往是最核心的一步,網(wǎng)絡(luò)模型通過卷積層來提取特征,在分類任務(wù)中,網(wǎng)絡(luò)的最后一層為每個類。分類算法得到的是一個決策面,通過對比濕度信息屬于每一類濕度的概率,進(jìn)而對號入座。
經(jīng)過多次調(diào)參嘗試,最后我使用7層Conv1D來提取特征值,每兩層Conv1D后添加一層MaxPooling1D來保留主要特征,減少計算量。每層卷積層使用雙曲正切函數(shù)tanh(hyperbolic tangent function)來提高神經(jīng)網(wǎng)絡(luò)對模型的表達(dá)能力。tanh經(jīng)常被運(yùn)用到多分類任務(wù)中用做激活函數(shù)。
神經(jīng)網(wǎng)絡(luò)本就具有不可解釋性,一般卷積核和全連接的結(jié)點(diǎn)數(shù)按照2的指數(shù)次冪來取。
Flatten()層作為中間層來鏈接卷積神經(jīng)網(wǎng)絡(luò)和全連接層。
神經(jīng)網(wǎng)絡(luò)被訓(xùn)練來最小化一個損失函數(shù)(Softmax loss),該函數(shù)捕捉到網(wǎng)絡(luò)的特征輸出與給定的期望輸出之間的差異。9個輸出結(jié)點(diǎn)對應(yīng)九個類別的濕度,Softmax將每個特征數(shù)據(jù)匹配到概率最大的特征類別。
交叉熵?fù)p失函數(shù)(categorical crossentropy)作為模型訓(xùn)練的損失函數(shù),它刻畫的是當(dāng)前學(xué)習(xí)到的概率分布與實(shí)際概率分布的距離,也就是損失函數(shù)越小,兩個概率分布越相似,此時損失函數(shù)接近于0。
我將網(wǎng)絡(luò)模型繪制成了AlexNet風(fēng)格的示意圖:
這里也給大家安利下這個很好用的繪制神經(jīng)網(wǎng)絡(luò)的工具:
http://alexlenail.me/NN-SVG/AlexNet.html
搭建好網(wǎng)絡(luò)之后就可以開始訓(xùn)練了。
# 訓(xùn)練分類器
estimator = KerasClassifier(build_fn=baseline_model, epochs=40, batch_size=1, verbose=1)
estimator.fit(X_train, Y_train)
batch_size設(shè)為1,然后一共訓(xùn)練40期。
這些數(shù)據(jù)大家都可以根據(jù)自己的實(shí)際情況做出調(diào)整和優(yōu)化。
到這一步已經(jīng)是搭建和訓(xùn)練的部分全部結(jié)束了。
緊接著是測試集來驗(yàn)證訓(xùn)練的準(zhǔn)確性。
為了每次測試前不重復(fù)訓(xùn)練,我們將訓(xùn)練的模型保存下來:
# 將其模型轉(zhuǎn)換為json
model_json = estimator.model.to_json()
with open(r"C:\Users\Desktop\model.json",'w')as json_file:json_file.write(model_json)# 權(quán)重不在json中,只保存網(wǎng)絡(luò)結(jié)構(gòu)
estimator.model.save_weights('model.h5')
下面是讀取模型的部分,這一部分完全可以寫到另一個函數(shù)里:
# 加載模型用做預(yù)測
json_file = open(r"C:\Users\Desktop\model.json", "r")
loaded_model_json = json_file.read()
json_file.close()
loaded_model = model_from_json(loaded_model_json)
loaded_model.load_weights("model.h5")
print("loaded model from disk")
loaded_model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
# 分類準(zhǔn)確率
print("The accuracy of the classification model:")
scores = loaded_model.evaluate(X_test, Y_test, verbose=0)
print('%s: %.2f%%' % (loaded_model.metrics_names[1], scores[1] * 100))
# 輸出預(yù)測類別
predicted = loaded_model.predict(X)
predicted_label = loaded_model.predict_classes(X)
print("predicted label:\n " + str(predicted_label))
當(dāng)然我們也希望可以直觀的顯示出模型的分類精度,和訓(xùn)練的過程。
混淆矩陣經(jīng)常用來表示分類的效果。
#顯示混淆矩陣
plot_confuse(estimator.model, X_test, Y_test)
# 混淆矩陣定義
def plot_confusion_matrix(cm, classes,title='Confusion matrix',cmap=plt.cm.jet):cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]plt.imshow(cm, interpolation='nearest', cmap=cmap)plt.rcParams['font.sans-serif'] = ['SimHei'] # 用來正常顯示中文標(biāo)簽plt.rcParams['axes.unicode_minus'] = False # 用來正常顯示負(fù)號plt.colorbar()tick_marks = np.arange(len(classes))plt.xticks(tick_marks,('0%','3%','5%','8%','10%','12%','15%','18%','20%','25%'))plt.yticks(tick_marks,('0%','3%','5%','8%','10%','12%','15%','18%','20%','25%'))thresh = cm.max() / 2.for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):plt.text(j, i, '{:.2f}'.format(cm[i, j]), horizontalalignment="center",color="white" if cm[i, j] > thresh else "black")plt.tight_layout()plt.ylabel('真實(shí)類別')plt.xlabel('預(yù)測類別')plt.savefig('test_xx.png', dpi=200, bbox_inches='tight', transparent=False)plt.show()# 顯示混淆矩陣
def plot_confuse(model, x_val, y_val):predictions = model.predict_classes(x_val)truelabel = y_val.argmax(axis=-1) # 將one-hot轉(zhuǎn)化為labelconf_mat = confusion_matrix(y_true=truelabel, y_pred=predictions)plt.figure()plot_confusion_matrix(conf_mat, range(np.max(truelabel)+1))
如果好奇的話你也可以看看每層卷積層到底學(xué)到了什么:
# 可視化卷積層
visual(estimator.model, X_train, 1)
但是我覺得看一維卷積層的特征沒啥意義,畢竟都是一些點(diǎn),看起來有點(diǎn)非人類。
# 卷積網(wǎng)絡(luò)可視化
def visual(model, data, num_layer=1):# data:圖像array數(shù)據(jù)# layer:第n層的輸出layer = keras.backend.function([model.layers[0].input], [model.layers[num_layer].output])f1 = layer([data])[0]print(f1.shape)num = f1.shape[-1]print(num)plt.figure(figsize=(8, 8))for i in range(num):plt.subplot(np.ceil(np.sqrt(num)), np.ceil(np.sqrt(num)), i+1)plt.imshow(f1[:, :, i] * 255, cmap='gray')plt.axis('off')plt.show()
最后運(yùn)行的所有結(jié)果如下:
這個結(jié)果這么好純粹是因?yàn)閿?shù)據(jù)比較好,分類的準(zhǔn)確率很大程度都是依賴你數(shù)據(jù)集本身的區(qū)分度的。
下面是數(shù)據(jù)集的特征圖,從圖上就可以看出數(shù)據(jù)的層次性很直觀。
當(dāng)然我給大家上傳的這部分?jǐn)?shù)據(jù)是我這邊結(jié)果最好的一批,其他數(shù)據(jù)集準(zhǔn)確度達(dá)不到這么高。
下面貼出整個分類過程的完整代碼:
# -*- coding: utf8 -*-
import numpy as np
import pandas as pd
import keras
from keras.models import Sequential
from keras.wrappers.scikit_learn import KerasClassifier
from keras.utils import np_utils,plot_model
from sklearn.model_selection import cross_val_score,train_test_split,KFold
from sklearn.preprocessing import LabelEncoder
from keras.layers import Dense,Dropout,Flatten,Conv1D,MaxPooling1D
from keras.models import model_from_json
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
import itertools# 載入數(shù)據(jù)
df = pd.read_csv(r"C:\Users\Desktop\數(shù)據(jù)集-用做分類.csv")
X = np.expand_dims(df.values[:, 0:246].astype(float), axis=2)
Y = df.values[:, 246]# 濕度分類編碼為數(shù)字
encoder = LabelEncoder()
Y_encoded = encoder.fit_transform(Y)
Y_onehot = np_utils.to_categorical(Y_encoded)# 劃分訓(xùn)練集,測試集
X_train, X_test, Y_train, Y_test = train_test_split(X, Y_onehot, test_size=0.3, random_state=0)# 定義神經(jīng)網(wǎng)絡(luò)
def baseline_model():model = Sequential()model.add(Conv1D(16, 3, input_shape=(246, 1)))model.add(Conv1D(16, 3, activation='tanh'))model.add(MaxPooling1D(3))model.add(Conv1D(64, 3, activation='tanh'))model.add(Conv1D(64, 3, activation='tanh'))model.add(MaxPooling1D(3))model.add(Conv1D(64, 3, activation='tanh'))model.add(Conv1D(64, 3, activation='tanh'))model.add(MaxPooling1D(3))model.add(Flatten())model.add(Dense(9, activation='softmax'))plot_model(model, to_file='./model_classifier.png', show_shapes=True)print(model.summary())model.compile(loss='categorical_crossentropy',optimizer='adam', metrics=['accuracy'])return model# 訓(xùn)練分類器
estimator = KerasClassifier(build_fn=baseline_model, epochs=40, batch_size=1, verbose=1)
estimator.fit(X_train, Y_train)# 卷積網(wǎng)絡(luò)可視化
# def visual(model, data, num_layer=1):
# # data:圖像array數(shù)據(jù)
# # layer:第n層的輸出
# layer = keras.backend.function([model.layers[0].input], [model.layers[num_layer].output])
# f1 = layer([data])[0]
# print(f1.shape)
# num = f1.shape[-1]
# print(num)
# plt.figure(figsize=(8, 8))
# for i in range(num):
# plt.subplot(np.ceil(np.sqrt(num)), np.ceil(np.sqrt(num)), i+1)
# plt.imshow(f1[:, :, i] * 255, cmap='gray')
# plt.axis('off')
# plt.show()# 混淆矩陣定義
def plot_confusion_matrix(cm, classes,title='Confusion matrix',cmap=plt.cm.jet):cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]plt.imshow(cm, interpolation='nearest', cmap=cmap)plt.rcParams['font.sans-serif'] = ['SimHei'] # 用來正常顯示中文標(biāo)簽plt.rcParams['axes.unicode_minus'] = False # 用來正常顯示負(fù)號plt.colorbar()tick_marks = np.arange(len(classes))plt.xticks(tick_marks,('0%','3%','5%','8%','10%','12%','15%','18%','20%','25%'))plt.yticks(tick_marks,('0%','3%','5%','8%','10%','12%','15%','18%','20%','25%'))thresh = cm.max() / 2.for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):plt.text(j, i, '{:.2f}'.format(cm[i, j]), horizontalalignment="center",color="white" if cm[i, j] > thresh else "black")plt.tight_layout()plt.ylabel('真實(shí)類別')plt.xlabel('預(yù)測類別')plt.savefig('test_xx.png', dpi=200, bbox_inches='tight', transparent=False)plt.show()# seed = 42
# np.random.seed(seed)
# kfold = KFold(n_splits=10, shuffle=True, random_state=seed)
# result = cross_val_score(estimator, X, Y_onehot, cv=kfold)
# print("Accuracy of cross validation, mean %.2f, std %.2f\n" % (result.mean(), result.std()))# 顯示混淆矩陣
def plot_confuse(model, x_val, y_val):predictions = model.predict_classes(x_val)truelabel = y_val.argmax(axis=-1) # 將one-hot轉(zhuǎn)化為labelconf_mat = confusion_matrix(y_true=truelabel, y_pred=predictions)plt.figure()plot_confusion_matrix(conf_mat, range(np.max(truelabel)+1))# 將其模型轉(zhuǎn)換為json
model_json = estimator.model.to_json()
with open(r"C:\Users\316CJW\Desktop\畢設(shè)代碼\model.json",'w')as json_file:json_file.write(model_json)# 權(quán)重不在json中,只保存網(wǎng)絡(luò)結(jié)構(gòu)
estimator.model.save_weights('model.h5')# 加載模型用做預(yù)測
json_file = open(r"C:\Users\Desktop\model.json", "r")
loaded_model_json = json_file.read()
json_file.close()
loaded_model = model_from_json(loaded_model_json)
loaded_model.load_weights("model.h5")
print("loaded model from disk")
loaded_model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
# 分類準(zhǔn)確率
print("The accuracy of the classification model:")
scores = loaded_model.evaluate(X_test, Y_test, verbose=0)
print('%s: %.2f%%' % (loaded_model.metrics_names[1], scores[1] * 100))
# 輸出預(yù)測類別
predicted = loaded_model.predict(X)
predicted_label = loaded_model.predict_classes(X)
print("predicted label:\n " + str(predicted_label))
#顯示混淆矩陣
plot_confuse(estimator.model, X_test, Y_test)# 可視化卷積層
# visual(estimator.model, X_train, 1)
但是,濕度值到底是連續(xù)的數(shù)值,分類任務(wù)驗(yàn)證了我們數(shù)據(jù)集特征的可分辨性。
下一篇博客中,我將對數(shù)據(jù)集稍作修改,將濕度類別改為真實(shí)濕度值。
利用卷積神經(jīng)網(wǎng)絡(luò)來提取特征,實(shí)現(xiàn)線性回歸,二者同出一脈。
【keras】一維卷積神經(jīng)網(wǎng)絡(luò)做回歸
https://blog.csdn.net/cjw838982809/article/details/106871107
總結(jié)
- 上一篇: sklearn数据处理_one_hot
- 下一篇: tensor转换 pytorch t