一直想写这个方法,CNN+SVM是我一开始学习的时候想复现的,结果学了很久才达到目标。主要还是太急于求成了,而且现成的资料很难查到,等对CNN和SVM模型以及训练方法有了一定了解以后,我才真正复现。但是实际上这个方法效果一般,只是给大家提供一种思路而已。
大家都知道CNN是通过梯度下降法,不断正向加反向传播来实现参数优化的,而SVM则更像是一次性的方法,只能正向传播,并不能同样进行反向传播,极限学习ELM也是一样的。因此,二者不能同步,CNN+SVM是无法直接进行训练的,这是必须首先认识到的一点。
因此CNN+SVM的典型过程就是CNN训练、保存参数、载入参数并输出特征值、再次训练加测试这几个步骤。
这个过程比较常规,以普通1DCNN模型为例,我们首先进行训练,而后保存模型参数:
def CNN_1D():
inputs1 = Input(shape=(2048, 1))
conv1 = Conv1D(filters=16, kernel_size=6, strides=2)(inputs1)
BN1 = BatchNormalization()(conv1)
act1 = Activation('relu')(BN1)
pool1 = MaxPooling1D(pool_size=2, strides=2)(act1)
conv4 = Conv1D(filters=24, kernel_size=3, padding='same')(pool1)
BN2 = BatchNormalization()(conv4)
act2 = Activation('relu')(BN2)
pool2 = MaxPooling1D(pool_size=2, strides=2)(act2)
conv6 = Conv1D(filters=36, kernel_size=3, padding='same',
activation='relu')(pool2)
BN3 = BatchNormalization()(conv6)
act3 = Activation('relu')(BN3)
pool3 = MaxPooling1D(pool_size=2, strides=2)(act3)
flat1 = Flatten()(pool3)
dense1 = Dense(100)(flat1)
BN4 = BatchNormalization()(dense1)
# drop1 = Dropout(0.4)(BN4)
outputs = Dense(10, activation='softmax')(BN4)
model = Model(inputs=inputs1, outputs=outputs)
return model
def model_evaluate_one(base_data1, base_model, path, save_name,epochs=30,rate=0.001):
trainX, trainy, valX, valy, testX, testy = base_data1
model = base_model
model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
optimizer=tf.keras.optimizers.Adam(lr=rate),
metrics=['accuracy'])
checkpoint_save_path = path + save_name + ".h5"
lr_reducer = ReduceLROnPlateau(monitor='val_loss', factor=0.7, patience=5,
mode='min', min_lr=1e-20)
checkpointer = tf.keras.callbacks.ModelCheckpoint(
filepath=checkpoint_save_path, monitor='val_loss', verbose=0, save_best_only=True, save_weights_only=True)
model.fit(trainX, trainy, epochs=epochs, batch_size=128, verbose=0, validation_data=(valX, valy),
callbacks=[checkpointer,lr_reducer])
model.load_weights(checkpoint_save_path)
_,accuracy1 = model.evaluate(testX, testy, batch_size=128, verbose=0)
return accuracy1 * 100
这里保存模型是为了下一步载入重构
CNN+SVM其实本质上就是将CNN模型里的全连接层用SVM代替,原始CNN部分的结构起到处理数据集的作用。因此这一步我们首先载入训练好的模型,将CNN模型从flat1 = Flatten()(pool3)处截断,输入训练数据,输出特征值作为SVM的输入。
svm_model = Model(inputs=model.input, outputs=model.get_layer(index=13).output) #截取CNN的一部分
feat_train = svm_model.predict(trainX) #通过CNN的前半部分输出特征值
feat_train = feat_train.reshape(feat_train.shape[0], -1)
feat_train = case10.BN(feat_train) #将特征值0-1标准化
trainy_svm = np.argmax(trainy, axis=1) #将one-hot标签改为常规模式
新模型构建完成后还需要进行训练,使得SVM部分的参数通过正向传播过程变为最优,而后输入测试数据得出结果。
model_svm = svm.SVC(C=0.9, kernel='rbf')
model_svm.fit(feat_train, trainy_svm) #训练SVM
feat_test = svm_model.predict(testX) #通过CNN的前半部分输出特征值
feat_test = feat_test.reshape(feat_test.shape[0], -1)
feat_test = case10.BN(feat_test) #将特征值0-1标准化
testy_svm = np.argmax(testy, axis=1) #将one-hot标签改为常规模式
accuracy2 = model_svm.score(feat_test, testy_svm)
运行2次我们看看结果如何:
>第1次 cnn: 100.00 cnn+svm: 100.00 耗时72.9 sec
>第2次 cnn: 100.00 cnn+svm: 100.00 耗时73.7 sec
总用时:146.7sec
训练平均用时: 73.3sec
Accuracy1: 100.00% (+/-0.00) Accuracy2: 100.00% (+/-0.00)
结果比较好,但是看不出差距。确实单工况数据集就是这样,只有在三种工况交叉测试时才能看出差别,看过我之前文章的小伙伴们应该不难理解。
下一篇我计划把ELM的内容加入进来,然后在多工况请条件下让大家看看这种方法到底效果如何。