如上图所示,这里的神经网络是模拟人的神经网络的工作方式所提出的一种理念。
感知机简单来说就是在收集了数据进行处理后由感知机得出结果,就像人喝水,由温觉感受器去感觉水温,然后得出结论要不要喝?
由上图我们可以看到,当只有一个感知机时,这不就跟逻辑回归一样吗,只是逻辑回归里叫sigmod,以0.5为阈值。上图所示,与或问题好解决,只需要一个感知机就行,如与问题,当w1和w2等于1时,右上角的点的目标值是2,而我阈值假设是1.5,那么大于1.5的是一个类,小于1.5的是另一个类,那么一定能找到一条直线能将(1,1)这个点与其他三个点分开!!如果要解决异或问题,也就是相同为0,不同为1,很明显(0,0)和(1,1)为同类,(0,1)与(1,0)为同类,这时,光凭w1,w2所构成的一条直线是不能将其成功划分的,那么我们就考虑用两条线!!!这时就要增加一个感知机!!到这也许你还是很懵逼,那么我们就直观来感受一下,请点击下面链接!动态感受分类
这个网站就是感受一下如何将右图中的两种原点准确分类!
当只有一个感知机时,最后的分类情况如下:
我们再看看两个感知机的情况,很明显有两条白线,离最终分类结果又近了一步。
下面来看看三个感知机,这是中间是个三角形,也就是三条白线线将其分割成最终结果。
输出结果时,并不是直接得到该样本是属于啥类,而是会算出该样本属于所有类别的概率,取最大,这一点又和朴素贝叶斯相似。
由上图,我们回忆一下,逻辑回归针对二分类问题,也就是是与不是的问题,用的是sigmod(激活函数)。而神经网络是针对多分类问题,利用softmax来得出某样本属于各个类别的概率,关注最大概率的那个。然后策略与优化后面再说!
这个手写的1我们要怎么识别?简单来说就是把这张图数字化,变成以像素来看待的,空白地方计为零,其他地方非零。然后经过大量图片的训练后,将此图传进去,即可判断!!
再来复习一下one-hot编码:
再三强调一定要记住神经网络最后的结果是所有分类都有,然后我们利用one-hot编码来进行标记,每一横行都是一个样本,在所属分类下记为1即可。
one-hotAPI介绍
获取数据
equal_list = tf.equal(tf.argmax(y, 1), tf.argmax(y_label, 1))
accuracy = tf.reduce_mean(tf.cast(equal_list, tf.float32))
成功为1,失败为0
这里就拿识别手写数字举例!
这个数据集里的图片都是28个像素*28个像素的,也就是784个特征,None代指若干个样本,也就是若干行。然后数字是从0~9,一拱十个数字,也就是十个类别。如上图,我这就只举了一层,也就是输入之后经过一层感知机就输出了。特征值是[none,784],
目标值是[none,10],目标值为什么是10列?因为是[none,10]是用的one-hot编码,初始化w是[784,10],这样一来,[none,784]矩阵乘[784,10]=[none,10]。
示例代码:
FLAGS = tf.app.flags.FLAGS
tf.app.flags.DEFINE_integer("is_train", 1, "指定程序是预测还是训练")
def full_connected():
# 获取真实的数据
mnist = input_data.read_data_sets("./data/mnist/input_data/", one_hot=True)
# 1、建立数据的占位符 x [None, 784] y_true [None, 10]
with tf.variable_scope("data"):
x = tf.placeholder(tf.float32, [None, 784])
y_true = tf.placeholder(tf.int32, [None, 10])
# 2、建立一个全连接层的神经网络 w [784, 10] b [10]
with tf.variable_scope("fc_model"):
# 随机初始化权重和偏置
weight = tf.Variable(tf.random_normal([784, 10], mean=0.0, stddev=1.0), name="w")
bias = tf.Variable(tf.constant(0.0, shape=[10]))
# 预测None个样本的输出结果matrix [None, 784]* [784, 10] + [10] = [None, 10]
y_predict = tf.matmul(x, weight) + bias
# 3、求出所有样本的损失,然后求平均值
with tf.variable_scope("soft_cross"):
# 求平均交叉熵损失
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_true, logits=y_predict))
# 4、梯度下降求出损失
with tf.variable_scope("optimizer"):
train_op = tf.train.GradientDescentOptimizer(0.1).minimize(loss)
# 5、计算准确率
with tf.variable_scope("acc"):
##这里y_true里面依然有占位符,我们在后面的会话里给他赋值
equal_list = tf.equal(tf.argmax(y_true, 1), tf.argmax(y_predict, 1))
# equal_list None个样本 [1, 0, 1, 0, 1, 1,..........]
accuracy = tf.reduce_mean(tf.cast(equal_list, tf.float32))
# 收集变量 单个数字值收集
tf.summary.scalar("losses", loss)
tf.summary.scalar("acc", accuracy)
# 高纬度变量收集
tf.summary.histogram("weightes", weight)
tf.summary.histogram("biases", bias)
# 定义一个初始化变量的op
init_op = tf.global_variables_initializer()
# 定义一个合并变量de op
merged = tf.summary.merge_all()
# 创建一个saver
saver = tf.train.Saver()
# 开启会话去训练
with tf.Session() as sess:
# 初始化变量
sess.run(init_op)
# 建立events文件,然后写入
filewriter = tf.summary.FileWriter("./tmp/summary/test/", graph=sess.graph)
if FLAGS.is_train == 1:
# 迭代步数去训练,更新参数预测
for i in range(2000):
# 取出真实存在的特征值和目标值
mnist_x, mnist_y = mnist.train.next_batch(50)
# 运行train_op训练
sess.run(train_op, feed_dict={x: mnist_x, y_true: mnist_y})
# 写入每步训练的值
summary = sess.run(merged, feed_dict={x: mnist_x, y_true: mnist_y})
filewriter.add_summary(summary, i)
print("训练第%d步,准确率为:%f" % (i, sess.run(accuracy, feed_dict={x: mnist_x, y_true: mnist_y})))
# 保存模型
saver.save(sess, "./tmp/ckpt/fc_model")
else:
# 加载模型
saver.restore(sess, "./tmp/ckpt/fc_model")
# 如果是0,做出预测
for i in range(100):
# 每次测试一张图片 [0,0,0,0,0,1,0,0,0,0]
x_test, y_test = mnist.test.next_batch(1)
print("第%d张图片,手写数字图片目标是:%d, 预测结果是:%d" % (
i,
tf.argmax(y_test, 1).eval(),
tf.argmax(sess.run(y_predict, feed_dict={x: x_test, y_true: y_test}), 1).eval()
))
return None
全连接神经网络的缺点
也就是说当中间层多了起来,就没法搞了,于是引入下面的卷积神经网络!!!
卷积神经网络的发展历史
卷积神经网络错误率
conv是卷积层,relu是激活函数,pool是池化层。
数据变化
下面我们来举一个计算卷积层的例子:
初始要识别的图片是55的,通道数1,也就是黑白图片,然后我们设一个33的过滤器(也称之为权重),然后将过滤器放在image的左上角,然后我们知道图片的像素就是特征值,而过滤器是权重,那么当过滤器放在图片左上角时,该过滤器会压着image左上角的九个元素,也是1个33的矩阵,那么我们就可以用这个矩阵乘权重,就会得出来一个值,我们就把他填入feature map得左上角第一个位置,然后filter水平向右移动,又压着新的九个像素,我们再做乘法,把得出的结果填入feature map的第一行第二列。。。然后过滤器再向右移,到头之后又向下移,反正要把图片扫描完!!(这个例子步长为1)
下面举个例子步长为2:
道理跟步长为1的一样,最后我们要的是feature map!!也就是卷积之后的这个结果。
卷积层的零填充
我们在移动filter时,有时候不能刚好扫描完图片,那么会超出几个像素,这时有两个策略,第一个是为了不越界,我就少扫描一部分,但这样不够准确。第二个方案是零填充,就是在filter越过图片时,用零去填充图片,因为0乘任何数都为零,所以不会对最终结果造成影响。
如上图,我们来计算一下图片2828,有32个filter时(32组不同权重),每个filter33,步长为1,零填充为1,那么我们最终的输出结果是多少?H2=(28-3+2)/1+1=28,w2=28,然后高等于过滤器数,然后最终的矩阵为[28,28,32],然后对比输入的[28,28,1],就好像将[28,28,1]堆叠了32个一样,所以形象的称为卷积。
多通道图片-外围补充与多Filter
当识别的图片是彩色时,通道数为3,假设有一个filter,这里面有三个33的权重,每个33权重扫描一个图片,只要保证输出结果只有一个就行,因为一个filter对应一个输出output,那么我怎么能保证输出是一个33呢??很简单,将上图每一组权重与自己压着的33的部分相乘,会得到一个值,将这三组权重得到得这个值相加填入最终的33得feature map对应的位置!就想上图所示得样子,最终结果绿框圈起来得-1就是3组权重与分别与自己压着得3*3矩阵相乘并求和而得来的!!
卷积API
为什么要这个激活函数而不用sigmod?
如上图,这里搞了两层神经元,右边是我加了激活函数所得到的分类图片,如果不加激活函数,是达不到这个效果的,我们就记住以下结论:增加激活函数可以增加网络的非线性分割能力
如上图所示,池化就相当于特征抽取,上图44矩阵是卷积后的结果,我们知道卷积后的数据会变复杂,那么我们一定要对他进行池化处理,这样最终的输出结果会简单一些,也就是去除冗余,上图我们把44矩阵分成了四块,最终结果取得分别是四块中最大的,由它去代表自己所在块得特征。
这里我们手动设置有几个卷积层,用的图还是上面那个简单神经网络的28*28的图,具体如何设置请看下图。
实现代码:
# 定义一个初始化权重的函数
def weight_variables(shape):
w = tf.Variable(tf.random_normal(shape=shape, mean=0.0, stddev=1.0))
return w
# 定义一个初始化偏置的函数
def bias_variables(shape):
b = tf.Variable(tf.constant(0.0, shape=shape))
return b
def model():
"""
自定义的卷积模型
:return:
"""
# 1、准备数据的占位符 x [None, 784] y_true [None, 10]
with tf.variable_scope("data"):
x = tf.placeholder(tf.float32, [None, 784])
y_true = tf.placeholder(tf.int32, [None, 10])
# 2、一卷积层 卷积: 5*5*1,32个,strides=1 激活: tf.nn.relu 池化
with tf.variable_scope("conv1"):
# 随机初始化权重, 偏置[32]
w_conv1 = weight_variables([5, 5, 1, 32])
b_conv1 = bias_variables([32])
# 对x进行形状的改变[None, 784] [None, 28, 28, 1]
x_reshape = tf.reshape(x, [-1, 28, 28, 1])
# [None, 28, 28, 1]-----> [None, 28, 28, 32]
x_relu1 = tf.nn.relu(tf.nn.conv2d(x_reshape, w_conv1, strides=[1, 1, 1, 1], padding="SAME") + b_conv1)
# 池化 2*2 ,strides2 [None, 28, 28, 32]---->[None, 14, 14, 32]
x_pool1 = tf.nn.max_pool(x_relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")
# 3、二卷积层卷积: 5*5*32,64个filter,strides=1 激活: tf.nn.relu 池化:
with tf.variable_scope("conv2"):
# 随机初始化权重, 权重:[5, 5, 32, 64] 偏置[64]
w_conv2 = weight_variables([5, 5, 32, 64])
b_conv2 = bias_variables([64])
# 卷积,激活,池化计算
# [None, 14, 14, 32]-----> [None, 14, 14, 64]
x_relu2 = tf.nn.relu(tf.nn.conv2d(x_pool1, w_conv2, strides=[1, 1, 1, 1], padding="SAME") + b_conv2)
# 池化 2*2, strides 2, [None, 14, 14, 64]---->[None, 7, 7, 64]
x_pool2 = tf.nn.max_pool(x_relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")
# 4、全连接层 [None, 7, 7, 64]--->[None, 7*7*64]*[7*7*64, 10]+ [10] =[None, 10]
with tf.variable_scope("conv2"):
# 随机初始化权重和偏置
w_fc = weight_variables([7 * 7 * 64, 10])
b_fc = bias_variables([10])
# 修改形状 [None, 7, 7, 64] --->None, 7*7*64]
x_fc_reshape = tf.reshape(x_pool2, [-1, 7 * 7 * 64])
# 进行矩阵运算得出每个样本的10个结果
y_predict = tf.matmul(x_fc_reshape, w_fc) + b_fc
return x, y_true, y_predict
def conv_fc():
# 获取真实的数据
mnist = input_data.read_data_sets("./data/mnist/input_data/", one_hot=True)
# 定义模型,得出输出
x, y_true, y_predict = model()
# 进行交叉熵损失计算
# 3、求出所有样本的损失,然后求平均值
with tf.variable_scope("soft_cross"):
# 求平均交叉熵损失# 求平均交叉熵损失
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_true, logits=y_predict))
# 4、梯度下降求出损失
with tf.variable_scope("optimizer"):
train_op = tf.train.GradientDescentOptimizer(0.0001).minimize(loss)
# 5、计算准确率
with tf.variable_scope("acc"):
equal_list = tf.equal(tf.argmax(y_true, 1), tf.argmax(y_predict, 1))
# equal_list None个样本 [1, 0, 1, 0, 1, 1,..........]
accuracy = tf.reduce_mean(tf.cast(equal_list, tf.float32))
# 定义一个初始化变量的op
init_op = tf.global_variables_initializer()
# 开启回话运行
with tf.Session() as sess:
sess.run(init_op)
# 循环去训练
for i in range(1000):
# 取出真实存在的特征值和目标值
mnist_x, mnist_y = mnist.train.next_batch(50)
# 运行train_op训练
sess.run(train_op, feed_dict={x: mnist_x, y_true: mnist_y})
print("训练第%d步,准确率为:%f" % (i, sess.run(accuracy, feed_dict={x: mnist_x, y_true: mnist_y})))
return None
以上是我们自己设定的神经网络结构,下面介绍几个现成的神经网络结构。
LeNet:1986年(这个就太老了)
AlexNet:2012年
GoogleNet: