坚持实施简单的神经网络
Stuck implementing simple neural network
我的头一直撞在这堵砖墙上似乎有永恒的时间,但我似乎无法绕过它。我正在尝试仅使用 numpy 和矩阵乘法来实现自动编码器。不允许使用 theano 或 keras 技巧。
我将描述问题及其所有细节。一开始有点复杂,因为有很多变量,但它真的很简单。
我们所知道的
1) X
是一个 m
乘以 n
的矩阵,它是我们的输入。输入是该矩阵的行。每个输入都是一个 n
维行向量,我们有 m
个。
2)我们(单个)隐藏层中的神经元数量,即k
。
3)我们神经元的激活函数(sigmoid,记为g(x)
)及其导数g'(x)
我们不知道又想找的东西
总的来说,我们的目标是找到 6 个矩阵:w1
即 n
乘以 k
,b1
即 m
乘以 k
, w2
是 k
乘以 n
, b2 是 m
乘以 n
, w3
是 n
乘以 n
和 b3
即 m
by n
.
它们是随机初始化的,我们使用梯度下降找到最佳解决方案。
过程
整个过程看起来像这样
首先我们计算z1 = Xw1+b1
。它是 m
by k
并且是我们隐藏层的输入。然后我们计算 h1 = g(z1)
,这只是将 sigmoid 函数应用于 z1
的所有元素。当然它也是 m
by k
并且是我们隐藏层的输出。
然后我们计算 z2 = h1w2+b2
是 m
乘以 n
并且是我们神经网络输出层的输入。然后我们计算 h2 = g(z2)
自然也是 m
by n
并且是我们神经网络的输出。
最后,我们获取此输出并对其执行一些线性运算符:Xhat = h2w3+b3
也是 m
by n
,这是我们的最终结果。
我卡在哪里
我要最小化的成本函数是均方误差。我已经在 numpy 代码中实现了它
def cost(x, xhat):
return (1.0/(2 * m)) * np.trace(np.dot(x-xhat,(x-xhat).T))
问题是找到成本关于 w1,b1,w2,b2,w3,b3
的导数。我们称成本为 S
.
推导自己并用数字检查自己后,我确定了以下事实:
1) dSdxhat = (1/m) * np.dot(xhat-x)
2) dSdw3 = np.dot(h2.T,dSdxhat)
3) dSdb3 = dSdxhat
4) dSdh2 = np.dot(dSdxhat, w3.T)
但我一辈子都搞不懂 dSdz2。这是一堵砖墙。
从链式规则来看,dSdz2 = dSdh2 * dh2dz2 但尺寸不匹配。
计算 S 关于 z2 的导数的公式是什么?
编辑 - 这是我对自动编码器的整个前馈操作的代码。
import numpy as np
def g(x): #sigmoid activation functions
return 1/(1+np.exp(-x)) #same shape as x!
def gGradient(x): #gradient of sigmoid
return g(x)*(1-g(x)) #same shape as x!
def cost(x, xhat): #mean squared error between x the data and xhat the output of the machine
return (1.0/(2 * m)) * np.trace(np.dot(x-xhat,(x-xhat).T))
#Just small random numbers so we can test that it's working small scale
m = 5 #num of examples
n = 2 #num of features in each example
k = 2 #num of neurons in the hidden layer of the autoencoder
x = np.random.rand(m, n) #the data, shape (m, n)
w1 = np.random.rand(n, k) #weights from input layer to hidden layer, shape (n, k)
b1 = np.random.rand(m, k) #bias term from input layer to hidden layer (m, k)
z1 = np.dot(x,w1)+b1 #output of the input layer, shape (m, k)
h1 = g(z1) #input of hidden layer, shape (m, k)
w2 = np.random.rand(k, n) #weights from hidden layer to output layer of the autoencoder, shape (k, n)
b2 = np.random.rand(m, n) #bias term from hidden layer to output layer of autoencoder, shape (m, n)
z2 = np.dot(h1, w2)+b2 #output of the hidden layer, shape (m, n)
h2 = g(z2) #Output of the entire autoencoder. The output layer of the autoencoder. shape (m, n)
w3 = np.random.rand(n, n) #weights from output layer of autoencoder to entire output of the machine, shape (n, n)
b3 = np.random.rand(m, n) #bias term from output layer of autoencoder to entire output of the machine, shape (m, n)
xhat = np.dot(h2, w3)+b3 #the output of the machine, which hopefully resembles the original data x, shape (m, n)
好的,这是一个建议。在矢量的情况下,如果您将 x 作为长度为 n
的矢量,那么 g(x)
也是长度为 n
的矢量。但是,g'(x)
不是向量,它是 Jacobian matrix,大小为 n X n
。类似地,在小批量情况下,X 是大小为 m X n
的矩阵,g(X)
是 m X n
但 g'(X)
是 n X n
。尝试:
def gGradient(x): #gradient of sigmoid
return np.dot(g(x).T, 1 - g(x))
@Paul 是正确的,偏差项应该是向量,而不是矩阵。你应该有:
b1 = np.random.rand(k) #bias term from input layer to hidden layer (k,)
b2 = np.random.rand(n) #bias term from hidden layer to output layer of autoencoder, shape (n,)
b3 = np.random.rand(n) #bias term from output layer of autoencoder to entire output of the machine, shape (n,)
Numpy 的广播意味着您不必更改 xhat
的计算。
然后(我认为!)你可以像这样计算导数:
dSdxhat = (1/float(m)) * (xhat-x)
dSdw3 = np.dot(h2.T,dSdxhat)
dSdb3 = dSdxhat.mean(axis=0)
dSdh2 = np.dot(dSdxhat, w3.T)
dSdz2 = np.dot(dSdh2, gGradient(z2))
dSdb2 = dSdz2.mean(axis=0)
dSdw2 = np.dot(h1.T,dSdz2)
dSdh1 = np.dot(dSdz2, w2.T)
dSdz1 = np.dot(dSdh1, gGradient(z1))
dSdb1 = dSdz1.mean(axis=0)
dSdw1 = np.dot(x.T,dSdz1)
这对你有用吗?
编辑
我决定我完全不确定 gGradient
应该是一个矩阵。怎么样:
dSdxhat = (xhat-x) / m
dSdw3 = np.dot(h2.T,dSdxhat)
dSdb3 = dSdxhat.sum(axis=0)
dSdh2 = np.dot(dSdxhat, w3.T)
dSdz2 = h2 * (1-h2) * dSdh2
dSdb2 = dSdz2.sum(axis=0)
dSdw2 = np.dot(h1.T,dSdz2)
dSdh1 = np.dot(dSdz2, w2.T)
dSdz1 = h1 * (1-h1) * dSdh1
dSdb1 = dSdz1.sum(axis=0)
dSdw1 = np.dot(x.T,dSdz1)
我的头一直撞在这堵砖墙上似乎有永恒的时间,但我似乎无法绕过它。我正在尝试仅使用 numpy 和矩阵乘法来实现自动编码器。不允许使用 theano 或 keras 技巧。
我将描述问题及其所有细节。一开始有点复杂,因为有很多变量,但它真的很简单。
我们所知道的
1) X
是一个 m
乘以 n
的矩阵,它是我们的输入。输入是该矩阵的行。每个输入都是一个 n
维行向量,我们有 m
个。
2)我们(单个)隐藏层中的神经元数量,即k
。
3)我们神经元的激活函数(sigmoid,记为g(x)
)及其导数g'(x)
我们不知道又想找的东西
总的来说,我们的目标是找到 6 个矩阵:w1
即 n
乘以 k
,b1
即 m
乘以 k
, w2
是 k
乘以 n
, b2 是 m
乘以 n
, w3
是 n
乘以 n
和 b3
即 m
by n
.
它们是随机初始化的,我们使用梯度下降找到最佳解决方案。
过程
整个过程看起来像这样
首先我们计算z1 = Xw1+b1
。它是 m
by k
并且是我们隐藏层的输入。然后我们计算 h1 = g(z1)
,这只是将 sigmoid 函数应用于 z1
的所有元素。当然它也是 m
by k
并且是我们隐藏层的输出。
然后我们计算 z2 = h1w2+b2
是 m
乘以 n
并且是我们神经网络输出层的输入。然后我们计算 h2 = g(z2)
自然也是 m
by n
并且是我们神经网络的输出。
最后,我们获取此输出并对其执行一些线性运算符:Xhat = h2w3+b3
也是 m
by n
,这是我们的最终结果。
我卡在哪里
我要最小化的成本函数是均方误差。我已经在 numpy 代码中实现了它
def cost(x, xhat):
return (1.0/(2 * m)) * np.trace(np.dot(x-xhat,(x-xhat).T))
问题是找到成本关于 w1,b1,w2,b2,w3,b3
的导数。我们称成本为 S
.
推导自己并用数字检查自己后,我确定了以下事实:
1) dSdxhat = (1/m) * np.dot(xhat-x)
2) dSdw3 = np.dot(h2.T,dSdxhat)
3) dSdb3 = dSdxhat
4) dSdh2 = np.dot(dSdxhat, w3.T)
但我一辈子都搞不懂 dSdz2。这是一堵砖墙。
从链式规则来看,dSdz2 = dSdh2 * dh2dz2 但尺寸不匹配。
计算 S 关于 z2 的导数的公式是什么?
编辑 - 这是我对自动编码器的整个前馈操作的代码。
import numpy as np
def g(x): #sigmoid activation functions
return 1/(1+np.exp(-x)) #same shape as x!
def gGradient(x): #gradient of sigmoid
return g(x)*(1-g(x)) #same shape as x!
def cost(x, xhat): #mean squared error between x the data and xhat the output of the machine
return (1.0/(2 * m)) * np.trace(np.dot(x-xhat,(x-xhat).T))
#Just small random numbers so we can test that it's working small scale
m = 5 #num of examples
n = 2 #num of features in each example
k = 2 #num of neurons in the hidden layer of the autoencoder
x = np.random.rand(m, n) #the data, shape (m, n)
w1 = np.random.rand(n, k) #weights from input layer to hidden layer, shape (n, k)
b1 = np.random.rand(m, k) #bias term from input layer to hidden layer (m, k)
z1 = np.dot(x,w1)+b1 #output of the input layer, shape (m, k)
h1 = g(z1) #input of hidden layer, shape (m, k)
w2 = np.random.rand(k, n) #weights from hidden layer to output layer of the autoencoder, shape (k, n)
b2 = np.random.rand(m, n) #bias term from hidden layer to output layer of autoencoder, shape (m, n)
z2 = np.dot(h1, w2)+b2 #output of the hidden layer, shape (m, n)
h2 = g(z2) #Output of the entire autoencoder. The output layer of the autoencoder. shape (m, n)
w3 = np.random.rand(n, n) #weights from output layer of autoencoder to entire output of the machine, shape (n, n)
b3 = np.random.rand(m, n) #bias term from output layer of autoencoder to entire output of the machine, shape (m, n)
xhat = np.dot(h2, w3)+b3 #the output of the machine, which hopefully resembles the original data x, shape (m, n)
好的,这是一个建议。在矢量的情况下,如果您将 x 作为长度为 n
的矢量,那么 g(x)
也是长度为 n
的矢量。但是,g'(x)
不是向量,它是 Jacobian matrix,大小为 n X n
。类似地,在小批量情况下,X 是大小为 m X n
的矩阵,g(X)
是 m X n
但 g'(X)
是 n X n
。尝试:
def gGradient(x): #gradient of sigmoid
return np.dot(g(x).T, 1 - g(x))
@Paul 是正确的,偏差项应该是向量,而不是矩阵。你应该有:
b1 = np.random.rand(k) #bias term from input layer to hidden layer (k,)
b2 = np.random.rand(n) #bias term from hidden layer to output layer of autoencoder, shape (n,)
b3 = np.random.rand(n) #bias term from output layer of autoencoder to entire output of the machine, shape (n,)
Numpy 的广播意味着您不必更改 xhat
的计算。
然后(我认为!)你可以像这样计算导数:
dSdxhat = (1/float(m)) * (xhat-x)
dSdw3 = np.dot(h2.T,dSdxhat)
dSdb3 = dSdxhat.mean(axis=0)
dSdh2 = np.dot(dSdxhat, w3.T)
dSdz2 = np.dot(dSdh2, gGradient(z2))
dSdb2 = dSdz2.mean(axis=0)
dSdw2 = np.dot(h1.T,dSdz2)
dSdh1 = np.dot(dSdz2, w2.T)
dSdz1 = np.dot(dSdh1, gGradient(z1))
dSdb1 = dSdz1.mean(axis=0)
dSdw1 = np.dot(x.T,dSdz1)
这对你有用吗?
编辑
我决定我完全不确定 gGradient
应该是一个矩阵。怎么样:
dSdxhat = (xhat-x) / m
dSdw3 = np.dot(h2.T,dSdxhat)
dSdb3 = dSdxhat.sum(axis=0)
dSdh2 = np.dot(dSdxhat, w3.T)
dSdz2 = h2 * (1-h2) * dSdh2
dSdb2 = dSdz2.sum(axis=0)
dSdw2 = np.dot(h1.T,dSdz2)
dSdh1 = np.dot(dSdz2, w2.T)
dSdz1 = h1 * (1-h1) * dSdh1
dSdb1 = dSdz1.sum(axis=0)
dSdw1 = np.dot(x.T,dSdz1)