MNist手写数字识别入门,双层神经网络的纯代码实现与TF实现(第一篇博客,有好多不完善的地方,望指正)

学习笔记

学习过程遇到的问题

1.第一次构建起来的神经网络第一层算出的梯度总是零,导致参数变化太小。
2.每张图像训练时误差熵要调整为多少才合适
3.overflow encountered in exp
return 1/(1+np.exp(-x))
要紧吗
4.学习率的确定
5.标准BP算法的过拟合现象

感知机

学习感知机构造是通向神经网络和深度学习的一种重要思想。
感知机接收多个输入信号,输出一个信号。
感知机的信号有流(1)和不流(0)两种状态。
例如下图为有两个输入的感知机。

y = { 0 ( ω 1 x 1 + ω 2 x 2 θ ) 1 ( ω 1 x 1 + ω 2 x 2 > θ ) y=\begin{cases} 0 \quad ({\omega}_1x_1+{\omega_2}x_2\leq\theta)\\ 1 \quad ({\omega}_1x_1+{\omega_2}x_2>\theta) \end{cases}
感知机的多个输入信号都有各自固有的权重,权重用于评价输入信号的重要程度,相当于电路中的电阻。

简单逻辑电路

与门(都为真才真)
与非门(都为真才假)、
或门(其中之一为真则真)

感知机的实现

定义一个接收参数 x 1 x_1 x 2 x_2 的AND函数
实现与门

def AND(x1, x2):
	w1, w2, theta = 0.5, 0.5, 0.7
    tmp = x1*w1 + x2*w2
    if tmp <= theta:
    	return 0
    elif tmp > theta
    	return 1

导入权重和偏置
y = { 0 ( b + ω 1 x 1 + ω 2 x 2 θ ) 1 ( b + ω 1 x 1 + ω 2 x 2 &gt; θ ) y=\begin{cases} 0 \quad (b+{\omega}_1x_1+{\omega_2}x_2\leq\theta)\\ 1 \quad (b+{\omega}_1x_1+{\omega_2}x_2&gt;\theta) \end{cases}
b b 称为偏置,感知机计算输入信号和权重的乘积,然后再加上偏置,

def AND(x1, x2):
	x = np.array([x1, x2])
    w = np.array([0.5, 0.5])
    b = -0.7
    tmp = np.sum(w*x) + b
    if tmp <= 0:
    	return 0
    else:
    	return 1

实现非门和或门

def NAND(x1, x2):
	x = np.array([x1, x2])
    w = np.array([0.5, 0.5])
    b = 0.7
    tmp = np.sum(w*x) + b
    if tmp <= 0:
    	return 0
    else:
    	return 1
def OR(x1, x2):
	x = np.array([x1, x2])
    w = np.array([0.5, 0.5])
    b = -0.2
    tmp = np.sum(w*x) + b
    if tmp <= 0:
    	return 0
    else:
    	return 1

这里把 θ -\theta 命名为偏置 b b 偏置调整的是神经元被激活的容易程度
,与权重不同。

感知机的局限性

只能表示由一条直线分割的空间

多层感知机

感知机的妙处在于它可以叠加层。
通过与门,非门和或门配置异或门

def XOR(x1, x2):
	s1 = NAND(x1, x2)
    s2 = OR(x1, x2)
    y = AND(x1, x2)
    return y

神经网络

对于感知机,好消息是,对于复杂函数,感知机也隐藏了能够表示它的可能性,但是面临一个问题就是确定能够符合预期的输入和输出权重还是需要人工来进行。神经网络的出现就是为了解决这样的一个问题。

从感知机到神经网络

神经网络包括输入层,中间层和隐藏层。
引入新的函数将式子改写:
y = h ( b + w 1 x 1 + w 2 x 2 ) y=h(b+w_1x_1+w_2x_2)
h ( x ) h(x) 将输入信号的总和转换为输出信号,这种函数称为激活函数。激活函数的作用在于决定如何来激活输入信号的总和。
激活函数是连接感知机和神经网络的桥梁。

神经网络使用的激活函数

s i g m o i d sigmoid 函数:
h ( x ) = 1 1 + e x p ( x ) h(x)=\frac{1}{1+exp(x)}
在这里插入图片描述
阶跃函数:
超过零时输出1否则输出0
ReLU函数:
在这里插入图片描述
大于0时输出该值小于等于0时输出0

神经网络的激活函数必须使用非线性函数,因为如果使用线性函数,那么加深神经网络的层数就没有意义了。

3层神经网络的实现

从输入层到第一层的信号传递

X = np.array([1.0, 0.5])
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
B1 = np.array([0.1, 0.2, 0.3])
A1 = np.dot(X, W1) + B1
Z1 = sigmoid(A1)

从第一层到第二层的信号传递

W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
B2 = np.array([0.1, 0.2])
A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)

第二层到输出层的信号传递

def identity_function(x):
	return x

W3 = np.array([[0.1, 0.3], [0.2, 0.4]])
B3 = np.array([0.1, 0.2])

A3 = np.dot(Z2, W3) + B3
Y = identity_function(A3)

其中的identity_function()函数(也称为“恒等函数”)将其作为输出层的激活函数,这是为了与前面的流程统一。

输出层所用的激活函数要根据求解问题的性质决定。一般地,回归问题可以使用恒等函数,二元分类问题可以使用sigmoid函数,多元分类问题可以使用softmax函数。

代码整合

import numpy as np


def sigmoid(x):
    return 1/(1+np.exp(-x))

def init_network():
    network = {}
    network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
    network['b1'] = np.array([0.1, 0.2, 0.3])
    network['W2'] = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
    network['b2'] = np.array([0.1, 0.2])
    network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]])
    network['b3'] = np.array([0.1, 0.2])

    return network


def forward(network, x):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']
    a1 = np.dot(x, W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, W2) + b2
    z2 = sigmoid(a2)
    a3 = np.dot(z2, W3) + b3
    return a3

network = init_network()
x = np.array([1.0, 0.5])
y = forward(network, x)
print(y)
输出层设计

softmax函数
y k = e x p ( a k ) i = 1 n e x p ( a k ) y_k = \frac{exp(a_k)}{\sum^{n}_{i=1} exp(a_k)}
作用于输出层所有输入信号

def softmax(a):
    exp_a = np.exp(a)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    
    return y

softmax函数溢出问题:分子分母存在同时溢出的可能性
解决:分子分母同时乘常数 C C 使分子分母都不溢出,而又不影响真实结果。

def softmax(a):
    exp_a = np.exp(a - c)#溢出对策
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    
    return y

softmax函数输出是0.0到0.1之间的实数,且输出值总和为1.
所以可以把softmax函数的输出解释为“概率”
一般而言,神经网络只把输出值最大的神经元所对应的识别类别作为识别结果。是否使用softmax并不影响识别结果,在进行分类问题处理时一般可以省略。

输出神经元的数量

输出神经元的数量根据解决哪类问题而确定。比如识别数字0~9就应该有10个输出神经元。

手写数字识别

求解机器学习问题的步骤分成学习和推理两个阶段进行。
使用训练数据集进行学习然后用得到的参数对测试数据集进行推理。
MNist图像数据;28×28灰度图像,每个图像都相应的标有“7”、“2”、“1”等标签。
定义一个读取MNist数据集的函数,返回“(训练图像,训练标签),(测试图像,测试标签)”的形式返回读入的MNIST数集,

注:Python中的pickle功能,可以将程序运行中的对象保存为文件。如果加载保存过的pickle文件,可以立即复原之前程序运行中的对象。

将数据以numpy数组的形式读取

import numpy as np
import matplotlib.pyplot as plt
import struct


train_images_idx3_ubyte_file = 'MNIST_data/train-images.idx3-ubyte'
train_labels_idx1_ubyte_file = 'MNIST_data/train-labels.idx1-ubyte'
test_images_idx3_ubyte_file = 'MMNIST_data/t10k-images.idx3-ubyte'
test_labels_idx1_ubyte_file = 'MNIST_data/t10k-labels.idx1-ubyte'


def decode_idx1_ubyte(idx1_ubyte_file):
    bin_data = open(idx1_ubyte_file, 'rb').read()
    offset = 0
    fmt_header = '>ii'
    magic_number, num_images = struct.unpack_from(fmt_header, bin_data, offset)

    print('魔数:%d, 图片数量%d张' % (magic_number, num_images))
    offset += struct.calcsize(fmt_header)
    fmt_image = '>B'
    labels = np.empty(num_images)
    for i in range(num_images):
        if(i + 1) % 10000 == 0:
            print('已解析%d' % (i + 1) + '张')
        labels[i] = struct.unpack_from(fmt_image, bin_data, offset)[0]
        offset += struct.calcsize(fmt_image)
    return labels


def decode_idx3_ubyte(idx3_ubyte_file):
    bin_data = open(idx3_ubyte_file, 'rb').read()
    offset = 0
    fmt_header = '>iiii'
    magic_number, num_images, num_rows, num_cols = struct.unpack_from(fmt_header, bin_data,offset)
    print('魔数:%d, 图片数量: %d张, 图片大小: %d*%d' % (magic_number, num_images, num_rows, num_cols))
    image_size = num_rows * num_cols
    offset += struct.calcsize(fmt_header)
    print(offset)
    fmt_image = '>' + str(image_size) + 'B'
    print(fmt_image, offset, struct.calcsize(fmt_image))
    images = np.empty((num_images, num_rows, num_cols))
    for i in range(num_images):
        if (i + 1) % 10000 == 0:
            print('已解析%d' % (i +1) + '张')
            print(offset)
        images[i] = np.array(struct.unpack_from(fmt_image, bin_data, offset)).reshape((num_rows, num_cols))
        offset += struct.calcsize(fmt_image)
    return images


def load_train_images(idx_ubyte_file=train_images_idx3_ubyte_file):
    return decode_idx3_ubyte(idx_ubyte_file)


def load_train_labels(idx_ubyte_file=train_labels_idx1_ubyte_file):
    return decode_idx1_ubyte(idx_ubyte_file)


def load_test_images(idx_ubyte_file=test_images_idx3_ubyte_file):
    return decode_idx3_ubyte(idx_ubyte_file)


def load_test_labels(idx_ubyte_file=test_labels_idx1_ubyte_file):
    return decode_idx1_ubyte(idx_ubyte_file)


if __name__ == '__main__':
    train_images = load_train_images()
    train_labels = load_train_labels()

    for i in range(10):
        print(train_labels[i])
        plt.imshow(train_images[i], cmap='gray')
        plt.pause(0.000001)
        plt.show()
    print('done')
    print(np.shape(train_images[50000]))
批处理

将数据一次性打包一次性处理,减少程序用在循环上的时间

归一化问题

(1)使网络快速的收敛:赞同。
(2)避免数值问题:赞同。
(3)统一量纲:本人认为这从属于业务层,与网络的训练无关。
(4)避免神经元饱和:与权值阈值相乘后,才是sigmoid的输入值,初始化得好的话,并不会饱和输出。若果使用“把权值和阈值随机初始化为[-1,1]之间的值”这种初始化方法,那不归一化就会引起神经元输出饱和现象。
(5)大数吞小数:若果我们找到适合的权值,是不会吞掉的,例如x1=10000,x2=1, 而w1=0.0001,w2=1,那么w1x1是不会吞掉w2x1的。

神经网络的学习

神经网络的特征就是可以从数据中学习而得到参数。在计算机视觉领域中,可以通过确定提取特征量再使用分类器进行分类,而在深度学习神经网络中,特征量也可以交给机器来确定。

损失函数

损失函数一般使用均方误差和交叉熵误差等。
均方误差:
E = 1 2 k ( y k t k ) 2 E=\frac{1}{2}\sum_{k}(y_k - t_k)^2
y k y_k 表示神经网络的输出
t k t_k 表示监督数据

y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]

这里t的表示方式成为one-hot表示

def mean_squared_error(y, t):
    return 0.5 * np.sum((y-t)**2)

交叉熵误差:
E = k t k log y k E=-\sum_{k}t_k\log y_k

def cross_entropy_error(y, t):
    delta = 1e-7
    return -np.sum(t * np.log(y + delta))

在神经网络的学习中,寻找最优参数(权重和偏置)时,小的参数。为了找到使损失函数值尽可能小的地方,需要计算参数的导数(确切的讲是梯度),然后以这个导数为指引,逐步更新参数的值。
假设有一个神经网络,那么就关注神经网络中某一个权重参数。对该权重参数的损失函数求导,表示“如果稍微改变这个权重参数的值,损失函数值将如何变化”。若导数值为零,则无论权重参数像哪个方向变化,损失函数值都不会改变,此时该权重参数的更新会停止在此处。
所以,不能使用识别精度作为指标,因为这样一来,绝大多数地方的导数都会变为0,导致参数无法更新。
如果使用阶跃函数作为激活函数,那么神经网络的学习将无法进行,因为参数的微调变化将被阶跃函数抹杀。

数值微分

使用数值微分近似切线

def numerical_diff(f, x):
    h = 1e-4
    return (f(x+h) - f(x-h)) / (2*h)

梯度计算;

def numerical_gradient(f, x):
    h = 1e-4
    grad = np.zeros_like(x)
    
    for idx in range(x.size):
        tmp_val = x[idx]
        x[idx] = tmp_val + h
        fxh1 = f(x)
        x[idx] = tmp_val -h
        fxh2 = f(x)
        grad[idx] = (fxh1 - fxh2) / (2*h)
        x[idx] = tmp_val
    return grad
梯度法

梯度未必指向的是真正的函数最小值,当函数很复杂且呈现扁平状时,学习可能会进入一个几乎平坦的区域,陷入“学习高原”进入学习停滞区。
此时梯度法就派上用场了。**沿着梯度方向前进一定的距离,然后在新的方向重新求梯度,再沿着新梯度的方向前进,如此反复,不断减小函数值的过程就称为梯度法。**梯度法是机器学习中最优化问题的常用方法,特别是在神经网络的学习中经常被使用。
x 0 = x 0 η f x 0 x_0 = x_0 - \eta f_{x_0}
x 1 = x 1 η f x 1 x_1 = x_1 - \eta f_{x_1}
式子中的 η \eta 表示更新量,在神经网络学习中称为学习率,学习率决定在一次学习中应该学习多少,以及多大程度上更新参数。
一般而言,学习率的值过大或者过小都无法抵达一个“好的位置”。在神经网络学习过程中,一般会一边改变学习率的值,一边确认学习是否正确进行了。

def gradient_descent(f, init_x, lr=0.01, step_num=100):
    x = init_x
    for i in range(step_num):
        grad = numerical_gradient(f, x)
        x -= lr * grad
    return x

这里 f f 是需要优化的函数, i n i t x init_x 是初始值, l r lr 是学习率, s t e p n u m step_num 是梯度法重复的次数。

示例:梯度法求函数 f ( x 0 + x 1 ) = x 0 2 + x 1 2 f(x_0 + x_1) = x_0^2 + x_1^2 的最小值

import numpy as np


def function_2(x):
    return x[0]**2 + x[1]**2


def numerical_gradient(f, x):
    h = 1e-4
    grad = np.zeros_like(x)
    for idx in range(x.size):
        tmp_val = x[idx]
        x[idx] = tmp_val + h
        fxh1 = f(x)
        x[idx] = tmp_val -h
        fxh2 = f(x)
        grad[idx] = (fxh1 - fxh2) / (2*h)
        x[idx] = tmp_val
    return grad


def gradient_descent(f, init_x, lr=0.01, step_num=100):
    x = init_x
    for i in range(step_num):
        grad = numerical_gradient(f, x)
        x -= lr * grad
    return x


init_x = np.array([-3.0, 4.0])
a = gradient_descent(function_2, init_x=init_x, lr=0.1, step_num=100)
print(a)

学习率过大或者过小都不能得到好的结果,例如上述例子中, l r = 10 lr=10 或者 l r = 1 e 10 lr = 1e-10 因此,设定合适的学习率是一个很重要的问题。
像学习率这样的参数称为超参数。这是一种与神经网络的参数(偏置和权重)不同的参数,学习率这样的超参数是由人工设定的。一般来说,超参数需要尝试多个值,一边找到一种可以使学习学习顺利进行的设定。
确定学习率至关重要!如果学习率低,训练会变得更加可靠,但是优化会耗费较长的时间,因为朝向损失函数的每个步长会非常小。如果学习率很高,训练可能根本不会收敛,甚至会发散。权重改变量可能非常大,使得优化越过最小值。
在这里插入图片描述
确定学习率的诀窍:从低学习率开始训练神经网络,并在每一个批次中指数提高学习率:
在这里插入图片描述
另一个方法是观察计算损失函数变化率(损失函数关于迭代次数的导数),然后以学习率为x轴,以变化率为y轴画图:
在这里插入图片描述
平均线;
在这里插入图片描述
找到学习率最小的位置,以此作为学习率。

神经网络的梯度

神经网络的学习也要求梯度,**这里说的梯度是指损失函数关于权重参数的梯度。**比如,有一个形状为 2 × 3 2×3 的权重 W W 的神经网络,损失函数为 L L 表示,此时,梯度可以用 L W L_W 表示,即 W W 中的全部元素变为损失函数 L L 对其的偏导。
请注意:只在训练之前选择一次学习率是不够的。训练过程中,最优学习率会随着时间推移而下降。你可以定期重新运行相同的学习率搜索程序,以便在训练的稍后时间查找学习率。

误差反向传播

数值微分的缺点是十分浪费时间。
理解误差反向传播的两种方法:

基于数学公式
基于计算图

##Tensorflow实现MNist手写数字集的训练
为了用python实现高效的数值计算,我们通常会使用函数库,比如NumPy,会把类似矩阵乘法这样的复杂运算使用其他外部语言实现。不幸的是,从外部计算切换回Python的每一个操作,仍然是一个很大的开销。如果你用GPU来进行外部计算,这样的开销会更大。用分布式的计算方式,也会花费更多的资源用来传输数据。
TensorFlow也把复杂的计算放在python之外完成,但是为了避免前面说的那些开销,它做了进一步完善。Tensorflow不单独地运行单一的复杂计算,而是让我们可以先用图描述一系列可交互的计算操作,然后全部一起在Python之外运行。(这样类似的运行方式,可以在不少的机器学习库中看到。)
使用前导入Tensorflow:

import tensorflow as tf

通过操作符号变量来描述这些可以交互操作的单元,可以用下面方式创建:

x = tf.placeholder('float', [None, 784])

x不是一个特定值,而是一个占位符placeholder,我们在Tensorflow运行计算时输入这个值。输入任意数量的MMNist图像,用二维的浮点数来表示这些图。
权重与偏置:

W = tf.Variable(tf.zeros([784, 10]))
b = Variable(tf.zeros([10]))

我们赋予tf.Variable不同的初值来创建不同的Variable:在这里,我们都用全为零的张量来初始化W和b。因为我们要学习W和b的值,它们的初值可以随意设置。
模型的实现:

y = tf.nn.softmax(tf.matmul(x, W) + b)

训练模型:
训练模型首先需要定义指标进行评估:
常用交叉熵误差进行评估。
添加占位符以输入正确值:

y_ = tf.placeholder('float', [None,10])

计算交叉熵:

cross_entropy = -tf.reduce_sum(y_*tf.log(y))

用TensorFlow来训练它是非常容易的。因为TensorFlow拥有一张描述你各个计算单元的图,它可以自动地使用反向传播算法来有效地确定你的变量是如何影响你想要最小化的那个成本值的。然后,TensorFlow会用你选择的优化算法来不断地修改变量以降低成本。

train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

在这里,我们要求TensorFlow用梯度下降算法(gradient descent algorithm)以0.01的学习速率最小化交叉熵。梯度下降算法(gradient descent algorithm)是一个简单的学习过程,TensorFlow只需将每个变量一点点地往使成本不断降低的方向移动。
初始化变量所需要的操作:

init = tf.initialize_all_variables()

在一个Session里面启动我们的模型,并且初始化变量:

sess = tf.Session()
sess.run(init)

开始训练模型:

for i in range(1000):
  batch_xs, batch_ys = mnist.train.next_batch(100)
  sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})

模型的评估:
tf.argmax 是一个非常有用的函数,它能给出某个tensor对象在某一维上的其数据最大值所在的索引值。由于标签向量是由0,1组成,因此最大值1所在的索引位置就是类别标签,比如tf.argmax(y,1)返回的是模型对于任一输入x预测到的标签值,而 tf.argmax(y_,1) 代表正确的标签,我们可以用 tf.equal 来检测我们的预测是否真实标签匹配(索引位置一样表示匹配)。

correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))

这行代码会给我们一组布尔值。为了确定正确预测项的比例,我们可以把布尔值转换成浮点数,然后取平均值。例如,[True, False, True, True] 会变成 [1,0,1,1] ,取平均值后得到 0.75.

accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

最后,我们计算所学习到的模型在测试数据集上面的正确率。

print sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})

完整代码:

import tensorflow as tf
import tensorflow.examples.tutorials.mnist.input_data as input_data


mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
x = tf.placeholder('float', [None, 784]) # 用于输入正确值的占位符
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
y = tf.nn.softmax(tf.matmul(x, W) + b)
y_ = tf.placeholder("float", [None, 10])
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(init)
for i in range(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
    correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
    print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))

构建一个多层卷积网络:

为了创建这个模型,我们需要创建大量的权重和偏置。
这个模型中的权重在初始化时应该加入少量的噪声来打破对称性以及避免0梯度。
由于我们使用的是ReLU神经元,因此比较好的做法是用一个较小的正数来初始化偏置项,以避免神经元节点输出恒为0的问题(dead neurons)。为了不在建立模型的时候反复做初始化操作,我们定义两个函数用于初始化。

def weight_variable(shape):
  initial = tf.truncated_normal(shape, stddev=0.1)
  return tf.Variable(initial)

def bias_variable(shape):
  initial = tf.constant(0.1, shape=shape)
  return tf.Variable(initial)

卷积和池化

TensorFlow在卷积和池化上有很强的灵活性。我们怎么处理边界?步长应该设多大?在这个实例里,我们会一直使用vanilla版本。我们的卷积使用1步长(stride size),0边距(padding size)的模板,保证输出和输入是同一个大小。我们的池化用简单传统的2x2大小的模板做max pooling。为了代码更简洁,我们把这部分抽象成一个函数。

def conv2d(x, W):
  return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

def max_pool_2x2(x):
  return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
                        strides=[1, 2, 2, 1], padding='SAME')

第一层卷积:

现在我们可以开始实现第一层了。它由一个卷积接一个max pooling完成。卷积在每个5x5的patch中算出32个特征。卷积的权重张量形状是[5, 5, 1, 32],前两个维度是patch的大小,接着是输入的通道数目,最后是输出的通道数目。 而对于每一个输出通道都有一个对应的偏置量。

W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])

为了用这一层,我们把x变成一个4d向量,其第2、第3维对应图片的宽、高,最后一维代表图片的颜色通道数(因为是灰度图所以这里的通道数为1,如果是rgb彩色图,则为3)。

x_image = tf.reshape(x, [-1,28,28,1])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
第二层卷积:

为了构建一个更深的网络,我们会把几个类似的层堆叠起来。第二层中,每个5x5的patch会得到64个特征。

W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])

h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
密集连接层:

现在,图片尺寸减小到7x7,我们加入一个有1024个神经元的全连接层,用于处理整个图片。我们把池化层输出的张量reshape成一些向量,乘上权重矩阵,加上偏置,然后对其使用ReLU。

W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])

h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

为了减少过拟合,我们在输出层之前加入dropout。我们用一个placeholder来代表一个神经元的输出在dropout中保持不变的概率。这样我们可以在训练过程中启用dropout,在测试过程中关闭dropout。 TensorFlow的tf.nn.dropout操作除了可以屏蔽神经元的输出外,还会自动处理神经元输出值的scale。所以用dropout的时候可以不用考虑scale。

keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
输出层

最后,我们添加一个softmax层,就像前面的单层softmax regression一样。

W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
sess.run(tf.initialize_all_variables())
for i in range(20000):
  batch = mnist.train.next_batch(50)
  if i%100 == 0:
    train_accuracy = accuracy.eval(feed_dict={
        x:batch[0], y_: batch[1], keep_prob: 1.0})
    print "step %d, training accuracy %g"%(i, train_accuracy)
  train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})

print "test accuracy %g"%accuracy.eval(feed_dict={
    x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0})

完整代码:

import tensorflow.examples.tutorials.mnist.input_data as input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
import tensorflow as tf


sess = tf.InteractiveSession()
x = tf.placeholder("float", shape=[None, 784])
y_ = tf.placeholder("float", shape=[None, 10])


def weight_variable(shape):
    initial = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(initial)


def bias_variable(shape):
    initial = tf.constant(0.1, shape=shape)
    return tf.Variable(initial)


def conv2d(x, W):
    return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')



def max_pool_2x2(x):
    return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')


W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
x_image = tf.reshape(x, [-1,28,28,1])

h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)

W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])

h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)

W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])

h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])

y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)


cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
sess.run(tf.initialize_all_variables())
for i in range(20000):
    batch = mnist.train.next_batch(50)
    if i % 100 == 0:
      train_accuracy = accuracy.eval(feed_dict={
          x:batch[0], y_: batch[1], keep_prob: 1.0})
      print("step %d, training accuracy %g" % (i, train_accuracy))
    train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})

print("test accuracy %g"%accuracy.eval(feed_dict={
    x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))
发布了2 篇原创文章 · 获赞 0 · 访问量 46

猜你喜欢

转载自blog.csdn.net/qq_39409403/article/details/93376675