神经网络基础
传统神经网络
神经网络可以视为一个满足拓扑排序的无环有向图,这张图可以按层铺开,最低的一层表示输入,入度为0,而最高的一层为输出,出度为0。两个神经元之间的边具有特定的权。
除去输入神经元和输出神经元,图中每一个神经元都连接着上一层所有神经元,同时也连接到下一层所有的神经元。每一个神经元直接获取输入或者从上一层获取输入,这些输入首先加权,然后求和,最后通过激活函数,产生本神经元的输出,如果还存在下一层神经元,那么输出会继续传递下去。
通过不断地进行以上步骤,神经网络便能够将原始输入逐层传递,最终在输出层得到输出,完成预测。
优化函数:
我们可以看出神经网络层次越多,网络越复杂,直接求解几乎不可能,因此我们直接采用随机梯度下降的方法。
也正是因此,神经网络的优化函数很简单,f=y’-y。y’代表预测值,y代表真实值。我们训练的目标便是,不断调整神经元之间的权值使得f=y’-y最小。
训练方式:
科学家们发明出一套反向自动微分的方法用于学习,它通过两次遍历神经网络进行梯度下降,第一次正向遍历,是从输入层到输入层,计算预测值,进而得到误差值,第二次反向遍历,从输入层到输出层,将误差的影响从输出层传递回输入层,并且在过程中,对神经元间的权值进行调整,以实现梯度下降。
CNN
卷积
离散卷积的数学公式可以表示为如下形式:
f(x) = - 其中C(k)代表卷积操作数,g(i)代表样本数据, f(x)代表输出结果。
举例如下:
假设g(i)是一个一维的函数,而且代表的样本数为G = [1,2,3,4,5,6,7,8,9]
假设C(k)是一个一维的卷积操作数, 操作数为C=[-1,0,1]
则输出结果f(x)可以表示为 F=[1,2,2,2,2,2,2,2,1] //边界数据未处理
以上只是一维的情况下,当对一幅二维数字图像加以卷积时,其数学意义可以解释如下:
源图像是作为输入源数据,处理以后要的图像是卷积输出结果,卷积操作数作为Filter
在XY两个方向上对源图像的每个像素点实施卷积操作。如图所示:
粉红色的方格每次在X/Y前进一个像素方格,就会产生一个新的输出像素,图中深蓝色的代
表要输出的像素方格,走完全部的像素方格,就得到了所有输出像素。
卷积运算
CNN
具有三个特性:
稀疏连接
参数共享
等变表示
目标分类:
目标探测:
循环神经网络RNN
CNN+RNN
对抗网络GAN
迁移学习:
AlexNet经典卷积网络
#!usr/bin/env python
# -*- coding:utf-8 _*-
"""
AlexNet经典卷积网络
"""
import tensorflow as tf
import time
from datetime import datetime
import math
def inference_op(images):
"""
实现前向传播
"""
# 实现第一个卷积层
with tf.name_scope('conv1'):
parameter = []
kernel = tf.Variable(tf.truncated_normal([11, 11, 3, 96], stddev=0.1), name='weights')
conv = tf.nn.conv2d(images, kernel, strides=[1, 4, 4, 1], padding='SAME')
bias = tf.Variable(tf.constant(0.0, dtype=tf.float32, shape=[96]), trainable=True, name='bias')
conv1 = tf.nn.relu(tf.nn.bias_add(conv, bias))
# 打印第一个卷积层
print(conv1.op.name, '', conv1.get_shape().as_list())
parameter += [kernel, bias]
# 添加一个LRN层和最大池化层
lrn1 = tf.nn.lrn(conv1, depth_radius=4, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name='lrn1')
pool1 = tf.nn.max_pool(lrn1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool1')
# 打印该网络结构
print(pool1.op.name, '', pool1.get_shape().as_list())
# 实现第二个卷积层
with tf.name_scope('conv2'):
kernel = tf.Variable(tf.truncated_normal([5, 5, 96, 256], stddev=0.1), name='weights')
conv = tf.nn.conv2d(pool1, kernel, strides=[1, 1, 1, 1], padding='SAME')
bias = tf.Variable(tf.constant(0.0, dtype=tf.float32, shape=[256]), trainable=True, name='bias')
conv2 = tf.nn.relu(tf.nn.bias_add(conv, bias))
# 打印第二个卷积层
print(conv2.op.name, '', conv2.get_shape().as_list())
parameter += [kernel, bias]
# 添加一个LRN层和最大池化层
lrn2 = tf.nn.lrn(conv2, depth_radius=4, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name='lrn2')
pool2 = tf.nn.max_pool(lrn2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool2')
# 打印该网络结构
print(pool2.op.name, '', pool2.get_shape().as_list())
# 实现第三个卷积层
with tf.name_scope('conv3'):
kernel = tf.Variable(tf.truncated_normal([3, 3, 256, 384], stddev=0.1), name='weights')
conv = tf.nn.conv2d(pool2, kernel, strides=[1, 1, 1, 1], padding='SAME')
bias = tf.Variable(tf.constant(0.0, dtype=tf.float32, shape=[384]), trainable=True, name='bias')
conv3 = tf.nn.relu(tf.nn.bias_add(conv, bias))
# 打印第三个卷积层
print(conv3.op.name, '', conv3.get_shape().as_list())
parameter += [kernel, bias]
# 实现第四个卷积层
with tf.name_scope('conv4'):
kernel = tf.Variable(tf.truncated_normal([3, 3, 384, 384], stddev=0.1), name='weights')
conv = tf.nn.conv2d(conv3, kernel, strides=[1, 1, 1, 1], padding='SAME')
bias = tf.Variable(tf.constant(0.0, dtype=tf.float32, shape=[384]), trainable=True, name='bias')
conv4 = tf.nn.relu(tf.nn.bias_add(conv, bias))
# 打印第四个卷积层
print(conv4.op.name, '', conv4.get_shape().as_list())
parameter += [kernel, bias]
# 实现第五个卷积层
with tf.name_scope('conv5'):
kernel = tf.Variable(tf.truncated_normal([3, 3, 384, 256], stddev=0.1), name='weights')
conv = tf.nn.conv2d(conv4, kernel, strides=[1, 1, 1, 1], padding='SAME')
bias = tf.Variable(tf.constant(0.0, dtype=tf.float32, shape=[256]), trainable=True, name='bias')
conv5 = tf.nn.relu(tf.nn.bias_add(conv, bias))
# 打印第五个卷积层
print(conv5.op.name, '', conv5.get_shape().as_list())
parameter += [kernel, bias]
# 添加最大池化层
pool5 = tf.nn.max_pool(conv5, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool5')
# 打印该网络结构
print(pool5.op.name, '', pool5.get_shape().as_list())
# 至此卷积结束,将pool5输出向量,方便全连层的输入
pool_shape = pool5.get_shape().as_list()
nodes = pool_shape[1] * pool_shape[2] * pool_shape[3]
reshaped = tf.reshape(pool5, [pool_shape[0], nodes])
# 创建第一个全连层
with tf.name_scope('fc_1'):
fc1_weights = tf.Variable(tf.truncated_normal([nodes, 4096], stddev=0.1, name='weights'))
fc1_bias = tf.Variable(tf.constant(0.0, shape=[4096]), trainable=True, name='bias')
fc_1 = tf.nn.relu(tf.matmul(reshaped, fc1_weights) + fc1_bias)
# 打印第一个全连层的信息
print(fc_1.op.name, '', fc_1.get_shape().as_list())
parameter += [fc1_weights, fc1_bias]
# 创建第二个全连层
with tf.name_scope('fc_2'):
fc2_weights = tf.Variable(tf.truncated_normal([4096, 4096], stddev=0.1, name='weights'))
fc2_bias = tf.Variable(tf.constant(0.0, shape=[4096]), trainable=True, name='bias')
fc_2 = tf.nn.relu(tf.matmul(fc_1, fc2_weights) + fc2_bias)
# 加入drop增大泛化能力
fc_2 = tf.nn.dropout(fc_2, keep_prob=0.5)
# 打印第二个全连层的信息
print(fc_2.op.name, '', fc_2.get_shape().as_list())
parameter += [fc2_weights, fc2_bias]
return fc_2, parameter
batch_size = 32
num_batch = 100
image_size = 224
"创建计算图用于运行该问题,一般不做处理为默认计算图"
g1 = tf.Graph()
with g1.as_default():
# 创建模拟的图片数据
images = tf.Variable(tf.random_normal([batch_size, image_size, image_size, 3], stddev=0.1))
# 运行前向传播过程
fc_2, parameter = inference_op(images)
# init_op = tf.global_variables_initializer()
# 配置会话,用于设置GPU的分配策略
# 采用最佳适配合算法
config = tf.ConfigProto()
config.gpu_options.allocator_type = 'BFC'
"前向传播与评测前向传播的耗时"
with tf.Session(config=config, graph=g1) as sess:
init_op = tf.global_variables_initializer()
sess.run(init_op)
num_steps_burn_in = 10
total_dural = 0.0
total_dural_squared = 0.0
back_total_dural = 0.0
back_total_dural_squared = 0.0
for i in range(num_batch + num_steps_burn_in):
start_time = time.time()
_ = sess.run(fc_2)
duration = time.time() - start_time
if i >= num_steps_burn_in:
if i % 10 == 0:
print('%s:step %d,duration = %.3f' % (datetime.now(), i - num_steps_burn_in, duration))
total_dural += duration
total_dural_squared += duration ** 2
averege_time = total_dural / num_batch
# 打印时间
print('%s:Forward across %d step, %.3f +/- %.3f sec/batch' %
(datetime.now(), num_batch, averege_time, math.sqrt(total_dural_squared / num_batch - averege_time ** 2)))
"反向传播与评测反向传播的耗时"
# 梯度优化 因为没有放入实测数据,目标损失为fc2下的L2正则损失
grad = tf.gradients(tf.nn.l2_loss(fc_2), parameter)
# 运行BP传播过程
for i in range(num_batch + num_steps_burn_in):
start_time = time.time()
_ = sess.run(grad)
duration = time.time() - start_time
if i >= num_steps_burn_in:
if i % 10 == 0:
print('%s:step %d,duration = %.3f' % (datetime.now(), i - num_steps_burn_in, duration))
back_total_dural += duration
back_total_dural_squared += duration ** 2
back_averege_t = back_total_dural / num_batch
print('%s:Forward-Backward across %d step, %.3f +/- %.3f sec/batch' %
(datetime.now(), num_batch, back_averege_t,
math.sqrt(back_total_dural_squared / num_batch - back_averege_t ** 2)))