这两天为了找工作,焦头烂额,越查别人的面试经越深知自己实力不济,基础不牢,毕竟半路出家,在基础上会有很多大不如人的地方。。
没办法,反正选择了这条路,只能一头扎进去了,大不了读个图像的博(实在是不想)
想来想去,还是来更个博,一来在总结中学习的可以稍微扎实一点,二来我的这个辣鸡博客也写简历上了,能稍微多加一点是一点。。
这篇博客里,我会总结一下各种深度学习+图像处理的算法,可能细节部分会有所遗失,希望有人能在下面补充啦。。
首先第一项是图像识别,也就是给一张图片,输入到一个网络里,然后会输出结果,这个结果就是整个网络对这张图片识别的结果。这里网络主要是使用CNN网络。
在说CNN网络之前,要先提一下BP(back propagation)神经网络。BP神经网络是86年由D.E.Ru melhart提出的。如名,这种神经网络是具有反向传播能力的,它可以对组成前向多层网络的各人工神经元之间的连接权值进行不断的修改,从而使该前向多层网络能够将输入它的信息变换成所期望的输出信息。之所以将其称作为反向学习算法,是因为在修改各人工神经元的连接权值时,所依据的是该网络的实际输出与其期望的输出之差,将这一差值反向一层一层的向回传播,来决定连接权值的修改。
这种网络的结构是一个前向的多层网络,学习过程是由信号的正向传播与误差的反向传播两个过程完成。这种过程一直进行到网络输出的误差减小到可以接受的程度或者到了预定学习次数为止。各节点的传输函数一般用sigmoid函数。训练之后,一般得到的是每一层之间的权重矩阵。
这个误差一般用什么来表示呢?一说是用损失(loss)函数,另一说是用代价(cost)函数,还有一说是目标(object)函数。实际上代价函数可以认为是所有损失函数的加总,代价函数越小,说明结构鲁棒性就越好。还有一个概念要先提一下,是风险(risk)函数。风险函数是损失函数的期望值,而由于分布是未知的,所以无法计算。不过我们有训练集,这个训练集的损失函数期望叫做经验风险(experiment risk)。
在拟合过程中,经验风险函数最小并不是最好的状态,由于过度学习历史数据的关系,在真正预测时效果会很不好,这种情况称之为过拟合(over fitting)。这种结果的原因一般都是因为拟合的函数过于复杂,使结构风险增大。
结构风险也就是函数的复杂度有另一个函数来度量,在机器学习中称为正则化(regularization)。一般常用的有L1,L2范数。我们最终需要优化的结果,就是结构风险加经验风险,也就是上面所说的目标函数。
说一下L1,L2范数。对于线性回归模型,L1正则化的模型叫做lasso回归,L2正则化的模型叫做Ridge回归(岭回归)。区别在于:
L1正则化是指权值向量w中各个元素的绝对值之和,通常表示为||w||1;
L2正则化是指权值向量w中各个元素的平方和然后再求平方根(可以看到Ridge回归的L2正则化项有平方符号),通常表示为||w||2。
L1正则化容易产生稀疏权值矩阵,即产生稀疏模型,进而可以用于特征的选择;而L2正则化不容易产生稀疏权值,他们的作用都是防止模型过拟合。
(原理层面有一篇文章说的非常好,给出链接:
https://blog.csdn.net/zouxy09/article/details/24971995)
说完了目标函数的问题,接下来就说如何通过反向传播来改变目标函数值。
简要来讲,就是首先计算输出层的误差,然后计算隐含层的误差,推导出参数变化率,再向着梯度的反方向更新参数。
更细节的部分由于时间问题,不在上边摆了,推荐两篇博客:
https://blog.csdn.net/u014303046/article/details/78200010(数学推导)
https://blog.csdn.net/mao_xiao_feng/article/details/53048213(推导例子)
如果你看完了这两篇博客,那应该对bp神经算法有所了解了。
bp神经算法还有各种改进版本,最为人所知的就是增加动量项:
标准BP算法在调整权值时,只按t时刻误差的梯度下降方向调整,而没有考虑t时刻以前的梯度方向,从而常使训练过程发生振荡,收敛缓慢。为了提高训练速度,可以在权值调整公式中加一动量项。大多数BP算法中都增加了动量项,以至于有动量项的BP算法成为一种新的标准算法。
还有一些改进方式,比如学习速度可变(VLBP)、学习速度自适应调节、引入陡度因子等。这方面了解不多,以后可能会单独拿出来总结www
(另:听说有的面试岗位还要写bp的代码实现还有svm的代码实现,惊了,对我来说还是有难度,在这里写一下,权当预习)
import math
import random
import string
random.seed(0)
# 生成区间[a, b)内的随机数
def rand(a, b):
return (b-a)*random.random() + a
# 生成大小 I*J 的矩阵,默认零矩阵 (当然,亦可用 NumPy 提速)
def makeMatrix(I, J, fill=0.0):
m = []
for i in range(I):
m.append([fill]*J)
return m
# 函数 sigmoid,这里采用 tanh,因为sigmoid梯度下降的好慢啊(不知道为啥)
def sigmoid(x):
return math.tanh(x)
# 函数 sigmoid 的派生函数, 为了得到输出 (即:y)(另,如上面使用sigmoid,此处应该是x*(1-x))
def dsigmoid(y):
return 1.0 - y**2
class NN:
''' 三层反向传播神经网络 '''
def __init__(self, ni, nh, no):
# 输入层、隐藏层、输出层的节点(数)
self.ni = ni + 1 # 增加一个偏差节点
self.nh = nh
self.no = no
# 激活神经网络的所有节点(向量)
self.ai = [1.0]*self.ni
self.ah = [1.0]*self.nh
self.ao = [1.0]*self.no
# 建立权重(矩阵)
self.wi = makeMatrix(self.ni, self.nh)
self.wo = makeMatrix(self.nh, self.no)
# 权重设为随机值
for i in range(self.ni):
for j in range(self.nh):
self.wi[i][j] = rand(-0.2, 0.2)
for j in range(self.nh):
for k in range(self.no):
self.wo[j][k] = rand(-2.0, 2.0)
# 最后建立偏置(矩阵)
self.ci = makeMatrix(self.ni, self.nh)
self.co = makeMatrix(self.nh, self.no)
def update(self, inputs):
if len(inputs) != self.ni-1:
raise ValueError('与输入层节点数不符!')
# 激活输入层
for i in range(self.ni-1):
#self.ai[i] = sigmoid(inputs[i])
self.ai[i] = inputs[i]
# 激活隐藏层
for j in range(self.nh):
sum = 0.0
for i in range(self.ni):
sum = sum + self.ai[i] * self.wi[i][j]
self.ah[j] = sigmoid(sum)
# 激活输出层
for k in range(self.no):
sum = 0.0
for j in range(self.nh):
sum = sum + self.ah[j] * self.wo[j][k]
self.ao[k] = sigmoid(sum)
return self.ao[:]
def backPropagate(self, targets, N, M):
''' 反向传播 '''
if len(targets) != self.no:
raise ValueError('与输出层节点数不符!')
# 计算输出层的误差
output_deltas = [0.0] * self.no
for k in range(self.no):
error = targets[k]-self.ao[k]
output_deltas[k] = dsigmoid(self.ao[k]) * error
# 计算隐藏层的误差
hidden_deltas = [0.0] * self.nh
for j in range(self.nh):
error = 0.0
for k in range(self.no):
error = error + output_deltas[k]*self.wo[j][k]
hidden_deltas[j] = dsigmoid(self.ah[j]) * error
# 更新输出层权重
for j in range(self.nh):
for k in range(self.no):
change = output_deltas[k]*self.ah[j]
self.wo[j][k] = self.wo[j][k] + N*change + M*self.co[j][k]
self.co[j][k] = change
#print(N*change, M*self.co[j][k])
# 更新输入层权重
for i in range(self.ni):
for j in range(self.nh):
change = hidden_deltas[j]*self.ai[i]
self.wi[i][j] = self.wi[i][j] + N*change + M*self.ci[i][j]
self.ci[i][j] = change
# 计算误差
error = 0.0
for k in range(len(targets)):
error = error + 0.5*(targets[k]-self.ao[k])**2
return error
def test(self, patterns):
for p in patterns:
print(p[0], '->', self.update(p[0]))
def weights(self):
print('输入层权重:')
for i in range(self.ni):
print(self.wi[i])
print()
print('输出层权重:')
for j in range(self.nh):
print(self.wo[j])
def train(self, patterns, iterations=1000, N=0.05, M=0.1):
# N: 学习速率(learning rate)
# M: 动量因子(momentum factor)
for i in range(iterations):
error = 0.0
for p in patterns:
inputs = p[0]
targets = p[1]
self.update(inputs)
error = error + self.backPropagate(targets, N, M)
if i % 100 == 0:
print('误差 %-.5f' % error)
def demo():
# 教神经网络学习逻辑异或(XOR
pat = [
[[0,0], [0]],
[[0,1], [1]],
[[1,0], [1]],
[[1,1], [0]]
]
# 创建一个神经网络:输入层有两个节点、隐藏层有两个节点、输出层有一个节点
n = NN(2, 2, 1)
# 用一些模式训练它
n.train(pat)
# 测试训练的成果
n.test(pat)
# 看看训练好的权重
n.weights()
if __name__ == '__main__':
demo()
记下来也不是一件简单的事呀。。
好吧,算这一块pass了,日后再来复习。
然后说到最常用的是CNN(卷积神经网络)。卷积代码实现倒是蛮简单的,主要来说一下不同结构之间的区别,优劣,以及不同层的作用等等,想到哪说哪。。
ps: 会不会有点长?要不要分下p?
先介绍一下及基础的吧!
这个图是发展历程,可以看到我们比较熟悉的LeNet,AlexNet,VGG,ResNet,还有后面要总结的R-CNN等等,还有一张图片是说明准确率和运算量的关系的:
终于上传图成功了,有时间我会把之前的图给补上的!(找完工作之后八)
然后这篇就到这里,我会直接跟进下一章的,主要就是讲上上面这个图,把这些神经网络的特点啊,突破啊乱七八糟的东西都总结一下w
鸣谢我引用的几个文章,都在文章里有链接!还有后面这张图的链接也鸣谢一下:
https://www.cnblogs.com/whenyd/p/7886637.html