目标
在本实验中,您将探索一个使用神经网络进行多类分类的示例。
工具
您将使用一些绘图例程。它们存储在这个目录下的lab_utils_multiclass_TF.py中。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib widget
from sklearn.datasets import make_blobs
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
np.set_printoptions(precision=2)
from lab_utils_multiclass_TF import *
import logging
logging.getLogger("tensorflow").setLevel(logging.ERROR)
tf.autograph.set_verbosity(0)
2.0多类分类
神经网络经常被用来对数据进行分类。例如神经网络:
- 拍摄照片,并将照片中的主题分类为{狗,猫,马,其他};
- 拍摄一个句子,并将其元素的“词性”分类:{名词,动词,形容词等。}
这种类型的网络将在其最后一层有多个单元。每个输出都与一个类别相关联。当一个输入示例应用于网络时,具有最高值的输出是预测的类别。如果输出应用于softmax函数,则softmax的输出将提供输入在每个类别中的概率。
在本实验中,您将看到一个在Tensorflow中构建多类网络的示例。然后我们将看看神经网络是如何进行预测的。
让我们从创建一个四类数据集开始。
2.1准备和可视化我们的数据
我们将使用Scikit-Learn make_blobs函数制作一个包含4个类别的训练数据集,如下图所示。
# make 4-class dataset for classification
classes = 4
m = 100
centers = [[-5, 2], [-2, -2], [1, 2], [5, -2]]
std = 1.0
X_train, y_train = make_blobs(n_samples=m, centers=centers, cluster_std=std,random_state=30)
plt_mc(X_train,y_train,classes, centers, std=std)
每个点代表一个训练样例。轴(x0,x1)是输入,颜色表示与示例相关联的类。一旦训练完毕,模型将呈现一个新的例子(x0,x1),并将预测该类。
在生成时,该数据集代表了许多现实世界的分类问题。有几个输入特征(x0,…,xn)和几个输出类别。该模型被训练成使用输入特征来预测正确的输出类别。
# show classes in data set
print(f"unique classes {np.unique(y_train)}")
# show how classes are represented
print(f"class representation {y_train[:10]}")
# show shapes of our dataset
print(f"shape of X_train: {X_train.shape}, shape of y_train: {y_train.shape}")
2.2模型
本实验将使用如图所示的2层网络。与二元分类网络不同,该网络有四个输出,每个类一个。给定一个输入示例,具有最大值的输出是该输入的预测类。
下面是一个如何在Tensorflow中构建该网络的示例。注意,输出层使用线性激活而不是softmax激活。虽然可以在输出层中包含softmax,但如果在训练期间将线性输出传递给损失函数,则在数值上更稳定。如果该模型用于预测概率,那么softmax可以
tf.random.set_seed(1234) # applied to achieve consistent results
model = Sequential(
[
Dense(2, activation = 'relu', name = "L1"),
Dense(4, activation = 'linear', name = "L2")
]
)
下面的语句编译并训练网络。将from_logits=True设置为损失函数的参数,指定输出激活是线性的,而不是softmax。
model.compile(
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
optimizer=tf.keras.optimizers.Adam(0.01),
)
model.fit(
X_train,y_train,
epochs=200
)
通过训练模型,我们可以看到模型是如何对训练数据进行分类的。
plt_cat_mc(X_train, y_train, model, classes)
上面的决策边界显示了模型如何划分输入空间。这个非常简单的模型对训练数据进行分类没有问题。它是如何做到的呢?让我们更详细地看看这个网络。
下面,我们将从模型中提取训练好的权重,并使用它来绘制每个网络单元的函数。再往下看,对结果有更详细的解释。要成功地使用神经网络,您不需要知道这些细节,但它可能有助于获得更多关于这些层如何组合以解决分类问题的直觉。
# gather the trained parameters from the first layer
l1 = model.get_layer("L1")
W1,b1 = l1.get_weights()
# plot the function of the first layer
plt_layer_relu(X_train, y_train.reshape(-1,), W1, b1, classes)
# gather the trained parameters from the output layer
l2 = model.get_layer("L2")
W2, b2 = l2.get_weights()
# create the 'new features', the training examples after L1 transformation
Xl2 = np.zeros_like(X_train)
Xl2 = np.maximum(0, np.dot(X_train,W1) + b1)
plt_output_layer_linear(Xl2, y_train.reshape(-1,), W2, b2, classes,
x0_rng = (-0.25,np.amax(Xl2[:,0])), x1_rng = (-0.25,np.amax(Xl2[:,1])))
解释
这些图显示了网络第一层中单元0和1的函数。输入在轴上是(
x
0
,
x
1
x_0,x_1
x0,x1)该单元的输出由背景的颜色表示。每个图表右侧的颜色条表示了这一点。请注意,由于这些单元使用的是ReLu,因此输出不一定落在0到1之间,在本例中,输出的峰值大于20。
该图中的等高线表示输出
a
j
[
1
]
a^{[1]}_j
aj[1]为零和非零之间的过渡点。回想一下ReLu的图
图中的等高线是ReLu中的拐点。
单元0将类别0和1与类别2和3分开。行左边的点(类0和1)将输出0,而右边的点将输出大于0的值。
单元1将0、2班与1、3班分开。线以上的点(类0和2)将输出零,而线以下的点将输出大于零的值。让我们看看下一层是如何实现的!
-----------------这是一条手动分割线------------------
这些图中的点是由第一层翻译的训练样本。考虑这个问题的一种方法是,第一层创建了一组新的特性,供第二层评估。这些图中的轴是前一层
a
0
[
1
]
a^{[1]}_0
a0[1]和
a
1
[
1
]
a^{[1]}_1
a1[1]的输出。如上所述,类0和1(绿色和蓝色)有
a
0
[
1
]
=
0
a^{[1]}_0 = 0
a0[1]=0,而类1和2(蓝色和绿色)有
a
1
[
1
]
=
0
a^{[1]}_1 = 0
a1[1]=0。
同样,背景颜色的强度表示最高值。
单元0将产生接近(0,0)的值的最大值,其中类0(蓝色)已被映射
单元1在左上角选择类别1(绿色)产生最大值。
第二单元的目标是右下角3班(橙色)所在的地方。
第3单元在右上方选择我们的最终类(紫色)时产生最大值。
图表中不明显的另一个方面是,单位之间的值是协调的。对于一个单位来说,为它所选择的类别产生最大值是不够的,它还必须是该类别中所有点数单位的最大值。这是通过隐含的softmax函数完成的,该函数是损失函数(’ SparseCategoricalCrossEntropy ')的一部分。与其他激活函数不同,softmax适用于所有输出。
你可以成功地使用神经网络,而不需要知道每个单元的细节。希望这个例子
恭喜
您已经学习了如何构建和操作用于多类分类的神经网络。