图片与矩阵
我们的图片(位图)可通过三维矩阵叠加而成也就是三个只有R、G、B的矩阵叠加,而每个矩阵每个值为[0, 255]之间来进行储存。
也就是说我们可以通过将图片数字化,使得计算机也具有对其进行处理的能力,目前机器视觉使用最广泛也最成熟的技术为“神经网络”。
神经元模型
神经元接收来自n个其它神经元传递过来的输入信号,这些信号的表达,通常通过神经元之间连接的权重(weight)大小来表示,神经元将接收到的输入值按照某种权重叠加起来,并将当前神经元的阈值进行比较,然后通过“激活函数(activation function)”向外表达输出。
激活函数
神经网络要引入激活函数来给神经网络增加一些非线性的特性。
比如最常见的Sigmoid函数和ReLU函数
Sigmoid:f(x)=1+e−x1ReLU:f(x)={x0x≥0x<0
其图像为
神经元的简单应用实例
- 单个神经元可实现与或非
- 再加一个神经元即可实现异或
所以通过网络的叠加即可实现更加复杂的分类预测问题
神经网络模型的建立
神经网络中成千上万的参数靠我们手工调试很明显是不可能的,所以引入一种能通过数据集自动获取适合参数的算法——误差逆传播
但在这之前我们先介绍其他的一些算法
前向传播
既是f(x⋅w)获得隐含层的值,同样然后再乘以下一层的权重,经过激活函数,获得输出层的最终结果。
反向传播
反向传播是神经网络最巧妙的地方,通过反向算法,神经网络实现了无需人们手动调参,自动“学习”到恰当的参数。
损失函数
用来表示预测值和实际值的“差距”,常用的表示误差的方式有很多,比如最常见的均方误差
Ek=21∑(y^−y)2
也就是预测值减去实际值的平方,前面的21是是为了求导更加方便。
更新参数
我们通过误差来更新我们每层神经网络的权重
v←v+Δv
梯度下降算法
Δwhj=−η∂wwhj∂Ek
其中η是学习率。
假设我们的损失函数图像如下图所示,我们可以通过梯度下降算法逐步逼近最小值
一个简单的实例——手写数字识别
这里使用的手写数字既是刚开始我们所讲的图片储存方法,只是他没有颜色通道(RGB)只用一个通道28*28个像素点的灰度值[0:255]
例如绘制出来如图所示
这是所有的训练集,共100个数据。
其中一个样本如图所示,第一个数字为实际值,后面的数字就都是图片信息了。
算法难点
这里使用最简单的神经网络模型,也就是只有一层隐含层。且不考虑阈值上一层到下一层只需要乘以权重w
前向传播很简单便可以实现,难点便是反向传播中参数的更迭
在这里我们使用上面提到的均方误差作为损失函数
也就是
E(w)=21k=outputs∑(tk−ok)2
权重影响
通过权重来确定改变量
偏导数
为了确定某个权重wji对误差的影响,我们可以使用偏导数
∂wji∂E
在运用上我们学过的链式求导法则
∂wji∂E=∂netj∂E⋅∂wji∂netj
我们知道net和w的关系是net=wij⋅xji所以
∂wji∂netj=xji
所以
∂wji∂E=∂netj∂E⋅xji
对于输出层单元
∂netj∂E=∂oj∂E∂netj∂oj
这里我们使用的激活函数是上面提到的Sigmoid函数,他的一个特点是求导
f′(x)=f(x)(1−f(x))
所以
∂netj∂E=−(tj−oj)0j(1−oj)
对于隐层层单元
我们只需
∂netj∂E=∂netk∂E∂netj∂netk
其中
∂netj∂netk=∂σj∂netk∂netj∂σj=wwj⋅0j(1−oj)
所以
∂netj∂E=−oj(1−oj)k∈outputs∑δkwkj
其中
δ=(tj−oj)0j(1−oj)
最后得到权值变化量
Δwji=−η∂wji∂E=−η∂netj∂E⋅xji
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| import matplotlib.pyplot as plt import numpy as np import scipy.special
class Network:
def __init__(self, input_nodes, hidden_nodes, output_nodes, learning_rate): self.input_nodes = input_nodes self.hidden_nodes = hidden_nodes self.output_nodes = output_nodes
self.learning_rate = learning_rate
self.w_i_h = np.random.normal(0.0, pow(self.hidden_nodes, -0.5), (self.hidden_nodes, self.input_nodes)) self.w_h_o = np.random.normal(0.0, pow(self.output_nodes, -0.5), (self.output_nodes, self.hidden_nodes))
self.activation_function = lambda x: scipy.special.expit(x) pass
def train(self, inputs_list, targets_list): inputs = np.array(inputs_list, ndmin=2).T targets = np.array(targets_list, ndmin=2).T
hidden_inputs = np.dot(self.w_i_h, inputs) hidden_outputs = self.activation_function(hidden_inputs)
final_inputs = np.dot(self.w_h_o, hidden_outputs) final_outputs = self.activation_function(final_inputs)
output_errors = targets - final_outputs hidden_errors = np.dot(self.w_h_o.T, output_errors)
self.w_h_o += self.learning_rate * np.dot((output_errors * final_outputs * (1 - final_outputs)), np.transpose(hidden_outputs)) self.w_i_h += self.learning_rate * np.dot((hidden_errors * hidden_outputs * (1 - hidden_outputs)), np.transpose(inputs)) pass
def query(self, input_list): inputs = np.array(input_list, ndmin=2).T hidden_inputs = np.dot(self.w_i_h, inputs) hidden_outputs = self.activation_function(hidden_inputs) final_inputs = np.dot(self.w_h_o, hidden_outputs) final_outputs = self.activation_function(final_inputs)
return final_outputs
input_nodes = 784 hidden_nodes = 150 output_nodes = 10 learning_rate = 0.1
n = Network(input_nodes, hidden_nodes, output_nodes, learning_rate)
training_data_file = open('./data/mnist_train_100.csv', 'r') training_data_list = training_data_file.readlines() training_data_file.close()
epochs = 5
for e in range(epochs): for record in training_data_list: all_values = record.split(',') inputs = (np.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01 targets = np.zeros(output_nodes) + 0.01 targets[int(all_values[0])] = 0.99 n.train(inputs, targets) pass pass
|
预测
1 2 3 4 5 6 7 8 9 10
| def prediction_data(all_data): all_data = all_data.split(',') pic = np.asfarray(all_data[1:]).reshape(28, 28) plt.imshow(pic) plt.show() inputs = (np.asfarray(all_data[1:]) / 255.0 * 0.99) + 0.01 outputs = n.query(inputs) num_index = np.argmax(outputs) print("这个数字有" + str(outputs[num_index]) + "的概率为: " + str(num_index) + " 正确答案为: " + str(all_data[0])) pass
|
1 2 3 4 5
| text_data_file = open('./data/mnist_test_10.csv', 'r') text_data = text_data_file.readlines() text_data_file.close()
prediction_data(text_data[3])
|
不过也有预测失误的时候(训练集太小了,100个训练集)。而且这个长得也很像是9
框架
伴随着模型的网络层数的增加,使用手动推导梯度也会变得非常艰难,所以我们可以使用一些框架自动获取梯度,只需定义前向自动后向传播,模型的建立也就变得像搭积木一样。
主流框架有Tensorflow、Pytorch
比如使用pytorch框架创建一个卷积模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import torch.nn as nn import torch.nn.functional as F
class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(3, 6, 5) self.pool = nn.MaxPool2d(2, 2) self.conv2 = nn.Conv2d(6, 16, 5) self.fc1 = nn.Linear(16*5*5, 120) self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, 10) def forward(self, x): x = self.pool(F.relu(self.conv1(x))) x = self.pool(F.relu(self.conv2(x))) x = x.view(-1, 16*5*5) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return x
net = Net()
|
然后加上损失函数和优化器 训练即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import torch.optim as optim
criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
for epoch in range(2): running_loss = 0.0 初始化loss
for i, data in enumerate(trainloader, 0): inputs, lables = data
optimizer.zero_grad()
outputs = net(inputs) loss = criterion(outputs, lables) loss.backward() optimizer.step()
running_loss += loss.item() if i % 2000 == 1999: print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss/2000)) running_loss = 0.0
print('finish training')
|