实现隐藏层
先修要求
接下来我们会讲神经网络在多层感知器里面的数学部分。讲多层感知器我们会用到向量和矩阵。你可以通过下列讲解对此做个回顾:
- Khan Academy's introduction to vectors.
- Khan Academy's introduction to matrices.
由来
之前我们研究的是有一个输出节点网络,代码也很直观。但是现在我们有不同的输入,多个隐藏层,他们的权重需要有两个索引 wij,i 表示输入单位,j 表示隐藏单位。
例如在下面这个网络图中,输入被标注为 x1,x2, x3,隐藏层节点是 h1 和 h2。
指向 h1 的权重被标成了红色,这样更好区分。
为了定位权重,我们把输入节点的索引 i 和输出节点的索引 j 结合,得到:
w11
代表从 x1 到 h1 的权重;
w12
代表从 x1 到 h2 的权重。
下图包括了从输入层到隐藏层用 wij 来标注的所有权重:
之前我们可以把权重写成序列,标记为 wi。
现在,权重被储存在矩阵中,由 wij 来标记。每一行表示从输入层发出的权重,每一列表示从输入到隐藏层的权重。这里我们由三个输入,两个因此节点,权重矩阵标示为:
记得比较一下上面的示意图来确定你了解不同的权重在矩阵中神经网络中的对应关系。
用 NumPy 来初始化权重,我们需要提供矩阵的维度,如果特征是包含输入的二维序列:
# Number of records and input units
# 数据点以及每个数据点有多少输入的个数
n_records, n_inputs = features.shape
# Number of hidden units
# 隐藏层个数
n_hidden = 2
weights_input_to_hidden = np.random.normal(0, n_inputs**-0.5, size=(n_inputs, n_hidden))
这样创建了一个 名为 weights_input_to_hidden
的 2D 序列,维度是 n_inputs
乘 n_hidden
。记住,输入到隐藏层是所有输入乘以隐藏层权重的和。所以对每一个隐藏层节点 hj,我们需要计算:
为了实现这点,我们需要运用矩阵乘法,如果你对线性代数有点忘了,我们建议你看下之前先修部分的资料。这里你只需要了解矩阵如何相乘。
在这里,我们把输入(一个行向量)与权重相乘。要实现这个,要把输入点乘(内积)以权重矩阵的每一列。例如要计算到第一个隐藏节点的输入 j=1,你需要把这个输入与权重矩阵的第一列做点乘:
针对第二个隐藏节点的输入,你需要计算输入与第二列的点积,以此类推。
在 NumPy 中,你可以直接使用 np.dot
hidden_inputs = np.dot(inputs, weights_input_to_hidden)
你可以定义你的权重矩阵是 n_hidden
乘 n_inputs
然后把输入作为竖向量相乘:
注意:
这里权重的索引在上图中做了改变,与之前图片并不匹配。这是因为,在矩阵标注时行索引永远在列索引之前,所以用之前的方法做标识会引起误导。你只需要了解这跟之前的权重矩阵是一样的,只是做了转换,之前的第一列现在是第一行,之前的第二列现在是第二行。如果用之前的标记,权重矩阵是下面这个样子的:
切记,上面标注方式是不正确的,这里只是为了让你更清楚这个矩阵如何跟之前神经网络的权重匹配。
矩阵相乘最重要的是他们的维度相匹配。因为它们在点乘时需要有相同数量的元素。在第一个例子中,输入向量有三列,权重矩阵有三行;第二个例子中,权重矩阵有三列,输入向量有三行。如果维度不匹配,你会得到:
# Same weights and features as above, but swapped the order
hidden_inputs = np.dot(weights_input_to_hidden, features)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-11-1bfa0f615c45> in <module>()
----> 1 hidden_in = np.dot(weights_input_to_hidden, X)
ValueError: shapes (3,2) and (3,) not aligned: 2 (dim 1) != 3 (dim 0)
3x2 的矩阵跟 3 个元素序列是没发相乘的。因为矩阵中的两列与序列中的元素个数并不匹配。能够相乘的矩阵如下:
这里的规则是,如果是序列在左边,序列的元素个数必须与右边矩阵的行数一样。如果矩阵在左边,那么矩阵的列数,需要与右边向量的行数匹配。
构建一个列向量
看上面的介绍,你有时会需要一个列向量,尽管 NumPy 默认是行向量。你可以用 arr.T
来对序列进行转制,但对一维序列来说,转制还是行向量。所以你可以用 arr[:,None]
来创建一个列向量:
print(features)
> array([ 0.49671415, -0.1382643 , 0.64768854])
print(features.T)
> array([ 0.49671415, -0.1382643 , 0.64768854])
print(features[:, None])
> array([[ 0.49671415],
[-0.1382643 ],
[ 0.64768854]])
当然,你可以创建一个二维序列,然后用 arr.T
得到列向量。
np.array(features, ndmin=2)
> array([[ 0.49671415, -0.1382643 , 0.64768854]])
np.array(features, ndmin=2).T
> array([[ 0.49671415],
[-0.1382643 ],
[ 0.64768854]])