深度学习的数学:使用Python语言
上QQ阅读APP看书,第一时间看更新

1.5 scikit-learn

虽然本书的目标是覆盖深度学习的数学知识,而非实现深度学习模型本身,但我们时不时会发现,一两个简单的神经网络模型有助于我们理解问题。这时候我们就会用到sklearn中的MLPClassifier类。另外,sklearn还提供了很多有用的组件用于评估模型的性能和绘制高维图像。

下面我们构建一个简单的神经网络,对8 × 8的手写体灰度小图进行分类。sklearn内置了我们这里需要的数据集。以下是示例代码:

  import numpy as np
  from sklearn.datasets import load_digits
  from sklearn.neural_network import MLPClassifier
 
❶ d = load_digits()
  digits = d["data"]
  labels = d["target"]
 
  N = 200
❷ idx = np.argsort(np.random.random(len(labels)))
  x_test, y_test = digits[idx[:N]], labels[idx[:N]]
  x_train, y_train = digits[idx[N:]], labels[idx[N:]]
 
❸ clf = MLPClassifier(hidden_layer_sizes=(128,))
  clf.fit(x_train, y_train)
 
  score = clf.score(x_test, y_test)
  pred = clf.predict(x_test)
  err = np.where(y_test != pred)[0]
  print("score : ", score)
  print("errors:")
  print(" actual : ", y_test[err])
  print(" predicted: ", pred[err])

上述代码首先导入numpy模块,然后从sklearn中导入load_digits来返回图像数据集,并导入MLPClassifier来训练一个基于多层感知机的传统神经网络。接下来从返回的图像数据集中分别抽取图片和对应的数字标签❶。其中图片被存储为包含8 × 8 = 64个元素的向量,这表示将单张图片的二维像素矩阵按行展开,并且拼接成一行。由于整个图像数据集包含1797张图片,因此digits是一个1797行、64列的二维NumPy数组,labels则是一个包含1797个数字标签的一维向量。

随机地将图片的顺序打乱(这里需要注意保持图片和标签的对应关系❷),然后划分训练数据和测试数据(x_train、x_test)以及对应的标签(y_train、y_test)。拿出前200张图片作为测试样本,余下的1597张图片则作为训练样本。这样每个手写体数字就大约有160张图片用于训练,而有大约20张图片用于测试。

接下来创建一个MLPClassifier对象用于建模❸。指定单个隐层的大小为128个神经元,其他参数全部默认。由于输入为包含64个元素的向量,因此在通过隐层后,向量的大小将翻倍。我们不用指定输出层的大小,sklearn会根据y_train的标签自动进行推测。训练模型非常简单,只需要调用clf.fit并且传入训练图片(x_train)和标签(y_train)。

训练这么小的数据集只需要几分钟就够了。训练结束后,模型学到的权重和截距被保存在对象clf中。我们先获得模型的score,它表示模型总体的准确率,然后拿到模型在测试集上的预测结果(pred)。通过与真实标签(y_test)进行对比,我们可以得到预测错误的样本编号,将其保存到变量err中。最后把错误标签和对应的真实标签输出并进行对比。

每次执行上述代码时,我们都会重新将样本打乱,因而划分的训练集和测试集会发生变化。此外,神经网络在训练前都会随机初始化各个参数。因此,每次训练的结果都会不同。我第一次执行这段代码时,得到的总体准确率为0.97(97%)。如果是随机猜测,准确率大约是10%,所以我们可以说模型训练得还不错。