本文为作者学习神经网络后的整理笔记,仅供学习使用!
一、概述
人工神经网络(Artificial Neural Network,即ANN),它从信息处理角度对人脑神经元网络进行抽象,建立某种简单模型,按不同的连接方式组成不同的网络。
神经网络是一种运算模型,由大量的节点(或神经元)之间相互联接构成。每个节点代表一种特定的输出函数,称为激励函数。每两个节点间的连接都代表一个对于通过该连接信号的加权值,称之为权重,这相当于人工神经网络的记忆。
二、基础概念
(1)线性分类:
假设现有一张图片为32*32*3,分别代表长*宽*颜色,转换为一维数组则为3072*1,有3组类别car、cat、flog,那么这张图片分别为这三组类别的得分为f(x, W) = Wx + b,其中,f(x, W)为[1 * 3]数组,x为[3072 * 1]数组, 那么W为[3 * 3072]的数组,b为[1 * 3]数组。 然后对得到的三组类别的得分进行比较,得分值较高的为图片的预测值。
(2)损失函数
对于某张图片的损失函数为:
如上图,
cat的损失函数为:L = max(0, 5.1 - 3.2 + 1) + max(0, -1.7 - 3.2 + 1) = 2.9 + 0 = 2.9
car的损失函数为:L = max(0, 1.3 - 4.9 + 1) + max(0, 2.0 - 4.9 + 1) = 0 + 0 = 0
frog的损失函数为:L = max(0, 2.2 + 3.1 + 1) + max(0, 2.5 + 3.1 + 1) = 6.3 + 6.6 = 12.9
总结来说,损失函数可表示为:
其中:
(3)正则化惩罚项
为得到均匀的参数结果W,需为损失函数L加上一个正则化惩罚项。现有两种正则化惩罚项:
(a)L1正则化:
(b)L2正则化:
因而,加上正则化惩罚项后,损失函数为:
(4)softmax分类器
softmax分类器的输入是一个向量,向量中的元素为任意实数的评分值,输出则是一个归一化的分类概率,也是一个向量,其中每个元素在0到1之间,且所有元素之和为1,函数表达式如下:
其损失函数为交叉熵损失:
(5)SVM和softmax比较
现有一个分类类别为[10, 9, 9],使用SVM分类的损失函数为L = max(9 - 10 + 1) + max(9 - 10 + 1) = 0,即无误差,分类比较好,然而实际上并非如此;使用softmax分类器的损失函数为[ -log(e**10 / sum), -log(e ** 9/ sum), -log(e**9/sum)], 其中,sum = e ** 10 + e ** 9 + e ** 9,这时,每个预测的分类类别的概率值之间存在明显的差异,因而,推荐使用softmax分类器进行分类。
(6)正向传播
正向传播是指从输入数据开始,到最终得到的损失值loss,这一个过程称为正向传播。
(7)反向传播
BP算法(即反向传播算法)是建立在梯度下降算法的基础上的适合于多层神经元网络的一种学习算法。反向传播算法主要由两个环节(激励传播、权重更新)反复循环迭代,直到网络中对输入的响应达到了制定的目标范围为止。
BP算法的学习过程由正向传播过程和反向传播过程组成。在正向传播过程中,输入信息通过输入层经隐含层,逐层处理并传向输出层。如果在输出层得不到期望的输出值,则取输出与期望的误差的平方和作为目标函数,转入反向传播,逐层求出目标函数对各神经元权值的偏导数,构成目标函数对权值向量的梯量,作为修改权值的依据,网络的学习在权值修改过程中完成。误差达到期望值范围后,网络学习结束。
假设现有一神经元f(x, y, z) = (x + y) * z,如下图
参数z的偏导数为:
其中,q = x + y
参数q的偏导数为:
参数x的偏导数为:
参数y的偏导数为:
总结来说:
(a)对于加法门单元,均等分配
(b)对于Max门单元,给最大的
(c)对于惩罚门单元,相互交换
(8)神经网络
人工神经网络是一种运算模型,有大量节点(或称为神经元)之间相互连接构成。每个节点代表一种特定的输出函数,称为激励函数。每两个节点之间的连接都代表一个对于通过该连接信号的加权值,称之为权重。
神经元越多,越能表达复杂的模型,但是有很大机率会出现过拟合的现象。减少过拟合的现象,选择合适的神经元数量,也可以适当调整正则化项中的学习率。
对于单层的神经网络:
对于双层的神经网络:
(9)激活函数
在神经元中,输入的Inputs通过加权、求和后,还被作用于一个函数,这个函数就是激活函数。引入激活函数是为了增加神经网络模型的非线性。没有激活函数的每层都相当于矩阵相乘。常见的激活函数有:
(a)sigmod函数
(b)ReLU函数
(10)权重初始化
权重W初始值不能为0,输入为0,经反向传播调整的权重还是为0;若权重为同一个值,则会导致权重W更新太慢了;因而推荐使用高斯初始化或者随机初始化:
W = 0.01 * np.random.randn(D, H)
(11)DROP-OUT
当每一个输入X与每一个参数W,参数W与参数之间是全连接时,容易引起过拟合现象。DROP-OUT可以有效降低过拟合的风险。DROP-OUT是指在进行一次神经网络迭代的时候,随机选择一定比例的神经元参与前向传播和反向传播,这样不仅可以降低过拟合的风险,还有助减少计算量。
三、代码示例
1、设置样本数据
import numpy as np
# number_of_per_class 每个分类的样本数量
# number_of_class 类别数量
# number_of_dimension 维数
def get_dataset(number_of_per_class = 100, number_of_class = 3, number_of_dimension = 2):
np.random.seed(0)
X = np.zeros((number_of_per_class * number_of_class, number_of_dimension))
y = np.zeros(number_of_per_class * number_of_class, dtype="uint8")
for num_of_class in np.arange(number_of_class):
list_of_int = range(number_of_per_class * num_of_class, number_of_per_class * (num_of_class + 1)) # 获取(A, B)范围内的整数集合
radius = np.linspace(0.0, 1, number_of_per_class) # 将[0.0, 1)均匀分成number_of_per_class份 (半径,radius)
theta = np.linspace(num_of_class * 4, (num_of_class + 1) * 4, number_of_per_class) + np.random.randn(number_of_per_class) * 0.2 # theta
X[list_of_int] = np.c_[radius * np.sin(theta), radius * np.cos(theta)] # 按行连接两个矩阵,就是将两个矩阵左右相加,要求行数相等,类似pandas中的Merge()
y[list_of_int] = num_of_class
return X, y
2、进行分类调参
import Util
import numpy as np
def create_model(X, y):
# 算法参数初始化
size_of_hidden_layer = 100 # 隐层的大小
Weight = 0.01 * np.random.randn(number_of_dimension, size_of_hidden_layer) # 权重初始化(使用随机初始化) X:300*2 2*100
b = np.zeros((1, size_of_hidden_layer))
Weight2 = 0.01 * np.random.randn(size_of_hidden_layer, number_of_class)
b2 = np.zeros((1, number_of_class))
# 超参数定义
step_size = 1e-0
reg = 1e-3 # 正则化强度
number_of_examples = X.shape[0]
for index_of_iter in np.arange(2000):
hidden_layer = np.maximum(0, np.dot(X, Weight) + b) # 300 * 100
scores = np.dot(hidden_layer, Weight2) + b2 # (300 * 100) * (100 * 3) = (300 * 3)
# softmax 分类器 f(x) = e ** x / sum(e ** x)
exp_scores = np.exp(scores)
probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
# 损失函数 L1-1 = - log(f(x))
current_logprobs = -np.log(probs[range(number_of_examples), y])
# 损失函数 L1 = sum(L1-1) / N
data_loss = np.sum(current_logprobs) / number_of_examples
# L2正则化惩罚 L2 = a * sum(Weight * Weight)
reg_loss = 0.5 * reg * np.sum(Weight * Weight) + 0.5 * reg * np.sum(Weight2 * Weight2)
# 损失函数 L = L1 + L2
loss = data_loss + reg_loss
if index_of_iter % 100 == 0:
print("iteration %d : loss %.2f" % (index_of_iter, loss))
# 优化方向:使用梯度下降的方式更新参数
dscores = probs
dscores[range(number_of_examples), y] -= 1
dscores /= number_of_examples
dWeight2 = np.dot(hidden_layer.T, dscores) # (300 * 100).T * (300 * 3) = (100 * 3)
db2 = np.sum(dscores, axis=0, keepdims=True) # (1 * 3)
dHidden = np.dot(dscores, Weight2.T) # (300 * 3) * (100 * 3).T = (300 * 100)
dHidden[hidden_layer <= 0] = 0
dWeight = np.dot(X.T, dHidden) # (300 * 2).T * (300 * 100) = (2 * 100)
db = np.sum(dHidden, axis=0, keepdims=True) # (1 * 100)
dWeight2 += reg * Weight2
dWeight += reg * Weight
Weight += -step_size * dWeight
b += -step_size * db
Weight2 += -step_size * dWeight2
db2 += -step_size * db2
return Weight, b, Weight2, b2
3、绘图
def draw_data_new(X, y, Weight, Weight2, b, b2):
h = 0.02
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
Z = np.dot(np.maximum(0, np.dot(np.c_[xx.ravel(), yy.ravel()], Weight) + b), Weight2) + b2
Z = np.argmax(Z, axis=1)
Z = Z.reshape(xx.shape)
fig = plt.figure()
plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral, alpha=0.8)
plt.scatter(X[:, 0], X[:, 1], c=y, s=40, cmap=plt.cm.Spectral)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
plt.show()
4、总调用程序
import Util
import numpy as np
import matplotlib.pyplot as plt
number_of_per_class = 100 # 每个分类的样本数量
number_of_class = 3 # 类别数量
number_of_dimension = 2 # 维数
X, y = Util.get_dataset(number_of_per_class, number_of_class, number_of_dimension)
Weight, b, Weight2, b2 = create_model(X, y)
hidden_layer = np.maximum(0, np.dot(X, Weight) + b)
scores = np.dot(hidden_layer, Weight2) + b2
predicted_class = np.argmax(scores, axis=1)
print("training accuracy : %.2f" % (np.mean(predicted_class == y)))
Util.draw_data_new(X, y, Weight, Weight2, b, b2)