update (#172)
@@ -176,9 +176,9 @@ def ToBool(YData):
|
||||
num_example = YData.shape[1]
|
||||
Y = np.zeros((1, num_example))
|
||||
for i in range(num_example):
|
||||
if YData[0,i] == 0: # 第一类的标签设为0
|
||||
if YData[0,i] == 1: # 第一类的标签设为0
|
||||
Y[0,i] = 0
|
||||
elif YData[0,i] == 1: # 第二类的标签设为1
|
||||
elif YData[0,i] == 2: # 第二类的标签设为1
|
||||
Y[0,i] = 1
|
||||
# end if
|
||||
# end for
|
||||
|
||||
@@ -142,20 +142,19 @@ $$
|
||||
|
||||
$$\frac{\partial{Z2}}{\partial{A1}} = \frac{\partial{(W2 \cdot A1 + B2)}}{\partial{A1}}=W2^T$$
|
||||
|
||||
$$\frac{\partial{A1}}{\partial{Z1}}=\frac{\partial{(Sigmoid(Z1))}}{\partial{Z1}}=A1 \cdot (1-A1)$$
|
||||
$$\frac{\partial{A1}}{\partial{Z1}}=\frac{\partial{(Sigmoid(Z1))}}{\partial{Z1}}=A1 \odot (1-A1)$$
|
||||
|
||||
所以:
|
||||
|
||||
$$dZ1=\frac{\partial{J}}{\partial{Z1}} = \frac{\partial{J}}{\partial{Z2}} \cdot \frac{\partial{Z2}}{\partial{A1}} \cdot \frac{\partial{A1}}{\partial{Z1}}$$
|
||||
$$=W2^T \cdot (Z2-Y) \cdot A1 \cdot (1-A1)$$
|
||||
$$=W2^T \cdot dZ2 \cdot A1 \cdot (1-A1) \tag{4}$$
|
||||
$$=W2^T \times dZ2 \odot A1 \odot (1-A1) \tag{4}$$
|
||||
|
||||
### 求W1的梯度
|
||||
|
||||
$$\frac{\partial{Z1}}{\partial{W1}} = \frac{\partial{(W1 \cdot X+B1)}}{\partial{W1}} = X^T$$
|
||||
|
||||
$$
|
||||
dW1=\frac{\partial{J}}{\partial{W1}} = \frac{\partial{J}}{\partial{Z1}} \frac{\partial{Z1}}{\partial{W1}}= dZ1 \cdot X^T \tag{5}
|
||||
dW1=\frac{\partial{J}}{\partial{W1}} = \frac{\partial{J}}{\partial{Z1}} \frac{\partial{Z1}}{\partial{W1}}= dZ1 \times X^T \tag{5}
|
||||
$$
|
||||
|
||||
### 求B1的梯度
|
||||
@@ -4,7 +4,8 @@ Copyright © Microsoft Corporation. All rights reserved.
|
||||
# 知识点
|
||||
|
||||
- [解决异或问题](09.1-实现逻辑异或门.md)
|
||||
- [非线性多分类](09.1-非线性多分类.md)
|
||||
- [理解工作原理](09.2-理解工作原理].md)
|
||||
- [非线性多分类](09.3-非线性多分类.md)
|
||||
- [结果可视化与模块化设计](09.2-结果可视化与模块化设计.md)
|
||||
|
||||
|
||||
@@ -19,6 +20,30 @@ Copyright © Microsoft Corporation. All rights reserved.
|
||||
|
||||
单层神经网络只能实现线性分类,虽然可以用一些特征的高阶形式来拟合有限的曲线,但要想实现真正的非线性分类,必须使用两层以上的神经网络。
|
||||
|
||||
# 用于分类的双层神经网络
|
||||
|
||||
和我们前面学到的线性分类器一样,双层神经网络也有二分类和多分类两种分类器。
|
||||
|
||||
这是二分类器:
|
||||
|
||||
<img src='./Images/9/binary_classifier.png' width="800"/>
|
||||
|
||||
- 输入层可以是任意数量的特征值
|
||||
- 隐层的神经元数量要大于等于输入层特征值数量,并且需要Sigmoid或Tanh等激活函数
|
||||
- 输出层用一个神经元,后接Sigmoid分类函数
|
||||
- 损失函数用二分类交叉熵函数
|
||||
|
||||
这是多分类器,图中所示为三分类:
|
||||
|
||||
<img src='./Images/9/multiple_classifier.png' width="800"/>
|
||||
|
||||
- 输入层可以是任意数量的特征值
|
||||
- 隐层的神经元数量要大于等于输入层特征值数量,并且需要Sigmoid或Tanh等激活函数
|
||||
- 输出层用N个神经元,神经元的数量等于分类的数量,后接Sigmoid分类函数
|
||||
- 损失函数用多分类交叉熵函数
|
||||
|
||||
# 工作原理
|
||||
|
||||
在两层神经网络的输出层,可以用和单层神经网络一样的结构来完成分类任务,而用隐层来完成非线性到线性的转换工作。我们可以通过以下几张图的比较来理解一下非线性到线性的转换。
|
||||
|
||||
以下图片均来自于[Christopher Olah的博客](https://colah.github.io/posts/2014-03-NN-Manifolds-Topology/)。
|
||||
|
||||
@@ -3,16 +3,60 @@ Copyright © Microsoft Corporation. All rights reserved.
|
||||
|
||||
# 实现逻辑异或门
|
||||
|
||||
## 简单证明异或的可能性
|
||||
|
||||
在1969年,一本著名的书《Perceptrons》(感知器,Minsky、Papert,1969)证明了无法使用单层网络(当时称为感知器)来表示最基本的异或逻辑功能。这本书带来了毁灭性的影响,对于神经网络这一新生领域的资金支持及兴趣都消失了。
|
||||
|
||||
50年前的书,就不要去找了,咱们自己简单地证明一下。先看样本数据:
|
||||
|
||||
|样本|1|2|3|4|
|
||||
|----|----|----|----|---|
|
||||
|x1|0|0|1|1|
|
||||
|x2|0|1|0|1|
|
||||
|y|0|1|1|0|
|
||||
|
||||
用单个神经元(感知机)的话,就是下图:
|
||||
|
||||
|神经元|激活函数 Sigmoid|
|
||||
|-|-|
|
||||
|<img src='./Images/9/xor_prove.png' width="400"/>|<img src='./Images/7/sigmoid_seperator.png' width="430"/>|
|
||||
|
||||
前向计算公式:
|
||||
|
||||
$$Z = w1 \cdot x1 + w2 \cdot x2 + b \tag{1}$$
|
||||
$$A = Sigmoid(Z) \tag{2}$$
|
||||
|
||||
对于第一个样本数据,x1=0, x2=0, 如果需要A=0的话,从Sigmoid函数曲线看,需要Z<0,于是有:
|
||||
$$w1 \cdot x1 + w2 \cdot x2 + b < 0$$
|
||||
因为x1=0, x2=0, 所以:
|
||||
$$b < 0 \tag{3}$$
|
||||
|
||||
同理,第二个样本和第三个样本的y值为1,要求Z值大于0,不等式为:
|
||||
|
||||
$$w2 + b > 0 \tag{4}$$
|
||||
$$w1 + b > 0 \tag{5}$$
|
||||
|
||||
第四个样本要求Z小于0:
|
||||
|
||||
$$w1 + w2 + b < 0 \tag{6}$$
|
||||
|
||||
把公式6两边都加b,并把公式3接续:
|
||||
|
||||
$$(w1 + b) + (w2 + b) < b < 0 \tag{7}$$
|
||||
|
||||
再看公式(4)(5),不等式左侧两个因子都大于0,其和必然也大于0,不可能小于b。因此无论如何也不能满足所有的4个样本的条件,所以单个神经元做异或是不可能的。
|
||||
|
||||
## 脑补理想的分类结果
|
||||
|
||||
因为当时还没有BP算法,所以深层神经网络无法实现。进入到两层神经网络后,我们将可以轻松地解决这个问题。看下图:
|
||||
|
||||
<img src='./Images/9/xor_task.png'/>
|
||||
|
||||
因为单层神经网络只能做线性分割,而上图中所示红蓝两色样本,用一条直线是不能分开的,于是人们脑补了那条曲线的分割方式,相当的fancy。但是神经网络真的会这样工作吗?
|
||||
|
||||
我们前边学习过来如何实现与、与非、或、或非,我们看看如何用已有的逻辑搭建异或:
|
||||
## 利用已有的知识解决问题
|
||||
|
||||
我们前边学习过如何实现与、与非、或、或非,我们看看如何用已有的逻辑搭建异或门:
|
||||
|
||||
<img src='./Images/9/xor_gate.png'/>
|
||||
|
||||
@@ -20,70 +64,96 @@ Copyright © Microsoft Corporation. All rights reserved.
|
||||
|----|----|----|----|----|
|
||||
|x1|0|0|1|1|
|
||||
|x2|0|1|0|1|
|
||||
|s1=NAND|1|1|1|0|
|
||||
|s2=OR|0|1|1|1|
|
||||
|y=AND|0|1|1|0|
|
||||
|x1 AND x2|0|0|0|1|
|
||||
|s1=x1 NAND x2|1|1|1|0|
|
||||
|s2=x1 OR x1|0|1|1|1|
|
||||
|y=s1 AND s2|0|1|1|0|
|
||||
|
||||
经过以上组合运算后,可以看到y的输出与x1,x2的输入相比,就是异或逻辑了。
|
||||
经过以上组合运算后,可以看到y的输出与x1,x2的输入相比,就是异或逻辑了。所以,实践证明两层神经网络可以解决问题。我们可以模拟这个思路,用两层神经网络搭建如下模型:
|
||||
|
||||
所以,我们可以模拟这个思路,用两层神经网络搭建如下模型:
|
||||
## 搭建神经网络
|
||||
|
||||
<img src='./Images/9/xor_nn_gate.png'/>
|
||||
<img src='./Images/9/xor_nn.png'/>
|
||||
|
||||
隐层的两个神经元接收x1,x2的输入,输出层有一个神经元负责分类。
|
||||
- 输入层两个特征值x1, x2
|
||||
- 隐层2x2的权重矩阵和2x1的偏移矩阵
|
||||
- 隐层由两个神经元构成
|
||||
- 输出层有一个神经元使用Sigmoid函数进行分类
|
||||
|
||||
## 前向计算
|
||||
|
||||
根据网络结构,我们有了前向计算图:
|
||||
|
||||
<img src='./Images/9/binary_forward.png'/>
|
||||
|
||||
$$Z1 = W1 \cdot X + B1 \tag{10}$$
|
||||
$$A1 = Sigmoid(Z1) \tag{11}$$
|
||||
$$Z2 = W2 \cdot A1 + B2 \tag{12}$$
|
||||
$$A2 = Sigmoid(Z2) \tag{13}$$
|
||||
|
||||
**注意:公式13的Sigmoid在这里不是激活函数,不是激活函数,不是激活函数(重要的概念说三遍),是用作分类函数。**
|
||||
|
||||
## 损失函数
|
||||
|
||||
我们把异或问题归类成二分类问题,所以使用二分类交叉熵损失函数:
|
||||
|
||||
$$
|
||||
J(w,b) = -{1 \over m} \sum^m_{i=1}y_i \ln (a_i)+(1-y_i) \ln (1-a_i) \tag{14}
|
||||
$$
|
||||
|
||||
可以简写为:
|
||||
|
||||
$$
|
||||
J = -Y \ln A + (1-Y) \ln (1-A) \tag{15}
|
||||
$$
|
||||
|
||||
|
||||
## 反向传播
|
||||
|
||||
<img src='./Images/9/binary_backward.png'/>
|
||||
|
||||
|||
|
||||
|---|---|
|
||||
|<img src='./Images/9/binary_result_1.png'/>|<img src='./Images/9/binary_result_2.png'/>|
|
||||
|<img src='./Images/9/binary_result_3.png'/>|<img src='./Images/9/binary_result_4.png'/>|
|
||||
|<img src='./Images/9/binary_result_5.png'/>|<img src='./Images/9/binary_result_6.png'/>|
|
||||
|<img src='./Images/9/binary_result_7.png'/>|<img src='./Images/9/binary_result_8.png'/>|
|
||||
### 梯度生成
|
||||
|
||||
对损失函数求导,可以得到损失函数对输出层的梯度值,即上图中的Z2部分。
|
||||
|
||||
以下是隐层为两个神经元时的结果输出:
|
||||
根据公式15,求A2和Z2的导数:
|
||||
|
||||
$${\partial{J} \over \partial{Z2}}={\partial{J} \over \partial{A2}} \cdot {\partial{A2} \over \partial{Z2}}$$
|
||||
$$= {A2-Y \over A2(1-A2)} \cdot A2(1-A2)$$
|
||||
$$=A2-Y \tag{15} => dZ2$$
|
||||
|
||||
### 求W2和B2的梯度
|
||||
|
||||
$${\partial{J} \over \partial{W2}}={\partial{J} \over \partial{Z2}}{\partial{Z2} \over \partial{W2}}=dZ2 \times A1^T => dW2\tag{16}$$
|
||||
|
||||
$${\partial{J} \over \partial{B2}}={\partial{J} \over \partial{Z2}}{\partial{Z2} \over \partial{B2}}=dZ2 => dB2 \tag{17}$$
|
||||
|
||||
### 求损失函数对隐层的梯度
|
||||
|
||||
$$
|
||||
\frac{\partial{J}}{\partial{Z1}} = \frac{\partial{J}}{\partial{Z2}} \cdot \frac{\partial{Z2}}{\partial{A1}} \cdot \frac{\partial{A1}}{\partial{Z1}}
|
||||
$$
|
||||
$$=W2^T \times dZ2 \odot A1 \odot (1-A1)=>dZ1 \tag{18}$$
|
||||
|
||||
### 求W1和B1的梯度
|
||||
|
||||
$$
|
||||
\frac{\partial{J}}{\partial{W1}} = \frac{\partial{J}}{\partial{Z1}} \frac{\partial{Z1}}{\partial{W1}}= dZ1 \times X^T => dW1\tag{19}
|
||||
$$
|
||||
|
||||
$$
|
||||
\frac{\partial{J}}{\partial{B1}} = \frac{\partial{J}}{\partial{Z1}} \frac{\partial{Z1}}{\partial{B1}}= dZ1 => dB1\tag{20}
|
||||
$$
|
||||
|
||||
## 运行结果
|
||||
|
||||
```
|
||||
epoch=6039, loss=0.005000
|
||||
epoch=6040, loss=0.004999
|
||||
epc=6040,ite=3,los=0.0050
|
||||
w=[[-7.00777143 -7.01121059]
|
||||
[ 5.51518649 5.51451102]]
|
||||
b=[[ 2.86885647]
|
||||
[-8.53863829]]
|
||||
|
||||
w=[[-12.0633545 -12.23675401]]
|
||||
b=[[5.95925939]]
|
||||
|
||||
testing...
|
||||
x=[[0] [0]] y=[[0]] output=[[0.00424183]]
|
||||
True
|
||||
x=[[0] [1]] y=[[1]] output=[[0.99453265]]
|
||||
True
|
||||
x=[[1] [0]] y=[[1]] output=[[0.99452718]]
|
||||
True
|
||||
x=[[1] [1]] y=[[0]] output=[[0.00476486]]
|
||||
True
|
||||
```
|
||||
|
||||
后面的testing...为测试结果,精度为1e-2。
|
||||
<img src='./Images/9/binary_result_2.png'/>
|
||||
|
||||
|
||||
||1|2|3|4|
|
||||
|---|---|---|---|---|
|
||||
|x1|0|0|1|1|
|
||||
|x2|0|1|0|1|
|
||||
|y|0|1|1|0|
|
||||
|Z1|2.86885647|-4.14235412|-4.13891495|-11.15012554|
|
||||
||-8.53863829|-3.02412727|-3.0234518|2.49105922|
|
||||
|A1|9.46285253e-01|1.56370110e-02|1.56900366e-02 |1.43732759e-05|
|
||||
||1.95718330e-04|4.63477089e-02|4.63775738e-02|9.23512657e-01|
|
||||
|Z2|-5.45851003|5.20347907|5.20247396|-5.3417112|
|
||||
|A2|0.00424183|0.99453265|0.99452718|0.00476486|
|
||||
|
||||
|||
|
||||
|---|---|
|
||||
|<img src='./Images/9/xor_x1x2.png'/>|<img src='./Images/9/xor_z1.png'/>|
|
||||
|<img src='./Images/9/xor_a1.png'/>|<img src='./Images/9/xor_z2a2.png'/>|
|
||||
代码位置:ch09, Level0, Level1
|
||||
@@ -0,0 +1,147 @@
|
||||
Copyright © Microsoft Corporation. All rights reserved.
|
||||
适用于[License](https://github.com/Microsoft/ai-edu/blob/master/LICENSE.md)版权许可
|
||||
|
||||
# 理解工作原理
|
||||
|
||||
我们在上一节课的代码基础上再增加些东西,来理解神经网络针对这个异或问题的工作原理。
|
||||
|
||||
## 隐层神经元数量的选择
|
||||
|
||||
一般来说,隐层的神经元数量要大于等于输入特征的数量,在本例中是2。我们从下图可以看到,如果隐层只有一个神经元的话,是不能完成分类任务的。
|
||||
|
||||
|||
|
||||
|---|---|
|
||||
|<img src='./Images/9/binary_result_1.png'/>|<img src='./Images/9/binary_result_2.png'/>|
|
||||
|1个神经元|2个神经元,迭代6040次到达精度要求|
|
||||
|<img src='./Images/9/binary_result_3.png'/>|<img src='./Images/9/binary_result_4.png'/>|
|
||||
|3个神经元,迭代5228次到达精度要求|4个神经元,迭代4331次到达精度要求|
|
||||
|<img src='./Images/9/binary_result_5.png'/>|<img src='./Images/9/binary_result_6.png'/>|
|
||||
|5个神经元,迭代4171次到达精度要求|6个神经元,迭代3903次到达精度要求|
|
||||
|<img src='./Images/9/binary_result_7.png'/>|<img src='./Images/9/binary_result_8.png'/>|
|
||||
|7个神经元,迭代5031次到达精度要求|8个神经元,迭代4107次到达精度要求|
|
||||
|
||||
## 隐层有两个神经元的工作原理
|
||||
|
||||
以下是隐层为两个神经元时的结果输出:
|
||||
|
||||
```
|
||||
w=[[-7.00777143 -7.01121059]
|
||||
[ 5.51518649 5.51451102]]
|
||||
b=[[ 2.86885647]
|
||||
[-8.53863829]]
|
||||
```
|
||||
|
||||
我们使用上面的权重矩阵结果,把4个样本数据代入到前向计算公式中,依次求得Z1,A1,Z2,A2的值,并列表如下:
|
||||
|
||||
||1|2|3|4|
|
||||
|---|---|---|---|---|
|
||||
|x1|0|0|1|1|
|
||||
|x2|0|1|0|1|
|
||||
|y|0|1|1|0|
|
||||
|Z1|2.86885647|-4.14235412|-4.13891495|-11.15012554|
|
||||
||-8.53863829|-3.02412727|-3.0234518|2.49105922|
|
||||
|A1|9.46285253e-01|1.56370110e-02|1.56900366e-02 |1.43732759e-05|
|
||||
||1.95718330e-04|4.63477089e-02|4.63775738e-02|9.23512657e-01|
|
||||
|Z2|-5.45851003|5.20347907|5.20247396|-5.3417112|
|
||||
|A2|0.00424183|0.99453265|0.99452718|0.00476486|
|
||||
|
||||
|||
|
||||
|---|---|
|
||||
|<img src='./Images/9/xor_x1x2.png'/>|<img src='./Images/9/xor_z1.png'/>|
|
||||
|<img src='./Images/9/xor_a1.png'/>|<img src='./Images/9/xor_z2a2.png'/>|
|
||||
|
||||
|
||||
|
||||
|损失函数值||
|
||||
|---|---|
|
||||
|<img src='./Images/9/xor_2_500_loss.png'/>|<img src='./Images/9/xor_2_800_loss.png'/>|
|
||||
|500次迭代|800次迭代|
|
||||
|<img src='./Images/9/xor_2_1000_loss.png'/>|<img src='./Images/9/binary_loss_2.png'/>|
|
||||
|1000次迭代|6000次迭代|
|
||||
|
||||
|Z1||
|
||||
|---|---|
|
||||
|<img src='./Images/9/xor_2_500_z1.png'/>|<img src='./Images/9/xor_2_800_z1.png'/>|
|
||||
|500次迭代|800次迭代|
|
||||
|<img src='./Images/9/xor_2_1000_z1.png'/>|<img src='./Images/9/xor_z1.png'/>|
|
||||
|1000次迭代|6000次迭代|
|
||||
|
||||
|A1||
|
||||
|---|---|
|
||||
|<img src='./Images/9/xor_2_500_a1.png'/>|<img src='./Images/9/xor_2_800_z1.png'/>|
|
||||
|500次迭代|800次迭代|
|
||||
|<img src='./Images/9/xor_2_1000_z1.png'/>|<img src='./Images/9/xor_z1.png'/>|
|
||||
|1000次迭代|6000次迭代|
|
||||
|
||||
|Z1:A1||
|
||||
|---|---|
|
||||
|<img src='./Images/9/xor_2_500_z2a2.png'/>|<img src='./Images/9/xor_2_800_z2a2.png'/>|
|
||||
|500次迭代|800次迭代|
|
||||
|<img src='./Images/9/xor_2_1000_z2a2.png'/>|<img src='./Images/9/xor_z2a2.png'/>|
|
||||
|1000次迭代|6000次迭代|
|
||||
|
||||
|分类结果||
|
||||
|---|---|
|
||||
|<img src='./Images/9/xor_2_500_result.png'/>|<img src='./Images/9/xor_2_800_result.png'/>|
|
||||
|500次迭代|800次迭代|
|
||||
|<img src='./Images/9/xor_2_1000_result.png'/>|<img src='./Images/9/binary_result_2.png'/>|
|
||||
|1000次迭代|6000次迭代|
|
||||
|
||||
|
||||
## 隐层有三个神经元的工作原理
|
||||
|
||||
最终输出的权重矩阵值:
|
||||
|
||||
```
|
||||
[[ 6.70984241 -6.82710114]
|
||||
[ 6.22781451 -5.99280808]
|
||||
[-2.74806343 2.4870413 ]]
|
||||
b=[[-3.51362288]
|
||||
[ 3.15804745]
|
||||
[-1.4773994 ]]
|
||||
|
||||
w=[[12.31224597 -9.738143 3.80483026]]
|
||||
b=[[3.05151459]]
|
||||
```
|
||||
|
||||
迭代5000次到达精度要求的各层的网络输出:
|
||||
|
||||
||1|2|3|4|
|
||||
|---|---|---|---|---|
|
||||
|x1|0|0|1|1|
|
||||
|x2|0|1|0|1|
|
||||
|y|0|1|1|0|
|
||||
|Z1|3.51362288|-10.34072402|3.19621952|-3.63088162|
|
||||
||3.15804745|-2.83476063|9.38586196|3.39305388|
|
||||
||-1.4773994|1.00964189|-4.22546284|-1.73842154|
|
||||
|A1|2.89270940e-02| 3.22898922e-05 |9.60691763e-01| 2.58090628e-02|
|
||||
||9.59224645e-01| 5.54744264e-02| 9.99916105e-01| 9.67486745e-01|
|
||||
||1.85820545e-01| 7.32950061e-01| 1.44079436e-02| 1.49513539e-01|
|
||||
|Z2|-5.22637905| 5.30044483 | 5.19728163 |-5.48336852|
|
||||
|A2|0.00534423 |0.9950354 | 0.99449885 |0.00413811|
|
||||
|
||||
|
||||
我们可以把Z1,A1看作是一个三维的坐标点,并比较迭代200次与迭代5000次的结果,从而了解神经网络的工作过程:
|
||||
|
||||
|
||||
||迭代200次|迭代5000次|
|
||||
|---|---|---|
|
||||
|初始|<img src='./Images/9/xor_x1x2.png'/>|<img src='./Images/9/xor_x1x2.png'/>
|
||||
|Z1-1|<img src='./Images/9/xor_3_200_z1_1.png'/>|<img src='./Images/9/xor_3_5000_z1_1.png'/>|
|
||||
||两个红色点距离较远|两个红色点距离很近|
|
||||
|Z1-2|<img src='./Images/9/xor_3_200_z1_2.png'/>|<img src='./Images/9/xor_3_5000_z1_3.png'/>|
|
||||
||绿色点从一开始就已经分得很开|绿色点的距离越来越远|
|
||||
|Z1-3|<img src='./Images/9/xor_3_200_z1_3.png'/>|<img src='./Images/9/xor_3_5000_z1_2.png'/>|
|
||||
||从某一角度看4个点在一条直线上||
|
||||
|A1-1|<img src='./Images/9/xor_3_200_a1_1.png'/>|<img src='./Images/9/xor_3_5000_a1_1.png'/>|
|
||||
||红点较散|红点缩到了一个角落里|
|
||||
|A1-2|<img src='./Images/9/xor_3_200_a1_2.png'/>|<img src='./Images/9/xor_3_5000_a1_2.png'/>|
|
||||
|A1-3|<img src='./Images/9/xor_3_200_a1_3.png'/>|<img src='./Images/9/xor_3_5000_a1_3.png'/>|
|
||||
|Z2|<img src='./Images/9/xor_3_200_z2a2.png'/>|<img src='./Images/9/xor_3_5000_z2a2.png'/>|
|
||||
||两种颜色的点没有分开|彻底分开了|
|
||||
|A2|<img src='./Images/9/xor_3_200_result.png'/>|<img src='./Images/9/xor_3_5000_result.png'/>|
|
||||
||没有完成|理想分类|
|
||||
|
||||
|
||||
|
||||
代码位置:ch09, Level2_HowXorGateEorks(两个神经元的情况), Level2_LogicXorGate3D(三个神经元的情况)
|
||||
@@ -1,12 +1,12 @@
|
||||
Copyright © Microsoft Corporation. All rights reserved.
|
||||
适用于[License](https://github.com/Microsoft/ai-edu/blob/master/LICENSE.md)版权许可
|
||||
|
||||
# 非线性多分类实现
|
||||
# 非线性多分类
|
||||
|
||||
|
||||
# 提出问题
|
||||
|
||||
前面我们学习了线性二分类与多分类。升级为两层的神经网络之后,我们看看如何用它来做非线性的二分类与多分类。
|
||||
前面用异或问题学习了二分类,现在我们看看如何用它来做非线性多分类。
|
||||
|
||||
我们有如下1000个样本和标签:
|
||||
|
||||
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 21 KiB |
@@ -0,0 +1,127 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
import matplotlib.pyplot as plt
|
||||
import math
|
||||
|
||||
from LossFunction import *
|
||||
from Activators import *
|
||||
from Level0_TwoLayerClassificationNet import *
|
||||
from DataReader import *
|
||||
from WeightsBias import *
|
||||
|
||||
|
||||
# x1=0,x2=0,y=0
|
||||
# x1=0,x2=1,y=1
|
||||
# x1=1,x2=0,y=1
|
||||
# x1=1,x2=1,y=0
|
||||
class XOR_DataReader():
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def ReadData(self):
|
||||
self.X = np.array([0,0,1,1,0,1,0,1]).reshape(2,4)
|
||||
self.Y = np.array([0,1,1,0]).reshape(1,4)
|
||||
self.num_example = self.X.shape[1]
|
||||
self.num_feature = self.X.shape[0]
|
||||
self.num_category = 1
|
||||
|
||||
def GetBatchSamples(self, batch_size, iteration):
|
||||
start = iteration * batch_size
|
||||
end = start + batch_size
|
||||
batch_X = self.X[0:self.num_feature, start:end].reshape(self.num_feature, batch_size)
|
||||
batch_Y = self.Y[:, start:end].reshape(-1, batch_size)
|
||||
return batch_X, batch_Y
|
||||
|
||||
|
||||
def ShowAreaResult(net, wb1, wb2, title):
|
||||
count = 50
|
||||
x1 = np.linspace(0,1,count)
|
||||
x2 = np.linspace(0,1,count)
|
||||
for i in range(count):
|
||||
for j in range(count):
|
||||
x = np.array([x1[i],x2[j]]).reshape(2,1)
|
||||
dict_cache = net.ForwardCalculationBatch2(x, wb1, wb2)
|
||||
output = dict_cache["Output"]
|
||||
if output[0,0] >= 0.5:
|
||||
plt.plot(x[0,0], x[1,0], 's', c='m')
|
||||
else:
|
||||
plt.plot(x[0,0], x[1,0], 's', c='y')
|
||||
# end if
|
||||
# end for
|
||||
# end for
|
||||
plt.title(title)
|
||||
#end def
|
||||
|
||||
def ShowData(X, Y):
|
||||
for i in range(X.shape[1]):
|
||||
if Y[0,i] == 0:
|
||||
plt.plot(X[0,i], X[1,i], '^', c='r')
|
||||
elif Y[0,i] == 1:
|
||||
plt.plot(X[0,i], X[1,i], '.', c='g')
|
||||
# end if
|
||||
# end for
|
||||
plt.xlabel("x1")
|
||||
plt.ylabel("x2")
|
||||
plt.show()
|
||||
|
||||
def Test(dataReader, net, wb1, wb2):
|
||||
print("testing...")
|
||||
for i in range(dataReader.num_example):
|
||||
x,y = dataReader.GetBatchSamples(1, i)
|
||||
dict_output = net.ForwardCalculationBatch2(x, wb1, wb2)
|
||||
output = dict_output["Output"]
|
||||
print(str.format("x={0} y={1} output={2}", x, y, output))
|
||||
if np.abs(output - y) < 1e-2:
|
||||
print("True")
|
||||
else:
|
||||
print("False")
|
||||
#end if
|
||||
#end for
|
||||
|
||||
def SaveWeights(wb1, wb2):
|
||||
np.save("xor_w1_2_2.npy", wb1.W)
|
||||
np.save("xor_w1_2_1.npy", wb1.B)
|
||||
np.save("xor_w2_1_2.npy", wb2.W)
|
||||
np.save("xor_w2_1_1.npy", wb2.B)
|
||||
|
||||
|
||||
def train():
|
||||
|
||||
dataReader = XOR_DataReader()
|
||||
dataReader.ReadData()
|
||||
|
||||
n_input, n_output = dataReader.num_feature, dataReader.num_category
|
||||
n_hidden = 2
|
||||
eta, batch_size, max_epoch = 0.1, 1, 10000
|
||||
eps = 0.005
|
||||
|
||||
params = CParameters(n_input, n_hidden, n_output, eta, max_epoch, batch_size, eps, LossFunctionName.CrossEntropy2)
|
||||
|
||||
loss_history = CLossHistory()
|
||||
net = TwoLayerClassificationNet()
|
||||
|
||||
#ShowData(XData, YData)
|
||||
|
||||
wb1, wb2 = net.train(dataReader, params, loss_history)
|
||||
|
||||
trace = loss_history.GetMinimalLossData()
|
||||
print(trace.toString())
|
||||
title = loss_history.ShowLossHistory(params)
|
||||
|
||||
print(wb1.toString())
|
||||
print(wb2.toString())
|
||||
|
||||
print("wait for 10 seconds...")
|
||||
|
||||
ShowAreaResult(net, trace.wb1, trace.wb2, title)
|
||||
ShowData(dataReader.X, dataReader.Y)
|
||||
|
||||
Test(dataReader, net, wb1, wb2)
|
||||
|
||||
SaveWeights(wb1, wb2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
train()
|
||||
@@ -0,0 +1,145 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
import matplotlib.pyplot as plt
|
||||
import math
|
||||
|
||||
from LossFunction import *
|
||||
from Activators import *
|
||||
from Level0_TwoLayerClassificationNet import *
|
||||
from DataReader import *
|
||||
from WeightsBias import *
|
||||
|
||||
|
||||
# x1=0,x2=0,y=0
|
||||
# x1=0,x2=1,y=1
|
||||
# x1=1,x2=0,y=1
|
||||
# x1=1,x2=1,y=0
|
||||
class XOR_DataReader():
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def ReadData(self):
|
||||
self.X = np.array([0,0,1,1,0,1,0,1]).reshape(2,4)
|
||||
self.Y = np.array([0,1,1,0]).reshape(1,4)
|
||||
self.num_example = self.X.shape[1]
|
||||
self.num_feature = self.X.shape[0]
|
||||
self.num_category = 1
|
||||
|
||||
def GetBatchSamples(self, batch_size, iteration):
|
||||
start = iteration * batch_size
|
||||
end = start + batch_size
|
||||
batch_X = self.X[0:self.num_feature, start:end].reshape(self.num_feature, batch_size)
|
||||
batch_Y = self.Y[:, start:end].reshape(-1, batch_size)
|
||||
return batch_X, batch_Y
|
||||
|
||||
|
||||
def ShowAreaResult(net, wb1, wb2, title):
|
||||
count = 50
|
||||
x1 = np.linspace(0,1,count)
|
||||
x2 = np.linspace(0,1,count)
|
||||
for i in range(count):
|
||||
for j in range(count):
|
||||
x = np.array([x1[i],x2[j]]).reshape(2,1)
|
||||
dict_cache = net.ForwardCalculationBatch2(x, wb1, wb2)
|
||||
output = dict_cache["Output"]
|
||||
if output[0,0] >= 0.5:
|
||||
plt.plot(x[0,0], x[1,0], 's', c='m')
|
||||
else:
|
||||
plt.plot(x[0,0], x[1,0], 's', c='y')
|
||||
# end if
|
||||
# end for
|
||||
# end for
|
||||
plt.title(title)
|
||||
#end def
|
||||
|
||||
def ShowData(X, Y):
|
||||
for i in range(X.shape[1]):
|
||||
if Y[0,i] == 0:
|
||||
plt.plot(X[0,i], X[1,i], '^', c='r')
|
||||
elif Y[0,i] == 1:
|
||||
plt.plot(X[0,i], X[1,i], '.', c='g')
|
||||
# end if
|
||||
# end for
|
||||
plt.xlabel("x1")
|
||||
plt.ylabel("x2")
|
||||
plt.show()
|
||||
|
||||
|
||||
def LoadWeights(wb1, wb2):
|
||||
wb1.W = np.load("xor_w1_2_2.npy")
|
||||
wb1.B = np.load("xor_w1_2_1.npy")
|
||||
wb2.W = np.load("xor_w2_1_2.npy")
|
||||
wb2.B = np.load("xor_w2_1_1.npy")
|
||||
|
||||
|
||||
def ShowZ1A1Z2A2():
|
||||
wb1 = WeightsBias(2,2,0.1,InitialMethod.Xavier)
|
||||
wb2 = WeightsBias(1,2,0.1,InitialMethod.Xavier)
|
||||
LoadWeights(wb1, wb2)
|
||||
print(wb1.toString())
|
||||
print(wb2.toString())
|
||||
|
||||
dataReader = XOR_DataReader()
|
||||
dataReader.ReadData()
|
||||
|
||||
|
||||
Z1 = np.dot(wb1.W, dataReader.X) + wb1.B
|
||||
A1 = Sigmoid().forward(Z1)
|
||||
# layer 2
|
||||
Z2 = np.dot(wb2.W, A1) + wb2.B
|
||||
A2 = Sigmoid().forward(Z2)
|
||||
print(Z1)
|
||||
print(A1)
|
||||
print(Z2)
|
||||
print(A2)
|
||||
|
||||
for i in range(dataReader.num_example):
|
||||
if dataReader.Y[0,i] == 0:
|
||||
plt.plot(dataReader.X[0,i],dataReader.X[1,i],'^',c='r')
|
||||
else:
|
||||
plt.plot(dataReader.X[0,i],dataReader.X[1,i],'o',c='g')
|
||||
plt.grid()
|
||||
plt.title("X1:X2")
|
||||
plt.show()
|
||||
|
||||
|
||||
for i in range(dataReader.num_example):
|
||||
if dataReader.Y[0,i] == 0:
|
||||
plt.plot(Z1[0,i],Z1[1,i],'^',c='r')
|
||||
else:
|
||||
plt.plot(Z1[0,i],Z1[1,i],'o',c='g')
|
||||
plt.grid()
|
||||
plt.title("Z1")
|
||||
plt.show()
|
||||
|
||||
|
||||
for i in range(dataReader.num_example):
|
||||
if dataReader.Y[0,i] == 0:
|
||||
plt.plot(A1[0,i],A1[1,i],'^',c='r')
|
||||
else:
|
||||
plt.plot(A1[0,i],A1[1,i],'o',c='g')
|
||||
plt.grid()
|
||||
plt.title("A1")
|
||||
plt.show()
|
||||
|
||||
x = np.linspace(-6,6)
|
||||
a = Sigmoid().forward(x)
|
||||
plt.plot(x,a)
|
||||
|
||||
for i in range(dataReader.num_example):
|
||||
if dataReader.Y[0,i] == 0:
|
||||
plt.plot(Z2[0,i],A2[0,i],'^',c='r')
|
||||
else:
|
||||
plt.plot(Z2[0,i],A2[0,i],'o',c='g')
|
||||
plt.grid()
|
||||
plt.title("Z2:A2")
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 每次运行之前,需要把level1中的Line 98, max_epoch的数字从小改到大,比如200,400,600,800,1000,...,
|
||||
# 每改一次max_epoch,运行一次level1,生成权重值并自动保存,再运行一次本程序绘图
|
||||
ShowZ1A1Z2A2()
|
||||
@@ -4,7 +4,7 @@
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
import matplotlib.pyplot as plt
|
||||
import math
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
|
||||
from LossFunction import *
|
||||
from Activators import *
|
||||
@@ -94,7 +94,7 @@ def train():
|
||||
dataReader.ReadData()
|
||||
|
||||
n_input, n_output = dataReader.num_feature, dataReader.num_category
|
||||
n_hidden = 2
|
||||
n_hidden = 3
|
||||
eta, batch_size, max_epoch = 0.1, 1, 10000
|
||||
eps = 0.005
|
||||
|
||||
@@ -122,7 +122,7 @@ def train():
|
||||
Test(dataReader, net, wb1, wb2)
|
||||
|
||||
SaveWeights(wb1, wb2)
|
||||
|
||||
|
||||
def LoadWeights(wb1, wb2):
|
||||
wb1.W = np.load("xor_w1_2_2.npy")
|
||||
wb1.B = np.load("xor_w1_2_1.npy")
|
||||
@@ -160,22 +160,28 @@ def ShowZ1A1Z2A2():
|
||||
plt.title("X1:X2")
|
||||
plt.show()
|
||||
|
||||
|
||||
fig = plt.figure()
|
||||
ax = Axes3D(fig)
|
||||
for i in range(dataReader.num_example):
|
||||
if dataReader.Y[0,i] == 0:
|
||||
plt.plot(Z1[0,i],Z1[1,i],'^',c='r')
|
||||
ax.scatter(Z1[0,i],Z1[1,i],Z1[2,i],c='r')
|
||||
#ax.plot(Z1[0,i],Z1[1,i],Z1[2,i],c='r')
|
||||
else:
|
||||
plt.plot(Z1[0,i],Z1[1,i],'o',c='g')
|
||||
ax.scatter(Z1[0,i],Z1[1,i],Z1[2,i],c='g')
|
||||
#ax.plot(Z1[0,i],Z1[1,i],Z1[2,i],c='g')
|
||||
plt.grid()
|
||||
plt.title("Z1")
|
||||
plt.show()
|
||||
|
||||
|
||||
fig = plt.figure()
|
||||
ax = Axes3D(fig)
|
||||
for i in range(dataReader.num_example):
|
||||
if dataReader.Y[0,i] == 0:
|
||||
plt.plot(A1[0,i],A1[1,i],'^',c='r')
|
||||
ax.scatter(A1[0,i],A1[1,i],A1[2,i],'^',c='r')
|
||||
#plt.plot(A1[0,i],A1[1,i],A1[2,i],'^',c='r')
|
||||
else:
|
||||
plt.plot(A1[0,i],A1[1,i],'o',c='g')
|
||||
ax.scatter(A1[0,i],A1[1,i],A1[2,i],'o',c='g')
|
||||
#plt.plot(A1[0,i],A1[1,i],A1[2,i],'o',c='g')
|
||||
plt.grid()
|
||||
plt.title("A1")
|
||||
plt.show()
|
||||
@@ -195,4 +201,7 @@ def ShowZ1A1Z2A2():
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
train()
|
||||
# 每次运行之前,需要把level1中的Line 98, max_epoch的数字从小改到大,比如200,400,600,800,1000,...,
|
||||
# 每改一次max_epoch,运行一次level1,生成权重值并自动保存,再运行一次本程序绘图
|
||||
ShowZ1A1Z2A2()
|
||||
@@ -8,7 +8,7 @@ import math
|
||||
|
||||
from LossFunction import *
|
||||
from Activators import *
|
||||
from Level1_TwoLayerClassificationNet import *
|
||||
from Level0_TwoLayerClassificationNet import *
|
||||
from DataReader import *
|
||||
from WeightsBias import *
|
||||
|
||||
@@ -22,7 +22,7 @@ def ShowAreaResult(net, wb1, wb2, title):
|
||||
for i in range(count):
|
||||
for j in range(count):
|
||||
x = np.array([x1[i],x2[j]]).reshape(2,1)
|
||||
dict_cache = net.ForwardCalculationBatch(x, wb1, wb2)
|
||||
dict_cache = net.ForwardCalculationBatch3(x, wb1, wb2)
|
||||
output = dict_cache["Output"]
|
||||
r = np.argmax(output, axis=0)
|
||||
if r == 0:
|
||||
@@ -4,7 +4,7 @@
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>3de29cfe-16a6-493d-97b3-e617074af796</ProjectGuid>
|
||||
<ProjectHome>.</ProjectHome>
|
||||
<StartupFile>Level2_LogicXorGate.py</StartupFile>
|
||||
<StartupFile>Level3_Classifier.py</StartupFile>
|
||||
<SearchPath>
|
||||
</SearchPath>
|
||||
<WorkingDirectory>.</WorkingDirectory>
|
||||
@@ -24,8 +24,10 @@
|
||||
<Compile Include="Activators.py" />
|
||||
<Compile Include="DataReader.py" />
|
||||
<Compile Include="Level0_TwoLayerClassificationNet.py" />
|
||||
<Compile Include="Level2_LogicXorGate.py" />
|
||||
<Compile Include="Level2_Classifier.py" />
|
||||
<Compile Include="Level2_LogicXorGate3D.py" />
|
||||
<Compile Include="Level2_HowXorGateWorks.py" />
|
||||
<Compile Include="Level1_LogicXorGate.py" />
|
||||
<Compile Include="Level3_Classifier.py" />
|
||||
<Compile Include="LossFunction.py" />
|
||||
<Compile Include="Parameters.py">
|
||||
<SubType>Code</SubType>
|
||||
|
||||