系列文章目录
#Python&Keras 1.如何从无到有在自己的数据集上实现深度学习模型(入门)前言
由于本文主要介绍的是深度学习模型(简称DL模型)的入门,模型非常简单。由于涉及到理解DL模型的输入,因此将会涉及到机器学习的输入等概念,若对机器学习的基础概念熟悉并代码实践过的朋友可以快速略过。Tensorflow的快速安装可点击链接跳转,需要预装anaconda和联网。
为了避免混乱,我们对下列几个词语进行区分:
- 总体:根据研究目的确定的所有同质观察单位的全体;
- 观察变量:每个观察单位的研究变量,即有自变量又有因变量;
- 样本:从研究“总体”中抽取部分有代表性的观察单位,对“变量”进行观测构成一个样本;
- 数据:在本文指数据集。
一、机器学习的部分概念
首先我们知道,机器学习是对能通过经验自动改进的计算机算法的研究。简单来说,就是我们用很多样本来训练模型,让模型达到可以识别样本的效果。
例如对于UCI的公共CTG数据集,如果我们要简单的训练一个GBDT对数据进行分类,首先最重要的一点就是要确定模型的输入到底是什么。
在公共CTG数据集的定义中,LB, AC, FM, UC, DL, DS, DP, ASTV, MSTV, ALTV, MLTV, Width, Min, Max,Nmax, Nzeros, Mode, Mean, Median, Variance, Tendency这21个是CTG变量,而NSP为标签(根据胎儿监护指南评价胎儿状态NSP: Normal, Suspicious, Pathological),即一个样本=21个CTG变量值+对应的NSP标签
。
在训练GBDT分类器时,我们使用特征作为模型的输入,使用标签来验证算法预测的结果是否准确,这就是最简单的分类算法。
二、简单实现深度学习算法
作为19年末接触深度学习的小白,当时我在理解深度学习算法的输入上花费了巨量的时间,那时候尝试使用信号数据进行建模,我是完全无法想象,这玩意,怎么放进模型里面训练???
现在再看这个问题真的感觉挺傻的,但那时候我甚至试过将信号转换成类似上面的图片,然后按照手写mnist数据集跑…
1.理解深度学习模型输入
深度学习跟机器学习还是有很多相似的地方的,这也是我为什么要在讲深度学习模型实现前插一嘴机器学习的一些概念。对于深度学习来说,模型的输入可以有很多形式,特征数据、信号、文本、图片、音频、视频等等都可以是模型输入,那这些数据都是怎么输入至模型当中呢?
有一点关键我们要明确,计算机只能处理数字!所以我们需要把模型输入从各种各样的形式转化成数字。 因为特征数据是可以直接输入至模型中,就不重复讲了,这里主要简单介绍一下信号、文本、图片的转化。
在介绍转换之前有一点需要注意,由于深度学习模型一般都比较大,我们不能像训练机器学习模型一样把数据一股脑都输入到模型中。因此这里需要对一些基本定义进行简单介绍:
- epoch: 训练轮次。由于模型的参数很多,如果只进行一轮训练是远远不够的,不足以让模型拟合数据,因此我们需要设置合适的轮次让模型对数据进行不断训练拟合,提高模型的效果;
- batch: 样本批次,简单理解为多个样本的集合。因为DL模型可以处理的数据形式多种多样,如果一次性把数据都输入至模型中那电脑的内存会吃不消,因此我们通常将数据分批输入,让模型拟合这批样本后再拟合下一批样本,直至所有样本。
- batch_size: 样本批次大小,即每次输入模型的样本量。一般样本批次大小越大,模型一次拟合的时间越长,所消耗的内存也越大。
(1)信号
信号数据(siganl data)是在不同时间上收集到的数据,用于所描述现象随时间变化的情况。这类数据反映了某一事物、现象等随时间的变化状态或程度。也就是说,信号数据的长度可长可短,主要取决于任务类型。
在理解了机器学习算法输入的情况下,可以简单把一个信号理解为一个很多很多特征的样本,例如对于这个信号来说,由于它都是数字构成,所以只需把它对应的标签加上,就构成了一个有标签的样本。所以对于信号数据建模,DL模型的输入即为一整串序列。
因此对于信号数据来说,模型的输入为(batch_size, 信号长度, 信号维度)
,而信号长度和信号维度的值,这个就看任务的需要了~ 如果说信号维度为1(即像上述的信号一样),那么模型的输入也可以简化为(batch_size, 信号长度)
,具体看在输入层之后的网络层要求而定~
(2)文本
文本也是序列数据的一种,是具有上下文关系的序列,而它一般又不是数字构成,因此在处理文本数据时我们需要现将文本转换为数字。
在NLP中,我们用词表来实现文本转换为数字。例如
user_dict = ['我', '爱', '中', '华', '!'] # 词表,每个文字对应一个数,索引0不会被分配
text = "我爱中国!" # 输入文本
text2num = [1, 2, 3, None, 5] # 按照词表转换文字,未在词表中的为0
在经过类似上述的转换就可以将一个文本样本转换为一个序列样本,就可以直接将它作为DL模型的输入了。模型输入为(batch_size, 文本长度)
,由于文本长度不定,一般都要进行序列扩增(即在文本后添加0)以保证输入长度一致。
(3)图片
因为图片在计算机中的存储形式也是数字,因此我们使用Python读取图片的方式就可以获取图片的信息(即一个长×宽×通道数的矩阵),按照需求将矩阵resize到相同维度即可输入模型。
image = cv2.imread(path + imgInfo['file_name']) # 使用OpenCV读取图片(其他方式也可以)
# print(imgInfo['file_name'])
image = cv2.resize(image, (IMAGE_SIZE, IMAGE_SIZE)) # 裁剪图片至(IMAGE_SIZE, IMAGE_SIZE)大小
经过上述转换,模型的输入为(batch_size, image_size1, image_size2, channel)
,channel通道数具体以图片输入为主。
2.读取数据
理解了深度学习模型输入之后我们就可以来构建模型的输入数据。以长度为32、维度为1的信号数据为例,如果将信号储存在*_signal.csv,对应标签存在*_label.csv,划分训练集与测试集后即:
- 训练集—信号:train_signal.csv,shape=(train_num, 32)
- 训练集—标签:train_label.csv,shape=(train_num, 1)
- 测试集—信号:test_signal.csv,shape=(test_num, 32)
- 测试集—标签:test_label.csv,shape=(test_num, 1)
在训练集与测试集中,信号与标签需一一对应,即*_series.csv和*_label.csv的同一行表示一个样本。如果满足上述要求,那么读取数据的代码如下:
import pandas as pd
train_x = pd.read_csv('train_signal.csv') # print(train_x.shape) >> (train_num, 32)
train_y = pd.read_csv('train_label.csv') # print(train_y.shape) >> (train_num, 1)
test_x = pd.read_csv('test_signal.csv')
test_y = pd.read_csv('test_label.csv')
# valid_x = pd.read_csv('valid_signal.csv')
# valid_y = pd.read_csv('valid_label.csv')
上面的train_*即为训练集,test_*为测试集。无论何种数据,只需要把原数据处理成这种形式即可输入模型训练。此外,如果实验更严谨且数据量足够的话,也应该从训练集中划分部分作为验证集并额外保存为csv文件。
3.搭建模型层
在确定好模型输入之后,便是选择合适的网络层来构建DL模型。这里使用Keras对信号数据进行模型搭建,适合刚入门时的理解。对信号数据我们一般使用循环神经网络或卷积神经网络,类似LSTM、GRU、一维卷积等。本文旨在帮助理解如果使用Keras的思路建模,所以这里只用最简单的前馈神经网络作为例子。
Keras有一个好处就是你只需要定义输入的维度,接下来的层会自动计算维度,但要注意每一种网络层对数据维度的影响,如Embedding层会使得输入数据增加一个维度、LSTM层、GRU层在默认情况下会使三维数据减少一维等等。
在Keras中有两种写法,一种是比较适合新手的Sequential顺序模型,搭建模型的方法很简单;另一种是Model的函数式API写法,可以写很复杂的网络,推荐熟练后使用。为了方便讲解,这里假定信号的长度为32,维度为1,一共有10类。
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import Dense, Activation
import tensorflow.keras.layers as layers
# Sequential写法(初级),不需要写Input layer,但是只能有一个输入
model = Sequential() # 声明model为顺序模型
model.add(Dense(32, input_dim=784))
model.add(Activation('relu'))
model.add(Dense(10))
model.add(Activation('softmax'))
# Model写法(高级),需要写Input layer,可以有多个输入
inputs = layers.Input(shape=(32))
fc1 = Dense(32)(inputs)
ac1 = Activation('relu')(fc1)
fc2 = Dense(10)(ac1)
output = Activation('softmax')(fc2)
model = Model(inputs=inputs, outputs=output)
这样我们就实现了一个两层的全连接网络,即最简单的前馈神经网络,我们可以用代码model.summary()
把模型结构打印出来:
>>> model.summary()
# sequential写法的模型summary
"""
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense (Dense) (None, 32) 1056
_________________________________________________________________
activation (Activation) (None, 32) 0
_________________________________________________________________
dense_1 (Dense) (None, 10) 330
_________________________________________________________________
activation_1 (Activation) (None, 10) 0
=================================================================
Total params: 1,386
Trainable params: 1,386
Non-trainable params: 0
_________________________________________________________________
"""
# model的API写法
"""
Model: "model"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_1 (InputLayer) [(None, 32)] 0
_________________________________________________________________
dense (Dense) (None, 32) 1056
_________________________________________________________________
activation (Activation) (None, 32) 0
_________________________________________________________________
dense_1 (Dense) (None, 10) 330
_________________________________________________________________
activation_1 (Activation) (None, 10) 0
=================================================================
Total params: 1,386
Trainable params: 1,386
Non-trainable params: 0
_________________________________________________________________
"""
这里用PPT对模型进行可视化:
接下来我们结合这个可视化图像理解一下代码的含义,这里分为两种写法分开描述:
-
Sequential模式:
①首先由于还未开始训练,将batch_size的值设为None。且因为Dense层不会改变数据的维度,为了最后每个unit可以输出对应类别的概率,所以确定模型的输入为(batch_size=None, input_dim=32)
;
②因为Sequential模式下可以将输入数据的维度写在第一个网络层中,所以省略不需要写输入层;
③在第一层Dense(32, input_dim=32)
中,第一个units=32表示这一网络层有32个神经元节点(units通常省略),input_dim即为输入数据的维度,每次输入的序列长度为32,所以第一层的输出为(batch_size=None, units=32)
;
④第二层model.add(Activation('relu'))
为激活函数层,也就是对上一层Dense的输出做relu变换,由于relu变换不会改变输入的维度,因此第二层的输出仍然为(batch_size=None, units=32)
;
⑤第三层model.add(Dense(10))
与第一层相同,但由于样本的类别数为10且该层是模型的最后一个可以对数据维度进行变换的网络层,因此需要将该层的units设置为10来分别对应10个分类
,所以该层的输出变为(batch_size=None, units=10)
,这样每个unit都对应每一类;
⑥最后一层model.add(Activation('softmax'))
为激活函数层,对上一层做softmax变换,将上一层的每一个unit的结果转化为概率的形式,且10个unit的值总和为1。 -
Model模式:
①同样,确定模型的输入为(batch_size=None, input_dim=32)
;
②因为Model模式可能存在多个输入,因此需要定义每一个输入,这里只有一个输入,即为inputs = layers.Input(shape=(32))
;
③之后我们定义一个Dense(32)层,并将inputs输入至该层中,输出结果fc1,用代码表示即为fc1 = Dense(32)(inputs)
;
④接下来将上一层结果fc1输入至激活层中进行relu变换,得到结果ac1 = Activation('relu')(fc1)
;
⑤根据样本的类别定义Dense(10)层,将ac1输入至该层中,得到fc2 = Dense(10)(ac1)
;
⑥对fc2进行softmax变换,则模型输出output = Activation('softmax')(fc2)
;
⑦最终确定输入与输出,得到最终模型model = Model(inputs=inputs, outputs=output)
。
从model.summary()中输出的模型结构可以看出,Activation层的参数为0,即该层不涉及参数训练,因此上面的代码可以简化为:
# Sequential写法
model = Sequential() # 声明model为顺序模型
model.add(Dense(32, input_dim=784, activation='relu'))
model.add(Dense(10, activation='softmax'))
"""
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense (Dense) (None, 32) 1056
_________________________________________________________________
dense_1 (Dense) (None, 10) 330
=================================================================
Total params: 1,386
Trainable params: 1,386
Non-trainable params: 0
_________________________________________________________________
"""
# Model写法
inputs = layer.Input(shape=(32))
fc = Dense(32, activation='relu')(inputs)
output = Dense(10, activation='softmax')(fc)
model = Model(inputs=inputs, outputs=output)
"""
Model: "model"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_1 (InputLayer) [(None, 32)] 0
_________________________________________________________________
dense (Dense) (None, 32) 1056
_________________________________________________________________
dense_1 (Dense) (None, 10) 330
=================================================================
Total params: 1,386
Trainable params: 1,386
Non-trainable params: 0
_________________________________________________________________
"""
经过上述的步骤,我们就构建了一个完整模型,之后就可以进行最后一步操作了~
4.训练模型
在训练模型之前,我们需要对模型配置一些学习过程,如优化器Optimizer,损失函数Loss Function,评估标准Metrics,这三个都是训练模型前必备的配置。
- 优化器:优化器是一种模型在反向传播过程中引导损失函数往正确的方向更新合适的大小,使得模型的损失趋向于全局最小,像梯度下降法就是所有优化器的核心思想体现;学习率也是在这里设置的~
- 损失函数:损失是模型关于单个样本的预测值与真实值之间的差距,而用来计算这种差距的函数就称为损失函数,常使用的有交叉熵损失函数Cross-Entropy Loss等;
- 评估标准:用来评估训练模型当前的性能,通常有准确率Accuracy、平均绝对误差MAE等。
这三个配置很简单,在Keras中用model.compile()来接收这三个配置,如对于分类问题而言:
# 多分类问题
model.compile(optimizer='rmsprop', # 使用RMSprop优化器
loss='categorical_crossentropy', # 多分类用categorical
metrics=['accuracy'])
# 二分类问题
model.compile(optimizer='rmsprop',
loss='binary_crossentropy', # 二分类用binary
metrics=['accuracy'])
# 设置学习率
opt = tensorflow.keras.optimizers.RMSprop(learning_rate=0.001, rho=0.9) # 不同优化器参数不同!
model.compile(optimizer=opt,
loss='categorical_crossentropy', # 多分类用categorical
metrics=['accuracy'])
关于优化器RMSprop的原理可以点击传送阅读论文或直接百度~(现在常使用的Optimizer是Adam)
因为我们的标签的维度为(train_num, 1),而模型的最后一层Dense(10)要求我们的标签维度为10,所以需要对标签进行one-hot处理。
from tensorflow.python.keras.utils.np_utils import to_categorical
train_onehot = to_categorical(train_label, num_classes=10)
valid_onehot = to_categorical(valid_label, num_classes=10)
test_onehot = to_categorical(test_label, num_classes=10)
现在我们有输入数据,有模型,有相关配置,接下来就可以进行训练啦。Keras的模型训练最简单的实现就是:
# 最简单
model.fit(train_x, train_onehot, epochs=10, batch_size=32)
# 如果有验证集的话
model.fit(
train_x, train_onehot,
epochs=10, batch_size=32,
validation_data=(valid_x, valid_onehot)
)
# 如果没有验证集也想验证的话,可以每次训练都在训练集中随机抽取部分出来作为验证集
# 不过还是推荐将验证集划分出来并单独保存
model.fit(
train_x, train_onehot,
epochs=10, batch_size=32,
validation_split=0.2, # 0.2表示从训练集中抽取的比例
)
""" 运行model.fit之后输出:
Epoch 1/10
20/20 [==============================] - 1s 14ms/step - loss: 11.7453 - accuracy: 0.0516 - val_loss: 11.9041 - val_accuracy: 0.0500
Epoch 2/10
20/20 [==============================] - 0s 5ms/step - loss: 11.8871 - accuracy: 0.0406 - val_loss: 12.1116 - val_accuracy: 0.0562
Epoch 3/10
20/20 [==============================] - 0s 5ms/step - loss: 12.0812 - accuracy: 0.0484 - val_loss: 12.3094 - val_accuracy: 0.0500
Epoch 4/10
20/20 [==============================] - 0s 6ms/step - loss: 12.2507 - accuracy: 0.0781 - val_loss: 12.4134 - val_accuracy: 0.0500
Epoch 5/10
20/20 [==============================] - 0s 7ms/step - loss: 12.3120 - accuracy: 0.0797 - val_loss: 12.4559 - val_accuracy: 0.0562
Epoch 6/10
20/20 [==============================] - 0s 5ms/step - loss: 12.3592 - accuracy: 0.0781 - val_loss: 12.5304 - val_accuracy: 0.0375
Epoch 7/10
20/20 [==============================] - 0s 5ms/step - loss: 12.5126 - accuracy: 0.0672 - val_loss: 12.7373 - val_accuracy: 0.0688
Epoch 8/10
20/20 [==============================] - 0s 5ms/step - loss: 12.8091 - accuracy: 0.0750 - val_loss: 13.1038 - val_accuracy: 0.0562
Epoch 9/10
20/20 [==============================] - 0s 5ms/step - loss: 13.2311 - accuracy: 0.0609 - val_loss: 13.5639 - val_accuracy: 0.0375
Epoch 10/10
20/20 [==============================] - 0s 5ms/step - loss: 13.7944 - accuracy: 0.0437 - val_loss: 14.0609 - val_accuracy: 0.0562
"""
如果对模型还有额外的需求,如想画模型指标的收敛曲线(分析模型的拟合效果)、想在每次模型训练完一个epoch就保存模型的权重(以用于之后复用)、当损失不再下降时提前终止训练等等,Keras对这些方法称之为回调Callbacks(更多方法详见链接),后面两个方法的使用如下:
import tensorflow.keras.callbacks as cb
callbacks = [
# 以验证集损失val_loss为准,只保存最佳模型,只保存模型权重不保存整个模型,verbose表示是否显示详细信息
cb.ModelCheckpoint('model.h5', monitor='val_loss', verbose=0,
save_best_only=True, save_weights_only=True,
mode='auto', period=1)
# 当经过patient个epoch,monitor的变化量绝对值小于min_delta时,模型提前终止训练
cb.EarlyStopping(monitor='val_loss', min_delta=0, patience=0, verbose=0,
mode='auto', baseline=None, restore_best_weights=False)
]
model.fit(
train_x, train_onehot,
epochs=10, batch_size=32,
validation_data=(valid_x, valid_onehot),
callbacks=callbacks
)
如果我们想记录模型训练过程中指标如loss、accuracy、val_loss和val_accuracy的变化呢?只需要:
# 模型训练时每轮的各项指标都会被存在history中,方便我们进行可视化
history = model.fit(
train_x, train_onehot,
epochs=10, batch_size=32,
validation_data=(valid_x, valid_onehot),
callbacks=callbacks
)
# history可视化
import matplotlib.pyplot as plt
plt.figure(figsize=(8, 6))
plt.plot(history.history['loss'], label='loss')
plt.plot(history.history['accuracy'], label='accuracy')
plt.plot(history.history['val_loss'], label='val_loss')
plt.plot(history.history['val_accuracy'], label='val_accuracy')
plt.legend()
plt.tight_layout()
plt.savefig('test.png', dpi=96)
plt.show()
history的可视化结果如下图所示:
当然因为这里的数据都是随机生成的,训练轮次和模型复杂度都比较小,所以效果也不咋地hhh
5.评估模型
模型训练完之后就要用测试集来评估一下模型的效果了!在Keras中我们用model.predict()
来获得模型的分类结果,因此:
import numpy as np
from sklearn.metrics import confusion_matrix, classification_report, auc
pre_label = model.predict(test_data)
print("pre_label\n", pre_label)
print("\nnp.argmax(pre_label, axis=1)\n", np.argmax(pre_label, axis=1))
print("\nnp.argmax(test_onehot, axis=1)\n", np.argmax(test_onehot, axis=1))
cm = confusion_matrix(np.argmax(test_onehot, axis=1), np.argmax(pre_label, axis=1))
print("\nconfusion matrix:\n", cm)
cr = classification_report(np.argmax(test_onehot, axis=1),np.argmax(pre_label, axis=1))
print("\nclassification report:\n", cr)
"""运行结果:
pre_label
[[0.07808532 0.08875899 0.12683888 ... 0.06995315 0.1505746 0.10445292]
[0.09848601 0.16047691 0.10339221 ... 0.11639596 0.13490641 0.0777345 ]
[0.0959314 0.18760066 0.07602581 ... 0.08224294 0.11492887 0.06309237]
...
[0.10322383 0.09843607 0.15411188 ... 0.16923514 0.09895203 0.06014431]
[0.14760548 0.07783186 0.0720889 ... 0.15980284 0.09465237 0.09731379]
[0.10474698 0.14553103 0.10743896 ... 0.07129949 0.14972432 0.06780767]]
np.argmax(pre_label, axis=1)
[8 1 1 7 2 2 8 0 5 7 8 7 7 7 5 9 8 2 7 2 2 8 1 7 2 7 8 7 2 7 0 7 4 1 2 0 2
6 7 2 4 7 7 2 6 1 0 8 8 8 2 4 5 7 7 7 2 6 2 1 5 2 7 7 2 2 8 7 6 2 5 2 6 7
2 2 2 7 7 1 7 2 7 6 1 7 4 7 6 6 6 4 7 7 5 7 7 7 7 6 6 2 1 7 7 2 1 4 2 7 6
5 7 1 8 7 7 4 7 7 2 6 7 2 2 2 0 2 2 6 1 5 2 1 1 1 2 1 2 7 2 6 2 2 0 7 2 8
2 7 2 7 8 7 6 7 7 7 7 4 7 7 2 2 7 6 2 5 1 2 8 7 7 2 4 7 2 2 2 7 6 5 1 2 2
8 4 2 7 7 2 6 2 0 2 2 5 7 7 8]
np.argmax(test_onehot, axis=1)
[4 0 8 2 8 4 2 8 5 4 4 5 7 6 5 0 8 3 0 1 7 9 4 9 4 7 2 8 0 1 2 4 8 0 7 8 5
0 5 9 3 4 0 4 7 8 9 6 0 5 6 2 6 8 4 9 2 4 1 9 6 8 9 3 6 0 9 0 0 2 3 6 1 5
9 3 6 6 7 6 8 8 9 9 1 8 1 5 0 3 0 8 9 7 5 3 9 8 6 1 5 3 7 4 4 3 3 4 5 1 5
7 4 1 4 7 9 4 1 9 2 2 4 0 7 6 4 9 4 8 8 0 1 5 1 5 8 6 3 2 6 7 8 1 3 7 1 5
2 4 9 4 0 2 3 1 2 7 3 9 9 9 0 2 4 2 3 3 6 9 4 1 3 3 8 2 2 2 2 4 6 1 7 8 0
3 1 9 3 6 4 8 4 8 8 4 2 6 9 9]
confusion matrix:
[[ 0 2 5 0 0 1 4 3 2 1]
[ 0 3 5 0 2 1 2 5 0 0]
[ 1 0 8 0 1 1 2 5 2 0]
[ 1 1 7 0 1 2 2 5 1 0]
[ 1 1 7 0 2 0 1 12 4 0]
[ 0 2 2 0 0 3 2 4 2 0]
[ 0 3 6 0 0 2 1 5 1 0]
[ 0 2 3 0 0 1 2 7 0 0]
[ 3 3 7 0 3 0 2 5 1 0]
[ 1 1 6 0 1 0 1 11 3 0]]
classification report:
precision recall f1-score support
0 0.00 0.00 0.00 18
1 0.17 0.17 0.17 18
2 0.14 0.40 0.21 20
3 0.00 0.00 0.00 20
4 0.20 0.07 0.11 28
5 0.27 0.20 0.23 15
6 0.05 0.06 0.05 18
7 0.11 0.47 0.18 15
8 0.06 0.04 0.05 24
9 0.00 0.00 0.00 24
accuracy 0.12 200
macro avg 0.10 0.14 0.10 200
weighted avg 0.10 0.12 0.09 200
"""
接下来就自己根据数据特点与模型结果进行分析吧~
6.代码总结
import numpy as np
import matplotlib.pyplot as plt
import tensorflow.keras.callbacks as cb
import tensorflow.keras.layers as layers
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import Dense, Activation
from tensorflow.python.keras.utils.np_utils import to_categorical
from sklearn.metrics import confusion_matrix, classification_report
# Sequential模式
# model = Sequential()
# model.add(Dense(32, input_dim=32))
# model.add(Activation('relu'))
# model.add(Dense(10))
# model.add(Activation('softmax'))
# model.summary()
# Model模式
inputs = layers.Input(shape=32)
fc1 = Dense(32)(inputs)
ac1 = Activation('relu')(fc1)
fc2 = Dense(10)(ac1)
output = Activation('softmax')(fc2)
model = Model(inputs=inputs, outputs=output)
# 输出模型结构
model.summary()
# 对模型进行编译
model.compile(
optimizer='rmsprop',
loss='categorical_crossentropy', # 二分类用binary
metrics=['accuracy']
)
# 由于没有csv文件,这里用numpy的random随机生成1200条数据,按4:1:1的比例
# 假设:信号长度为32,维度为1;label有10类,维度为1
train_data = np.random.random((800, 32))
train_label = np.random.randint(10, size=(800, 1))
valid_data = np.random.random((200, 32))
valid_label = np.random.randint(10, size=(200, 1))
test_data = np.random.random((200, 32))
test_label = np.random.randint(10, size=(200, 1))
# 将标签转换为分类的 one-hot 编码
train_onehot = to_categorical(train_label, num_classes=10)
valid_onehot = to_categorical(valid_label, num_classes=10)
# 设置回调函数
callbacks = [
cb.ModelCheckpoint('model.h5', monitor='val_loss', verbose=0,
save_best_only=False, save_weights_only=False,
mode='auto', period=1),
cb.EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=0,
mode='auto', baseline=None, restore_best_weights=False)
]
# 拟合模型
history = model.fit(
train_data, train_onehot,
epochs=10, batch_size=32,
validation_data=(valid_data, valid_onehot),
callbacks=callbacks
)
# 将模型训练过程可视化
plt.figure(figsize=(8, 6))
plt.plot(history.history['loss'], label='loss')
plt.plot(history.history['accuracy'], label='accuracy')
plt.plot(history.history['val_loss'], label='val_loss')
plt.plot(history.history['val_accuracy'], label='val_accuracy')
plt.legend()
plt.tight_layout()
plt.savefig('test.png', dpi=96)
plt.show()
# 使用训练好的模型预测测试集
pre_label = model.predict(test_data)
print("pre_label\n", pre_label)
# 将预测结果转换为数值型
print("np.argmax(pre_label, axis=1)\n", np.argmax(pre_label, axis=1))
# 计算混淆矩阵
cm = confusion_matrix(test_label, np.argmax(pre_label, axis=1))
print("confusion matrix:\n", cm)
# 输出分类报告
cr = classification_report(test_label, np.argmax(pre_label, axis=1))
print("classification report:\n", cr)
Tips: 如果模型可以正常summary()而训练又报错,那么问题80%都出在数据上!!仔细检查数据是否存在异常!输入输出维度是否与模型匹配!!
更多内容可以查看Keras官方中文文档,或官方英文文档,英文文档的例子更多一点。有帮助的话可以一键三连(不重要)~
下期预告:(可能)介绍模型调参、网络层的组合等
End