说明

我们将实现一个 4 层的全连接网络,来完成二分类任务。 网络输入节点数为 2,隐藏
层的节点数设计为: 25、 50和25,输出层两个节点,分别表示属于类别 1 的概率和类别 2
的概率,如下图所示

生成数据

1
2
from sklearn.datasets import make_moons 
from sklearn.model_selection import train_test_split
1
2
3
N=2000
test_size=0.3
X,y=make_moons(n_samples=N,noise=0.2,random_state=100)

划分训练集和测试集

1
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=test_size,random_state=42)
1
print(X.shape,y.shape)
(2000, 2) (2000,)

数据可视化

1
2
3
4
import matplotlib.pyplot as plt
plt.figure(figsize=(16,12))
plt.scatter(X[:,0],X[:,1],c=y.ravel())
plt.show()

png

网络层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Layer:
def __init__(self,n_input,n_neurons,activation=None,weights=None,bias=None):
self.weights=weights if weights is not None else np.random.randn(n_input,n_neurons)*np.sqrt(1/n_neurons)
self.bias=bias if bias is not None else np.random.rand(n_neurons)*0.1
self.activation=activation
self.last_activation=None#激活函数的输出值o
self.error=None#用于计算当前层的delta变量的中间变量
self.delta=None#记录当前层的delta变量,用于计算梯度

#前向传播函数
def activate(self,x):
r=np.dot(x,self.weights)+self.bias
self.last_activation=self._apply_activation(r)
return self.last_activation

#激活函数的实现
def _apply_activation(self,r):
if self.activation is None:
return r
elif self.activation=='relu':
return np.maximum(r,0)
elif self.activation=='tanh':
return np.tanh(r)
elif self.activation=='sigmoid':
return 1/(1+np.exp(-r))
return r

#激活函数的导数实现
def apply_activation_derivative(self,r):
#若无激活函数,导数为1
if self.activation is None:
return np.ones_like(r)
elif self.activation =='relu':
grad=np.array(r,copy=True)
grad[r>0]=1.
grad[r<0]=0
return grad
elif self.activation =='tanh':
return 1-r**2
elif self.activation =='sigmoid':
return r*(1-r)
return r

网络模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class NeuralNetwork:
def __init__(self):
self._layers=[]#网络层对象列表

#追加网络层
def add_layer(self,layer):#这里的layer将由Layer类实例化得到
self._layers.append(layer)

#网络的前向传播
def feed_forward(self,X):
# 依次通过各个网络层,流动态
for layer in self._layers:
X=layer.activate(X)
return X

#网络的反向传播
def backpropagation(self,X,y,lr):

#首先前向计算,得到输出值
output=self.feed_forward(X)

#然后反向循环,这里有些不明白
for i in reversed(range(len(self._layers))):
layer=self._layers[i]#得到当前层的对象
#如果是输出层
if layer==self._layers[-1]:
layer.error=y-output
layer.delta=layer.error*layer.apply_activation_derivative(output)
#如果是隐藏层
else:
next_layer=self._layers[i+1]#得到下一层的对象
layer.error=np.dot(next_layer.weights,next_layer.delta)
layer.delta=layer.error*layer.apply_activation_derivative(layer.last_activation)

#最后循环更新权值
for i in range(len(self._layers)):
layer=self._layers[i]
# o_i 为上一网络层的输出
o_i=np.atleast_2d(X if i==0 else self._layers[i-1].last_activation)
#执行梯度下降算法
layer.weights+=lr*layer.delta*o_i.T#layer.delta*o_i.T便是梯度,注意delta为负,故这里用的加号

#训练模型
def train(self,X_train,X_test,y_train,y_test,lr,max_epochs):
#onehot编码
y_onehot=np.zeros((y_train.shape[0],2))
y_onehot[np.arange(y_train.shape[0]),y_train]=1
#开始训练...
mses=[]
for i in range(max_epochs):
for j in range(len(X_train)):#一次训练一个样本
self.backpropagation(X_train[j],y_onehot[j],lr)
if i%10==0:
#打印loss
mse=np.mean(np.square(y_onehot-self.feed_forward(X_train)))
mses.append(mse)
print('Epoch:#%s:%f'%(i,float(mse)))
#打印准确率
#print('Accurary:%.2f%%'%(self.accuracy(self.predict(X_test),y_test.flatten())*100))

return mses

创建网络对象,添加4层全连接层

1
2
3
4
5
nn=NeuralNetwork()
nn.add_layer(Layer(2,25,'sigmoid'))#隐藏层1,2 ==> 25
nn.add_layer(Layer(25,50,'sigmoid'))#隐层层2,25==> 50
nn.add_layer(Layer(50,25,'sigmoid'))#隐藏层3,50 ==> 25
nn.add_layer(Layer(25,2,'sigmoid'))#输出层,25 ==> 2

训练模型

1
mses=nn.train(X_train,X_test,y_train,y_test,0.3,100)
Epoch:#0:0.101804
Epoch:#10:0.100500
Epoch:#20:0.096917
Epoch:#30:0.095081
Epoch:#40:0.093955
Epoch:#50:0.093544
Epoch:#60:0.093765
Epoch:#70:0.075405
Epoch:#80:0.043148
Epoch:#90:0.027516

可视化loss与epoch的关系

1
2
3
plt.plot([i for i in range(10)],mses)
plt.xlabel('epochs')
plt.ylabel('loss')

png