第二章 感知机
感知机:神经网络初步
学习是确定合适的参数的过程。
相同构造的感知机,仅需调整参数的值,就可以实现与非门和或非门。
w1、w2:控制输入信号的重要性的参数
b:调整神经元被激活的容易程度
为什么叫激活呢?因为感知机可以看成一个求和函数,当输入的加权和超过某一阈值时,阶跃函数才能有输出。
但是,感知机无法实现异或门。
局限性:单层感知机只能表示由一条直线分割的空间。
多层感知机
多层感知机可以表示非线性空间,可以实现异或门。
2层感知机(严格地说是激活函数使用了非线性的sigmod函数的感知机)可以表示任意函数。
(不过设计两层感知机的权重是一件很累的事,一般都是通过设计门控电路组成逻辑单元,再组成CPU)
总结
单层感知机只能表示线性空间,多层感知机可以表示非线性空间。
第三章 神经网络
自动学习感知机中的参数,就是神经网络。
神经元=节点
激活函数:连接感知机和神经网络的桥梁
激活函数决定如何来激活输入信号的总和。
就是把感知机中的阶跃函数换成其他的非线性函数。
阶跃函数的实现
def step_fuction(x):
y = x>0
return y.astype(np.int)
上面这个函数可以输入NumPy数组。y是一个布尔值,然后用astype()转换数组类型为int
>>> import numpy as np
>>> x = np.array([-1.0, 1.0, 2.0])
>>> x
array([-1., 1., 2.])
>>> y = x>0
>>> y
array([False, True, True])
sigmod函数的实现
def sigmod(x):
return 1 / (1 + np.exp(-x))
x为NumPy数组时也能用(广播功能)。
另:def回车之后还要输入tab才可以
>>> import numpy as np
>>> t = np.array([1.0, 2.0, 3.0])
>>> 1.0 + t
array([2., 3., 4.])
>>> 1.0/t
array([1. , 0.5 , 0.33333333])
注意,第一步运算之后并没有给t重新赋值,所以第二步1/t,t还是原来那个数组。
画图:
>>> import matplotlib.pylab as plt
>>> x = np.arange(-5.0, 5.0, 0.1)
>>> y = sigmod(x)
>>> plt.plot(x,y)
[<matplotlib.lines.Line2D object at 0x00000228C1379340>]
>>> plt.ylim(-0.1,1.1)
(-0.1, 1.1)
>>> plt.show()
当输入信号为重要信息时,阶跃函数和sigmod函数都会输出较大的值;输入信号为不重要的信息时,两者都输出较小的值。
ReLU函数的实现
relu:
h(x)={xif x>00if x≤0
h(x) = \begin{cases}
x & \text{if } x > 0 \\
0 & \text{if } x \leq 0
\end{cases}
h(x)={x0if x>0if x≤0
>>> def relu(x):
... return np.maximum(0, x)
多维数组
多维数组
>>> import numpy as np
>>> A = np.array([1,2,3,4])
>>> np.ndim(A) #数组维数
1
>>> A.shape #数组形状(返回一个元组tuple)
(4,)
>>> A.shape[0]
4
>>> B = np.array([[1,2],[3,4],[5,6]]) #注意中括号数量
>>> np.ndim(B)
2
>>> B.shape
(3, 2) #3*2数组,第一个维度有三个元素,第二个维度有两个元素。(第一个维度是0维,第二个是1维)
缩进:[1,2]里不能有缩进,外面可以
矩阵乘法
>>> A = np.array([[1,2], [3,4]])
>>> B = np.array([[5,6], [7,8]])
>>> np.dot(A, B)
array([[19, 22],
[43, 50]])
注意:np.dot(A, B)$ \neq \$np.dot(B,A)
神经网络的实现
>>> X = np.array([1, 2])
>>> W = np.array([[1,3,5],[2,4,6]])
>>> Y = np.dot(X,W)
>>> print(Y)
[ 5 11 17]
第一层
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])
A1 = np.dot(X, W1) + B1
Z1 = sigmoid(A1)
第二层
W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
B2 = np.array([0.1, 0.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,输出层的激活函数。这个函数没必要设,只是为了和前面的流程保持统一
输出层的激活函数用 σ() 表示,隐 藏层的激活函数 h()
σ() 要根据求解问题的性质决定。
总结
def init_network():
network = {}
network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
network['b1'] = np.array([0.1, 0.2, 0.3])
network['W2'] = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
network['b2'] = np.array([0.1, 0.2])
network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]])
network['b3'] = np.array([0.1, 0.2])
return network
def forward(network, x):
W1, W2, W3 = network['W1'], network['W2'], network['W3']
b1, b2, b3 = network['b1'], network['b2'], network['b3']
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
z2 = sigmoid(a2) a3 = np.dot(z2, W3) + b3
y = identity_function(a3)
return y
network = init_network()
x = np.array([1.0, 0.5])
y = forward(network, x)
print(y) # [ 0.31682708 0.69627909]
输出层处理
一般而言,回归问题用恒等函数,分类问题用 softmax 函数。
恒等函数
softmax 函数
分类问题中,用于输出层的激活函数(输出是“概率”,数值在0-1之间)
yk=exp(ak)∑i=1nexp(ai) y_k = \frac{\exp(a_k)}{\sum_{i=1}^{n} \exp(a_i)} yk=∑i=1nexp(ai)exp(ak)
输出层共有 n 个神经元,计算第 k 个神经元的输出 yk
>>> a = np.array([0.3, 2.9, 4.0])
>>> exp_a = np.exp(a) # 指数函数 [ 1.34985881 18.17414537 54.59815003]
>>> sum_exp_a = np.sum(exp_a) # 指数函数的和 74.1221542102
>>> y = exp_a / sum_exp_a
>>> print(y) #[ 0.01821127 0.24519181 0.73659691]
def softmax(a):
exp_a = np.exp(a)
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
softmax 函数溢出问题
指数就会容易数据过大而导致溢出。
这里的 C’可以使用任何值,但是为了防止溢出,一般会使用输入信号中的最大值。
yk=exp(ak)∑i=1nexp(ai)=Cexp(ak)C∑i=1nexp(ai)=exp(ak+logC)∑i=1nexp(ai+logC)=exp(ak+C′)∑i=1nexp(ai+C′)
\begin{align*}
y_k &= \frac{\exp(a_k)}{\sum_{i=1}^{n} \exp(a_i)} = \frac{C \exp(a_k)}{C \sum_{i=1}^{n} \exp(a_i)} \\
&= \frac{\exp(a_k + \log C)}{\sum_{i=1}^{n} \exp(a_i + \log C)} \\
&= \frac{\exp(a_k + C')}{\sum_{i=1}^{n} \exp(a_i + C')}
\end{align*}
yk=∑i=1nexp(ai)exp(ak)=C∑i=1nexp(ai)Cexp(ak)=∑i=1nexp(ai+logC)exp(ak+logC)=∑i=1nexp(ai+C′)exp(ak+C′)
>>> a = np.array([1010, 1000, 990])
>>> np.exp(a) / np.sum(np.exp(a)) # softmax 函数的运算
array([ nan, nan, nan]) # 没有被正确计算
>>> c = np.max(a) # 1010
>>> a - c array([ 0, -10, -20])
>>> np.exp(a - c) / np.sum(np.exp(a - c))
array([ 9.99954600e-01, 4.53978686e-05, 2.06106005e-09])
softmax 函数特征
softmax 函数的输出是 0.0 到 1.0 之间的实数。并且,softmax 函数的输出值的总和是 1。
正 因为有了这个性质,我们才可以把 softmax 函数的输出解释为“概率”。
最终的输出y可以解释成
y[0] 的概率是 0.018(1.8 %),y[1] 的概率 是 0.245(24.5 %),y[2] 的概率是 0.737(73.7 %)。
从概率的结果来看,可以 说“因为第 2 个元素的概率最高,所以答案是第 2 个类别”
不过,实际上, a 的各元素的大小关系和 y 的各元素的大小关系并没有改变。所以在实际的问题中的推理阶段, 输出层的 softmax 函数 一般会被省略。
手写数字识别——推理
数据集:MNIST 手写数字图像集
图像数据:28 像素 × 28 像素的灰度图像,取值0-255,每个都有标签
正规化:在提高学习性能方面很有效
batch 批:图像打包一起输入的方式,就如同纸币一样扎成一捆。批处理一次性计算大型数组要比分开逐步计算 各个小型数组速度更快。
x, t = get_data() network = init_network()
batch_size = 100 # 批数量
accuracy_cnt = 0
for i in range(0, len(x), batch_size):
x_batch = x[i:i+batch_size]
y_batch = predict(network, x_batch)
p = np.argmax(y_batch, axis=1)
accuracy_cnt += np.sum(p == t[i:i+batch_size])
print("Accuracy:" + str(float(accuracy_cnt) / len(x)))
range(start, end, step) 这样指定 3 个整数,则生成的列表中的 下一个元素会增加 step 指定的值。
>>> list( range(0, 10, 3) )
[0, 3, 6, 9]
tch_size):
x_batch = x[i:i+batch_size]
y_batch = predict(network, x_batch)
p = np.argmax(y_batch, axis=1)
accuracy_cnt += np.sum(p == t[i:i+batch_size])
print(“Accuracy:” + str(float(accuracy_cnt) / len(x)))
range(start, end, step) 这样指定 3 个整数,则生成的列表中的 下一个元素会增加 step 指定的值。
list( range(0, 10, 3) )
[0, 3, 6, 9]
``