本博文记录了机器学习的一些常见算法,展示了自己的笔记,但因才疏学浅,可能会出现纰漏和错误,还望不吝赐教。
CNN实现的代码,有详细的注释,可以画出学习的曲线,有一个参数调整的过程,希望能帮助到读者。
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras import backend as K
import matplotlib.pyplot as plt
# kears只是一个高级化的API库,提供一些很简单的函数调用完成强大的功能,把调用权完全赋予用户
# 模型按层独立构造,backend指示后台,tensorflow或者CNTK等完成底层的张量乘法或者卷积操作
# 写一个LossHistory类,保存loss和acc
class LossHistory(keras.callbacks.Callback):
def on_train_begin(self, logs={}):
self.losses = {'batch':[], 'epoch':[]}
self.accuracy = {'batch':[], 'epoch':[]}
self.val_loss = {'batch':[], 'epoch':[]}
self.val_acc = {'batch':[], 'epoch':[]}
def on_batch_end(self, batch, logs={}):
self.losses['batch'].append(logs.get('loss'))
self.accuracy['batch'].append(logs.get('acc'))
self.val_loss['batch'].append(logs.get('val_loss'))
self.val_acc['batch'].append(logs.get('val_acc'))
def on_epoch_end(self, batch, logs={}):
self.losses['epoch'].append(logs.get('loss'))
self.accuracy['epoch'].append(logs.get('acc'))
self.val_loss['epoch'].append(logs.get('val_loss'))
self.val_acc['epoch'].append(logs.get('val_acc'))
def loss_plot(self, loss_type):
iters = range(len(self.losses[loss_type]))
plt.figure()
# acc
plt.plot(iters, self.accuracy[loss_type], 'r', label='train acc')
# loss
plt.plot(iters, self.losses[loss_type], 'g', label='train loss')
if loss_type == 'epoch':
# val_acc
plt.plot(iters, self.val_acc[loss_type], 'b', label='val acc')
# val_loss
plt.plot(iters, self.val_loss[loss_type], 'k', label='val loss')
plt.grid(True)
plt.xlabel(loss_type)
plt.ylabel('acc-loss')
plt.legend(loc="upper right")
plt.show()
batch_size = 128
num_classes = 10
epochs = 10 # 训练的次数
img_rows, img_cols = 28, 28
# 数据预处理和切分
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# x_test 10000*28*28 y_test 10000 测试数据集10000张
# x_train 60000*28*28 y_train 60000 训练数据集60000张
# 默认图像的维度顺序 channel_first channel_last
# 2D or 3D -> channel row column
# shape()返回一个矩阵的维度,以一个元组的形式返回,依次是第一维、第二维、、
# reshape()把原矩阵按照新的维度组成一个新矩阵
if K.image_data_format() == 'channels_first':
x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)
x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
input_shape = (1, img_rows, img_cols)
else:
x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
input_shape = (img_rows, img_cols, 1)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
print('x_train shape:', x_train.shape)
print('y_train shape', y_train.shape)
print('x_test shape', x_test.shape)
print('y_test shape', y_test.shape)
# 把y_train or y_test结果向量展成NN输出响应的形式
# 这种形式是one-hot独热矩阵
# 用于以categorical_crossentropy为目标损失函数的模型中
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
# 设置模型参数
model = Sequential() # 序列模型,适用于单输入单输出的顺序堆叠
# Conv2D() filters卷积核的数目 也就是每张图像卷积完之后得到的新的图像数
# kernel_size卷积核的维度可以是单值,表示卷积核是个方阵,也可以是一个元组代表行和列
# 输入时4D张量为 samples rows columns channels
# 输出时4D张量为 samples rows columns filters
model.add(Conv2D(32, kernel_size=(3, 3),
activation='relu',
input_shape=input_shape))
model.add(Conv2D(64, (3, 3), activation='relu')) # 使用add()函数来堆叠子模型
# 池化层 pool_size是指把输入矩阵依照该参数指定的宽度和高度划分成一个个的小矩阵,并取矩阵里的最大值作为新矩阵的元素
model.add(MaxPooling2D(pool_size=(2, 2)))
# 断开层 在每次更新参数的时候随机的断开一些神经元之间的连接,使得残差随机的传播,可以很好的避免过拟合
model.add(Dropout(0.25))
# 扁平层 把多维的矩阵数据拉伸成一个一维的向量,往往在卷积层和全连接层之间
model.add(Flatten())
# 全连接层 输出维度为128
model.add(Dense(256, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))
# 输出以上各层模型的参数情况
model.summary()
model.compile(loss=keras.losses.categorical_crossentropy,
optimizer=keras.optimizers.Adadelta(),
metrics=['accuracy']) # 模型构建完成之后可以通过compile()函数来定义学习过程
# 创建一个实例history
history = LossHistory()
model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
verbose=1,
validation_data=(x_test, y_test),
# validation_split=0.4,
callbacks=[history]) # 批量的在数据集上进行迭代
# validation_data参数用来指定验证集,而validation_split表示在训练集上分裂出多少比例的样本做验证集
# callback参数表明回调的入口用来画出在训练过程中learning curve
score = model.evaluate(x_test, y_test, verbose=0) # 使用测试集评估模型的性能
print('Test loss:', score[0])
print('Test accuracy:', score[1])
# 绘制acc-loss曲线
history.loss_plot('epoch')
# 保存实验模型
if input('Do you want to save the model ?\n y/n : ') == 'y':
fileName = input("Please input the file name:\n")
model.save(fileName + '.h5')