提升模型准确率:组合模型

各位朋友,新年好! 随着春节假期的结束,想必你们陆陆续续返回工做岗位,开始新的一年的拼搏。我也会继续努力,争取在深度学习方面更进一步,接下来,我将继续聊一聊深度学习在计算机视觉中的应用。git

在前面的《站在巨人的肩膀上:迁移学习》和《再谈迁移学习:微调网络》两篇文章中,咱们介绍了迁移学习的强大之处。然而,人们探索新知识老是永无止境,在提升深度学习模型准确率方面,仍在孜孜不倦的追求着。这篇文章将介绍一种提高模型准确率的方法:组合模型。github

从字面上理解,组合模型并不难解释,简单说,就是为深度学习创建多个模型,而后用多个模型来预测,采起投票或平均法来决定最后的预测结果。稍微想想,彷佛比较好理解,俗话说,三个臭皮匠,顶个诸葛亮。多个模型投票的结果,应该好于单个模型的准确率。固然,机器学习看起来有些不靠谱(拿几率说事),但仍是创建在严密的理论基础之上,组合模型提升准确率若是仅仅创建在一条谚语之上,不足以说服人,也没办法让人接受。bash

事实上,组合模型是创建在一个称为琴生不等式(Jensen's inequality)之上,该公式以丹麦数学家约翰·琴生(Johan Jensen)命名,给出了积分的凸函数值和凸函数的积分值间的关系。有兴趣的同窗能够去Google一下,看看这个公式有何神奇之处,我也找了一些资料,然而...没看懂,只了解其大意是说,可能某个的模型的偏差低于全部模型的平均值,但因为咱们没有能够用来“选择”此模型的标准,因此咱们能够确信全部模型的平均值不会比随机选择一个模型差。是否是仍是有些晕乎?嗯,这个不重要,咱们用实践来检验一下是否是有效吧。微信

接下来,咱们就要准备训练多个机器学习模型。咱们也不用把问题复杂化,设计多种网络结构的模型,最简单的方法是,采用相同的网络结构,甚至使用相同的超参数,但训练出不一样的参数。闲话少说,直接上代码:网络

((trainX, trainY), (testX, testY)) = cifar10.load_data()
trainX = trainX.astype("float") / 255.0
testX = testX.astype("float") / 255.0


lb = LabelBinarizer()
trainY = lb.fit_transform(trainY)
testY = lb.transform(testY)

labelNames = ["airplane", "automobile", "bird", "cat", "deer", "dog", "frog", "horse", "ship", "trunk"]

aug = ImageDataGenerator(rotation_range=10, width_shift_range=0.1, height_shift_range=0.1, horizontal_flip=True,
                         fill_mode="nearest")

for i in np.arange(0, args["num"]):
  print("[INFO] training model {}/{}".format(i + 1, args["num"]))
  opt = SGD(lr=0.01, decay=0.01/40, momentum=0.9, nesterov=True)
  model = MiniVGGNet.build(width=32, height=32, depth=3, classes=10)
  model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=["accuracy"])
  H = model.fit_generator(aug.flow(trainX, trainY, batch_size=64), validation_data=(testX, testY), epochs=40,
                          steps_per_epoch=len(trainX)//64, verbose=1)
  p = [args["models"], "model_{}.model".format(i)]
  model.save(os.path.sep.join(p))
复制代码

代码比较容易理解,采用cifar10数据集训练,10种类别标签,对输入数据进行了数据扩充(data augmentation),这个数据扩充是随机实时进行,加上训练数据集和验证数据集也是随机划分,因此最后训练出的网络参数有所不一样,训练完成以后,将模型序列化到文件,供后面使用。循环num遍,就产生了num个模型。app

接下来就是依次加载个模型文件,每一个模型分别进行预测,而后取均值:机器学习

(testX, testY) = cifar10.load_data()[1]
testX = testX.astype("float") / 255.0
labelNames = ["airplane", "automobile", "bird", "cat", "deer", "dog", "frog", "horse", "ship", "trunk"]
lb = LabelBinarizer()
testY = lb.fit_transform(testY)

model_paths = os.path.join(args["models"], "*.model")
model_paths = list(glob.glob(model_paths))
models = []

for (i, model_path) in enumerate(model_paths):
  print("[INFO] loading model {}/{}".format(i + 1, len(model_paths)))
  models.append(load_model(model_path))


print("[INFO] evaluating ensemble ...")
predictions = []
for model in models:
  predictions.append(model.predict(testX, batch_size=64))

predictions = np.average(predictions, axis=0)
print(classification_report(testY.argmax(axis=1), predictions.argmax(axis=1), target_names=labelNames))

复制代码

上面的代码,代码量相对于单个模型而言,并无增长多少,只是多了一个循环,多了一个取平均值,但训练过程却多了num倍,我用电脑训练5个模型,结果花了一夜尚未训练完:(函数

最后测试的结果如何呢?经过组合多个网络的输出,成功将准确度从83%提升到84%,即便这些网络使用彻底相同的超参数在同一数据集上进行训练。有数据代表,采用组合模型,一般准确度有1-5%的提高。post

看到这儿,你可能会有些失望,费了这么大的劲,好像也没啥提高,可是别忘了,在医疗领域、自动驾驶领域,即便费上好大的力气,准确率可以提高小数点后面几位,都是值得的。就像每一年度的kaggle竞赛,人们依然在孜孜不倦的追求着准确率的提高。学习

以上实例均有完整的代码,点击阅读原文,跳转到我在github上建的示例代码。

另外,我在阅读《Deep Learning for Computer Vision with Python》这本书,在微信公众号后台回复“计算机视觉”关键字,能够免费下载这本书的电子版。

往期回顾

  1. 再谈迁移学习:微调网络
  2. 站在巨人的肩膀上:迁移学习
  3. 聊一聊rank-1和rank-5准确度
  4. 使用数据加强技术提高模型泛化能力

image
相关文章
相关标签/搜索