用Numpy写一个简洁的神经网络
最近想实现一个简单的神经网络,之前用C语言写过一次,只觉得很繁琐,最近看到一个非常简洁的神经网络实现,代码如下
X = np.array([ [0,0,1],[0,1,1],[1,0,1],[1,1,1] ])
y = np.array([[0,1,1,0]]).T
syn0 = 2*np.random.random((3,4)) - 1
syn1 = 2*np.random.random((4,1)) - 1
for j in xrange(60000):
l1 = 1/(1+np.exp(-(np.dot(X,syn0))))
l2 = 1/(1+np.exp(-(np.dot(l1,syn1))))
l2_delta = (l2 - y)*(l2*(1-l2)) #此处原链接中取负号,后面梯度计算取加号,有很大误导性
l1_delta = l2_delta.dot(syn1.T) * (l1 * (1-l1))
syn1 -= l1.T.dot(l2_delta)
syn0 -= X.T.dot(l1_delta)
具体的解释见原链接。
非常简洁,利用numpy中便捷的矩阵运算代替了我之前所使用的多重循环,运算速度也快了很多。因此我想学习一下这种pythonic的方式来实现一个简单的径向基网络。
首先需要了解的是numpy。np.array是一种很方便的数据类型,既是多维数组,也是矩阵。当它作为矩阵时有两种运算:
- 如 + - * 普通运算
- 点乘等矩阵运算
当执行简单运算的时候对于两个操作数有一些要求:
比如,一个 n*m 的矩阵可以加一个 1*m的矩阵,这等于是每一列都加上一个1*m的矩阵,或者是加上一个 n*1的矩阵,这也就等于每一行都加上一个n*1的矩阵。减法和乘法同理。
当一侧是一个数字的时候,这就等于对矩阵中的每一个元素执行该操作,还有就是 n**2 或者 n*n表示每一个元素都取平方,而不是矩阵乘法。
真正的矩阵乘法需要使用dot,如x.dot(y)或者np.dot(x,y),遵循矩阵运算法则。
好的,现在我们来写一个RBF网络。
我们假设网络为3层,节点数分别为i=2,j=5,k=1,样本数量为n=4
X = np.array([ [0,0],[0,1],[1,0],[1,1] ])
y = np.array([[0,1,1,0]]).T
c = 2*np.random.random((2,5)) - 1
b = 2*np.random.random((1,5)) - 1
w = 2*np.random.random((1,5)) - 1
def euclidDistance(X, Y): #1*n
Z = X - Y
return Z.dot(Z.T)
def rbf(X, c, b): # X:n*i c:i*j b:1*j w:1*j
N = len(X)
J = len(c.T)
r = np.zeros((N,J))
for i in range(N):
for j in range(J):
r[i][j] = euclidDistance(X[i],c.T[j])
return r # return n*j
for u in range(6000):
r = rbf(X,c,b)
l1 = np.exp(-1*b*r)
l2 = l1.dot(w.T)
delta_w = (l2-y) * l1
delta_b = (l2-y) * w * l1* -1 * r
w -= 0.1*np.sum(delta_w, 0) //此处其实是累积bp算法,将n个样本的梯度一起计算了
b -= 0.1*np.sum(delta_b, 0)
公式为:
$$ φ(x)=∑w_j p(x,c_j) $$
$$ \rho (x,c_j)=w−β_j||x−c_j||^2 $$
反向求导之后
$$ \Delta w = (o - y)\rho $$
$$ \Delta \beta = -(o - y )w_j\rho ||x−c_j||^2 $$
可以与代码中对应,但是需要注意的是程序中进行的都是矩阵运算,在看的过程中需要十分注意。
(之所以代码长度比普通bp网络长是因为bp网络中的线性运算可以非常流畅地转换成矩阵运算,基本上一次dot点乘就可以省略一个循环。而欧几里得距离的计算方式我没有想到什么好方法表示...只有用一个双重循环了)