纯Numpy实现多层神经网络
本文分为以下几个内容
1.实现层的逻辑结构
2.实现各种激活函数
3.实现Dense层
4.实现前向传播
5.实现反向传播
6.示例
实现网络层的逻辑:
首先实现一个简单的网络层,该网络层什么也不做,就是前向传播,来什么,就前向传播什么。
# 定义一个layer
class Layer:
def __init__(self):
pass
def forward(self, input):
return input
def backward(self, input, grad_output):
pass
实现基本的神经网络激活函数:
# 定义Relu层
class ReLU(Layer):
def __init__(self):
pass
def forward(self,input):
return np.maximum(0,input) # relu函数为max(0,x)
def backward(self,input,grad_output):
relu_grad = input>0 #relu函数导数为1 if x>0 else 0
return grad_output*relu_grad
实现Sigmoid层
class Sigmoid(Layer):
def __init__(self):
pass
def _sigmoid(self,x):
return 1.0/(1+np.exp(-x))
def forward(self,input):
return self._sigmoid(input)
def backward(self,input,grad_output):
sigmoid_grad = self._sigmoid(input)*(1-self._sigmoid(input))
return grad_output*sigmoid_grad
实现Tanh层
class Tanh(Layer):
def __init__(self):
pass
def _tanh(self,x):
return np.tanh(x)
def forward(self,input):
return self._tanh(input)
def backward(self, input, grad_output):
grad_tanh = 1-(self._tanh(input))**2
return grad_output*grad_tanh
还有各种各样的激活函数,只要继承了Layer类即可。
实现Dense
一个Dense包括层,权重以及偏置,同时也包括前向传播,反向传播,反向传播,公式如下
前向传播
[公式]
反向传播
[公式]
那么这样就很明显了,我们实现前向传播以及反向传播:
class Dense(Layer):
def __init__(self, input_units, output_units, learning_rate=0.01):
self.learning_rate = learning_rate
self.weights = np.random.randn(input_units, output_units)*0.01
self.biases = np.zeros(output_units)
def forward(self,input):
return np.dot(input,self.weights)+self.biases
def backward(self,input,grad_output):
grad_input = np.dot(grad_output, self.weights.T)
grad_weights = np.dot(input.T,grad_output)/input.shape[0]
grad_biases = grad_output.mean(axis=0)
self.weights = self.weights - self.learning_rate*grad_weights
self.biases = self.biases - self.learning_rate*grad_biases
return grad_input
通过上面的工作,如果我们需要构建一个神经网络,我们可以这样:
network = []
network.append(Dense(1,50))
network.append(Tanh())
network.append(Dense(50,1))
这样,就构建了一个只含有一个隐藏层的神经网络:
前向传播
def forward(network,X):
activations = []
input = X
for layer in network:
activations.append(layer.forward(input))
input = activations[-1]
assert len(activations) == len(network)
return activations
预测:
def predict(network,X):
logits = forward(network,X)[-1]
return logits
训练:
训练的时候需要损失函数,这里我只定义了一个mse损失函数
def train(network,X,y):
layer_activations = forward(network,X)
layer_inputs = [X]+layer_activations
logits = layer_activations[-1]
# 这里的损失函数需要自己定义
loss = np.square(logits - y).sum()
loss_grad = 2.0*(logits-y)
for layer_i in range(len(network))[::-1]:
layer = network[layer_i]
loss_grad = layer.backward(layer_inputs[layer_i],loss_grad) #grad w.r.t. input, also weight updates
return np.mean(loss)
这样,整个神经网络就搭建起来。
我们使用该神经网络拟合 [公式]:y=sinx
首先构建训练集和测试集:
x_train = np.linspace(-np.pi,0.7 * np.pi,140).reshape(140,-1)
y_train = np.sin(x_train)
x_test = np.linspace(np.pi*0.7,np.pi,60).reshape(60,-1)
y_test = np.sin(x_test)
使用刚刚我们定义的神经网络:
losses = []
for h in range(3,100):
network = []
network.append(Dense(1,h))
network.append(Tanh())
network.append(Dense(h,1))
ll = []
for e in range(100000):
loss = train(network,x_train,y_train)
ll.append(loss)
print(np.mean(ll[-1000:]))
losses.append(np.mean(ll[-1000:]))
通过对含有一个隐藏层的神经元个数分析
可以发现
当隐藏层神经元为94的时候,该loss是最小的,于是,我们将神经元个数设置为94
进行训练:
看起来效果不错,我们将图画出来:
看起来神经网络对历史的拟合是非常完美的,但是对未来的预测还是有点差强人意。