目录
在这一节中,我们将用模拟数据集(2个特征),实现一个完整的2层神经网络。我们将首先实现一个简单的线性分类器,然后将代码扩展到2层神经网络。我们将看到,这个扩展惊人地简单,只需要非常少的变化即可。
10.1 生成数据
让我们生成一个不容易线性分割的分类数据集。我们最喜欢的例子是螺旋数据集,代码如下:
import matplotlib.pyplot as plt
import numpy as np
#生成数据集
#每个分类100个点
N = 100
#2个维度
D = 2
#3个分类
K = 3
#数据矩阵(每行一个样本)
X = np.zeros((N*K,D))
#分类标签
y = np.zeros(N*K, dtype='uint8')
for j in range(K):
ix = range(N*j,N*(j+1))
#半径
r = np.linspace(0.0,1,N)
# theta
t = np.linspace(j*4,(j+1)*4,N) + np.random.randn(N)*0.2
X[ix] = np.c_[r*np.sin(t), r*np.cos(t)]
y[ix] = j
#可视化数据
plt.scatter(X[:, 0], X[:, 1], c=y, s=40, cmap=plt.cm.Spectral)
plt.show()
模拟的螺旋数据由三个类(蓝、红、黄)组成,它们不是线性可分离的
通常,我们希望对数据集进行预处理,以使每个特征具有零均值和单位标准偏差,但这是模拟数据,特征已经在从-1到1的良好范围内,所以我们跳过这一步。
10.2 训练Softmax线性分类器
10.2.1初始化参数
首先让我们在这个分类数据集上训练一个Softmax分类器。前面的章节讲过,Softmax分类器有一个线性分值函数,使用交叉熵损失。线性分类器的参数由权重矩阵W和每个类的偏置向量 b 组成。首先让这些参数初始化为随机数:
#随机初始化参数W
W = 0.01 * np.random.randn(D,K)
b = np.zeros((1,K))
D=2是维数,k=3是分类数。
10.2.2计算分类得分
由于这是一个线性分类器,我们可以非常简单地用户一个矩阵乘法计算所有类分数:
#为一个线性分类器计算分类得分
scores = np.dot(X, W) + b
在这个例子中,我们有300个2维点,因此在乘法之后,分数大小是[300×3 ],其中每行给出对应于3个类(蓝色、红色、黄色)的类得分。
10.2.3计算损失
我们需要的第二个关键因素是损失函数,它是一个可微分的目标,用计算的分类分数来量化我们的不爽。直观地说,我们希望正确的类具有比其他类更高的分数。在这种情况下,损失应该很低,否则损失会很高。有很多方法来量化这种直觉,但是在这个例子中,我们使用与Softmax 分类器相关联的交叉熵损失。回想一下,如果F是一个样本的类分数数组(例如这里就是3个数的数组),那么Softmax 分类器计算该样本的损失为:
我们可以看到,Softmax 分类器将F的每个元素解释为保持三个类的(非归一化)对数概率。我们对这些进行指数化以得到(非规范化)概率,然后将它们归一化以获得概率。因此,对数内的表达式是正确类的归一化概率。注意这个表达式是如何工作的:这个量总是在0到1之间。当正确类的概率非常小(接近0)时,损失将朝向(正)无穷大。相反,当正确的类概率接近1时,由于log(1)=0,损失将趋于零。因此,当正确的类概率较高时,Li的值较低,而当正确的类概率较低时非常高。
还记得,完整的Softmax 分类器损失,是定义在训练样本上的平均交叉熵损失和正则化:
考虑到上面计算的数组 scores
,我们可以计算损失。首先,获得概率的方法是直截了当的:
num_examples = X.shape[0]
#获得没有归一化的概率
exp_scores = np.exp(scores)
#归一化
probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
我们现在有一个数组probs
,大小为 300×3,其中每行现在包含类概率。因为我们已经将它们归一化,现在每一行总和为1。现在我们可以查询在每个样本中分配给正确类的对数概率:
correct_logprobs = -np.log(probs[range(num_examples),y])
correct_logprobs
是一个一维数组,为每个样本分配给正确的类的概率。完全损失是