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%,所以我们可以说模型训练得还不错。