
8.1 使用矩阵编写人工智能框架
ANN_V1.py的源代码如下:

在Spyder集成开发环境中运行代码ANN_V1.py,运行结果如下:

第一个预测值是0.009 664 49,实际值是0;第二个预测值是0.007 865 06,实际值是0;第三个预测值是0.993 588 98,实际值是1;第四个预测值是0.992 119 57,实际值是1。预测的结果很不错。
ANN_V1.py的输入数据如下:

输入特征x包括4行3列数据,分别为[0,0,1]、[0,1,1]、[1,0,1]、[1,1,1],第1行是[0,0,1],对应的y值是0;第2行是[0,1,1],对应的y值是0;第3行是[1,0,1],对应的y值是1;第4行是[1,1,1],对应的y值是1。根据x中每一行的数据,预测y中对应的值,判断预测值是否准确。
如图8-1所示,输入数据x是自变量(Independent Variable),输出数据y是因变量(Dependent Variable)。从数学的角度,例如y=ax+b,x是自变量,y就是因变量,y随着x的改变而改变。自变量x有3列,x1、x2、x3;y只有1列。

图8-1 输入及输出数据
NumPy通过np.array方法创建矩阵。如何判断一个变量是否为矩阵?在Spyder的Variable explorer栏中,如果变量是(4,3),表明是一个矩阵;如果是(4,),表明是一个数组,如图8-2所示。

图8-2 查询变量
通过np.array创建一个矩阵,在Spyder中将鼠标指针放在np.array上,按Ctrl+鼠标左键即可查看np.array的源代码,将打开numerictypes.py的代码文件。
numerictypes.py的源代码如下:

ANN_V1.py的输入数据X是一个矩阵,在Spyder的Ipython console栏中通过print (dir(X))命令查询矩阵X的方法如下:

矩阵对象包括很多方法,其中一个方法是矩阵转置(transpose),在IPython console栏中输入help(X.T),可查询矩阵转置的使用方法。矩阵转置是将行变成列,将列变成行。矩阵转置的示例如下:

第1个示例中的x是2行2列的数据,第1行是1.、2.,第2行是3.、4.,第1列是1.、3.,第2列是2.、4.。对x进行转置以后,行变成列,列变成行,第1行的1.、2.就变成了第1列的1.、2.,第2行3.、4.就变成了第2列3.、4.。
第2个示例中的x是一个数组,只有1行数据1.、2.、3.、4.,对x进行转置以后,x仍是1.、2.、3.、4.。
例如ANN_V1.py中y=np.array([[0,0,1,1]]).T的值,转置以后变成了矩阵[[0],[0],[1],[1]],如图8-3所示。

图8-3 y转置矩阵
为理解转置的作用,我们将ANN_V1.py中的y=np.array([[0,0,1,1]]).T的转置去掉,变成y=np.array([[0,0,1,1]]),y是np.array创建的数组,这里y变成了一行数据。从数值的角度,y是一个二维数组,包含一行四列的数据,运行结果如图8-4所示。
将y=np.array([[0,0,1,1]])代码改回y=np.array([[0,0,1,1]]).T,再次运行ANN_V1.py,矩阵转置将一行变成了一列,对比结果如图8-5所示,左侧是转置运算以后的结果,右侧是未做转置运算的结果。

图8-4 不做转置运算

图8-5 转置运算、不做转置运算的对比
在Spyder右下角的IPython console栏,在提示符后输入y,查询y的值,如图8-6所示。

图8-6 y值查询
y的表现形式是一个4行1列的数组。
为了加深对矩阵转置运算的理解,我们对x=np.array([1.,2.,3.,4.])代码进行测试,x的输出结果是array([1., 2., 3., 4.]),如图8-7所示。
然后进行x.T转置运算,此时x.T的计算结果仍然是array([1.,2.,3.,4.]),在Spyder的Variable explorer栏中查看变量x的值,x在代码中声明的是一维数组类型(4,),对x进行转置操作以后没有变化,一维数组没有将行变成列,如图8-8所示。
可以使用NumPy声明简单的一维数组,但使用NumPy定义的二维数组都是矩阵。在机器学习中都采用矩阵的方式,转置操作本身是对矩阵的操作,矩阵转置将矩阵的行变成列。

图8-7 np.array方法

图8-8 x.T转置运算
构建一个矩阵y=np.array([[1,2,3,4]])。y.T的转置运算和x.T不一样,x=np.array([1.,2.,3.,4.])中只有一对中括号,表明是一个一维数组,一维数组转置运算没有将行变成列;而y=np.array([[1,2,3,4]])中有2对中括号,表示是一个二维数组,不是一个矩阵。矩阵转置运算将行变成了列,如图8-9所示。

图8-9 矩阵转置运算
在ANN_V1.py代码中,layer0是输入层,layer1是隐藏层,输入层的数据通过和权重的计算,结果到达隐藏层,隐藏层有一个Sigmoid激活函数的操作。神经网络是一个前向传播、反向传播的训练过程,前向传播使用Sigmoid函数对数据进行处理,反向传播是对Sigmoid函数进行求导,导数是数值的变化度,就是对结果的负责程度。
nonlin函数对Sigmoid激活函数进行了封装,nonlin函数的第二个参数deriv使用了布尔值,默认值是False,将Sigmoid作为激活函数,在前向传播中使用;如果传入参数deriv为True,则对Sigmoid激活函数求导,在反向传播的时候使用,判断哪些特征对当前的误差结果负有更大的责任。
