深度学习入门:基于Python的理论与实践 笔记
一,Python基础
由于本人之前已经系统学习过Python,此处只总结有关深度学习的Python的库
-
NumPy
-
生成NumPy数组
要生成NumPy数组,需要使用np.array()方法。np.array()接收Python
列表作为参数,生成NumPy数组(numpy.ndarray)
>>> x = np.array([1.0, 2.0, 3.0]) >>> print(x) [ 1. 2. 3.] >>> type(x) <class 'numpy.ndarray'>
-
NumPy 的算术运算
>>> x = np.array([1.0, 2.0, 3.0]) >>> y = np.array([2.0, 4.0, 6.0]) >>> x + y # 对应元素的加法 array([ 3., 6., 9.]) >>> x - y array([ -1., -2., -3.]) >>> x * y # element-wise product array([ 2., 8., 18.]) >>> x / y array([ 0.5, 0.5, 0.5])
数组x和数组y的元素个数是相同的(两者均是元素
个数为3的一维数组)。当x和y的元素个数相同时,可以对各个元素进行算
术运算。如果元素个数不同,程序就会报错,所以元素个数保持一致非常重要
-
NumPy的N维数组
NumPy不仅可以生成一维数组(排成一列的数组),也可以生成多维数组。
比如,可以生成如下的二维数组(矩阵)。
>>> A = np.array([[1, 2], [3, 4]]) >>> print(A) [[1 2] [3 4]] >>> A.shape (2, 2) >>> A.dtype dtype('int64')
-
广播
NumPy中,形状不同的数组之间也可以进行运算。在这个运算中,如图1-2所示,一维数组B被“巧妙地”变成了和二位数组A相同的形状,然后再以对应元素的方式进行运算。
-
-
Matplotlib
-
绘制简单图形
import numpy as np import matplotlib.pyplot as plt # 生成数据 x = np.arange(0, 6, 0.1) # 以0.1为单位,生成0到6的数据 y = np.sin(x) # 绘制图形 plt.plot(x, y) plt.show()
这里使用NumPy的arange方法生成了[0, 0.1, 0.2, ……, 5.8, 5.9]的
数据,将其设为x。对x的各个元素,应用NumPy的sin函数np.sin(),将x、
y的数据传给plt.plot方法,然后绘制图形。最后,通过plt.show()显示图形。
-
pyplot的功能
使用pyplot的添加标题和x轴标签名
import numpy as np import matplotlib.pyplot as plt # 生成数据 x = np.arange(0, 6, 0.1) # 以0.1为单位,生成0到6的数据 y1 = np.sin(x) y2 = np.cos(x) # 绘制图形 plt.plot(x, y1, label="sin") plt.plot(x, y2, linestyle = "--", label="cos") # 用虚线绘制 plt.xlabel("x") # x轴标签 plt.ylabel("y") # y轴标签 plt.title('sin & cos') # 标题 plt.legend() plt.show()
-
二,感知机
2.1 感知机是什么
接收多个信号后返回一个信号。
权重(W):输入信号被送往神经元时,会被分别乘以固定的权重。感知机的多个输入信号都有各自固有的权重,这些权重发挥着控制各个信号的重要性的作用。也就是说,权重越大,对应该权重的信号的重要性就越高
阈值(θ):“神经元激活的界限”
2.2 简单逻辑电路
-
与门
与门仅在两个输入均为1时输出1,其他时候则输出0。
-
与非门和或门
与非门:仅当x1和x2同时为1时输出0,其他时候则输出1
或门:只要有一个输入信号是1,输出就为1
这里决定感知机参数的并不是计算机,而是我们人。我们看着真值表这种“训练数据”,人工考虑(想到)了参数的值。而机器学习的课题就是将这个决定参数值的工作交由计算机自动进行。学习是确定合适的参数的过程,而人要做的是思考感知机的构造(模型),并把训练数据交给计算机。3个门电路只有参数的值(权重和阈值)不同。也就是说,相同构造的感知机,只需通过适当地调整参数的值,就可以像“变色龙演员”表演不同的角色一样,变身为与门、与非门、或门
2.3 感知机的实现
b称为偏置,w1和w2称为权重。感知机会计算输入信号和权重的乘积,然后加上偏置,如果这个值大于0则输出1,否则输出0。
2.4 感知机的局限性
使用感知机可以实现与门、与非门、或门三种逻辑电路。现在我们来考虑一下异或门(XOR gate)。
异或门也被称为逻辑异或电路。
感知机是无法实现这个异或门的:因为或门是线性的,可是异或门无法使用一根直线做出分隔空间
感知机的局限性就在于它只能表示由一条直线分割的空间。
2.5多层感知机
感知机
的绝妙之处在于它可以“叠加层”
异或门可以通过图2-11所示的配置来实现。这里,x1和x2表示输入信号,y表示输出信号。x1和x2是与非门和或门的输入,而与非门和或门的输出则是与门的输入。
def XOR(x1, x2):
s1 = NAND(x1, x2)
s2 = OR(x1, x2)
y = AND(s1, s2)
return y
XOR(1, 0) # 输出1
XOR(0, 1) # 输出1
XOR(1, 1) # 输出0
异或门是一种多层结构的神经网络。这里,将最左边的一列称为第0层,中间的一列称为第1层,最右边的一列称为第2层
加了多层的感知机也称为多层感知机(multi-layered perceptron)。
小结
• 感知机是具有输入和输出的算法。给定一个输入后,将输出一个既定的值。
• 感知机将权重和偏置设定为参数。
• 使用感知机可以表示与门和或门等逻辑电路。
• 异或门无法通过单层感知机来表示。
• 使用2层感知机可以表示异或门。
• 单层感知机只能表示线性空间,而多层感知机可以表示非线性空间。
• 多层感知机(在理论上)可以表示计算机。
三,神经网络
3.1 从感知机到神经网络
我们把最左边的一列称为输入层,最右边的一列称为输出层,中间的一列称为中间层。中间层有时也称为隐藏层。
激活函数:函数会将输入信号的总和转换为输出信号,这种函数一般称为激活函数(activation function)。如“激活”一词所示,激活函数的作用在于决定如何来激活输入信号的总和。
表示神经元的○中明确显示了激活函数的计算过程,即信号的加权总和为节点a,然后节点a被激活函数h()转换成节点y。
激活函数是连接感知机和神经网络的桥梁。
3.2 激活函数
式(3.3)表示的激活函数以阈值为界,一旦输入超过阈值,就切换输出。这样的函数称为“阶跃函数”。
神经网络中经常使用的一个激活函数就是式(3.6)表示的sigmoid函数(sigmoid function)。
神经网络中用sigmoid函数作为激活函数,进行信号的转换,转换后的信号被传送给下一个神经元。
-
跃阶函数
def step_function(x): if x > 0: return 1 else: return 0
绘图
import numpy as np import matplotlib.pylab as plt def step_function(x): return np.array(x > 0, dtype=np.int) x = np.arange(-5.0, 5.0, 0.1) y = step_function(x) plt.plot(x, y) plt.ylim(-0.1, 1.1) # 指定y轴的范围 plt.show()
-
sigmoid函数的实现
def sigmoid(x): return 1 / (1 + np.exp(-x))
绘图
x = np.arange(-5.0, 5.0, 0.1) y = sigmoid(x) plt.plot(x, y) plt.ylim(-0.1, 1.1) # 指定y轴的范围 plt.show()
-
比较
首先注意到的是“平滑性”的不同。sigmoid函数是一条平滑的曲线,输出随着输入发生连续性的变化。而阶跃函数以0为界,输出发生急剧性的变化。sigmoid函数的平滑性对神经网络的学习具有重要意义。
另一个不同点是,相对于阶跃函数只能返回0或1,sigmoid函数可以返回0.731 ...、0.880 ...等实数(这一点和刚才的平滑性有关)。也就是说,感知机中神经元之间流动的是0或1的二元信号,而神经网络中流动连续的实数值信号。
两者均为非线性函数
-
非线性函数
阶跃函数和sigmoid函数还有其他共同点,就是两者均为非线性函数。sigmoid函数是一条曲线,阶跃函数是一条像阶梯一样的折线,两者都属于非线性的函数。
神经网络的激活函数必须使用非线性函数。因为使用线性函数的话,加深神经网络的层数就没有意义了.线性函数的问题在于,不管如何加深层数,总是存在与之等效的“无隐藏层的神经网络”。因此,为了发挥叠加层所带来的优势,激活函数必须使用非线性函数
-
ReLU函数
def relu(x): return np.maximum(0, x)
3.3 多维数组的运算
-
简单地讲,多维数组就是“数字的集合”,数字排成一列的集合、排成长方形的集合、排成三维状或者(更加一般化的)N维状的集合都称为多维数组。
这里生成了一个3 × 2的数组B。3 × 2的数组表示第一个维度有3个元素,第二个维度有2个元素。另外,第一个维度对应第0维,第二个维度对应第1维(Python的索引从0开始)。二维数组也称为矩阵(matrix)。如图3-10所示,数组的横向排列称为行(row),纵向排列称为列(column)
>>> B = np.array([[1,2], [3,4], [5,6]]) >>> print(B) [[1 2] [3 4] [5 6]] >>> np.ndim(B) 2 >>> B.shape (3, 2)
-
矩阵乘法
比如2 × 2的矩阵,其乘积可以像图3-11这样进行计算
>>> A = np.array([[1,2], [3,4]]) 3.3 多维数组的运算 53 >>> A.shape (2, 2) >>> B = np.array([[5,6], [7,8]]) >>> B.shape (2, 2) >>> np.dot(A, B) array([[19, 22], [43, 50]])
这里需要注意的是矩阵的形状(shape)。具体地讲,矩阵A的第1维的元素个数(列数)必须和矩阵B的第0维的元素个数(行数)相等。
使用np.dot(多维数组的点积),可以一次性计算出Y 的结果。
3.4 3层神经网络的实现
图3-17中增加了表示偏置的神经元“1”。请注意,偏置的右下角的索引号只有一个。这是因为前一层的偏置神经元(神经元“1”)只有一个A 。为了确认前面的内容,现在用数学式表示 。
通过加权信号和偏置的和按如下方式进行计算。
此外,如果使用矩阵的乘法运算,则可以将第1层的加权和表示成下面的式(3.9)。
A(1)= XW(1) + B(1)
X = np.array([1.0, 0.5]) W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]]) B1 = np.array([0.1, 0.2, 0.3]) print(W1.shape) # (2, 3) print(X.shape) # (2,) print(B1.shape) # (3,) A1 = np.dot(X, W1) + B1
下面,我们来实现第1层到第2层的信号传递(图3-19)
W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]]) B2 = np.array([0.1, 0.2]) print(Z1.shape) # (3,) print(W2.shape) # (3, 2) print(B2.shape) # (2,) A2 = np.dot(Z1, W2) + B2 Z2 = sigmoid(A2)
def identity_function(x): return x W3 = np.array([[0.1, 0.3], [0.2, 0.4]]) B3 = np.array([0.1, 0.2]) A3 = np.dot(Z2, W3) + B3 Y = identity_function(A3) # 或者Y = A3
输出层所用的激活函数,要根据求解问题的性质决定。一般地,回归问题可以使用恒等函数,二元分类问题可以使用 sigmoid函数,多元分类问题可以使用 softmax函数。
-
3.5 输出层的设计
神经网络可以用在分类问题和回归问题上,不过需要根据情况改变输出层的激活函数。一般而言,回归问题用恒等函数,分类问题用softmax函数。
机器学习的问题大致可以分为分类问题和回归问题。分类问题是数据属于哪一个类别的问题。比如,区分图像中的人是男性还是女性的问题就是分类问题。而回归问题是根据某个输入预测一个(连续的)数值的问题。比如,根据一个人的图像预测这个人的体重的问题就是回归问题(类似“57.4kg”这样的预测)。
-
恒等函数和 softmax函数
恒等函数会将输入按原样输出,对于输入的信息,不加以任何改动地直接输出。
分类问题中使用的softmax函数可以用下面的式(3.10)表示
式(3.10)表示假设输出层共有n个神经元,计算第k个神经元的输出y**k。如式(3.10)所示,softmax函数的分子是输入信号a**k的指数函数,分母是所有输入信号的指数函数的和。
>>> a = np.array([0.3, 2.9, 4.0]) >>> >>> exp_a = np.exp(a) # 指数函数 >>> print(exp_a) [ 1.34985881 18.17414537 54.59815003] >>> >>> sum_exp_a = np.sum(exp_a) # 指数函数的和 >>> print(sum_exp_a) 74.1221542102 >>> >>> y = exp_a / sum_exp_a >>> print(y) [ 0.01821127 0.24519181 0.73659691]
-
实现 softmax函数时的注意事项
softmax函数的实现中要进行指数函数的运算,但是此时指数函数的值很容易变得非常大。比如,e 10的值会超过20000,e 100会变成一个后面有40多个0的超大值,e 1000的结果会返回
一个表示无穷大的inf。如果在这些超大值之间进行除法运算,结果会出现“不确定”的情况。
softmax函数的实现可以像式(3.11)这样进行改进。
通过减去输入信号中的最大值(上例中的c),我们发现原本为nan(not a number,不确定)的地方,现在被正确计算了。综上,我们可以像下面这样实现softmax函数。
def softmax(a): c = np.max(a) exp_a = np.exp(a - c) # 溢出对策 sum_exp_a = np.sum(exp_a) y = exp_a / sum_exp_a return y
-
softmax函数的特征
oftmax函数的输出是0.0到1.0之间的实数。并且,softmax函数的输出值的总和是1。输出总和为1是softmax函数的一个重要性质。正因为有了这个性质,我们才可以把softmax函数的输出解释为“概率”。
一般而言,神经网络只把输出值最大的神经元所对应的类别作为识别结果。并且,即便使用softmax函数,输出值最大的神经元的位置也不会变。因此,神经网络在进行分类时,输出层的softmax函数可以省略。