4 months ago

前面一篇在做RNN前期数据处理的时候,经过一系列数据变换,搞的人云里雾里,这次要逐步好好消化一下

一、静态RNN:图片数据的处理

先看静态RNN的核心代码

x=mnist.train.next_batch(batch_size)
x1=tf.reshape(x,[-1,step,input])
# 截止目前,输入数据的尺寸为(batch_size, step, input)
# LSTM需要的数据形状是长度为step的张量列表,每个张量的尺寸为(batch_size,input)
x2=tf.transpose(x1,[1,0,2])
x3=tf.reshape(x2,[-1,input])
x4=tf.split(axis=0,num_or_size_splits=step,value=x3)
# 构建LSTM网络(forget_bias=1.0,所以实际上还是普通RNN)
rnn_cell=tf.nn.rnn_cell.BasicLSTMCell(hidden,forget_bias=1.0)
outputs1,last_state=tf.nn.static_rnn(rnn_cell,x4,dtype=tf.float32)
w=tf.Variable(tf.random_normal([hidden,classes]))
b=tf.Variable(tf.random_normal([classes,]))
l=tf.matmul(outputs1[-1],w)+b
pre=tf.nn.softmax(l)

下面对输入数据的处理过程进行逐步解析

x=mnist.train.next_batch(batch_size)

获取一个batch的训练集数据,形状为(batch-size, 28*28)

x1=tf.reshape(x,[-1,step,input])

截止目前,输入数据的尺寸为(batch-size, step, input)
图片的尺寸是(height,width),即(行,列),也就是说step就是图片的height,input就是图片的width
因此此时的数据形状为(batch-size, height, wieth)
LSTM需要的数据形状是长度为step的张量列表,每个张量的尺寸为(batch-size,input),即[height x (batch-size,width)],下面还需要一步步尺寸变换

x2=tf.transpose(x1,[1,0,2])

此时数据形状(step,batch-size,input),即(height,batch-size,width)

x3=tf.reshape(x2,[-1,input])

此时数据形状(step x batch-size,input),即(height x batch-size,width)

x4=tf.split(axis=0,num_or_size_splits=step,value=x3)

此时数据是一个张量列表,形状[step x (batch-size x input,)],即[height x (batch-size x width,)]
然后把这个列表传入RNN对象中

在整个变换过程中,input数据(即width)始终没有被打散,就是说原图片的每一行内部数据保持不变
为了直观展示这一过程,我们举一个实际例子来展示其中的每一步变换过程



从这个过程中可以直观地看到,RNN需要的输入数据是这样的:先把所有样本的第一行按先后顺序排成一行输进去,再把所有样本的第二行所有数据排成一行输进去......直到把所有样本的最后一行(第28行)按照先后顺序排成一行输进去
原来是这个样子的

但是请保持头脑清晰:输入数据尺寸[step x (batch-size x input,)],但输入层神经元个数依然是input
在整个变换过程中,input始终保持不动

二、动态RNN:图片数据的处理

weights ={
    'in': tf.Variable(tf.random_normal([inputs, hidden])),
    'out': tf.Variable(tf.random_normal([hidden, classes]))
}

biases = {
    'in': tf.Variable(tf.random_normal([n_hidden,])),
    'out': tf.Variable(tf.random_normal([n_classes, ]))
}

#定义RNN模型
def RNN(X, weights, biases):
    # 此时,X形状 (128 batch, 28 steps * 28 inputs)
    X = tf.reshape(X,[-1, inputs])
    # 此时,X形状 (128 batch * 28 steps ,28 inputs),然后进入隐藏层
    X_in = tf.matmul(X,weights['in']) + biases['in']
    # 此时,X_in = (128 batch * 28 steps ,128 hidden)
    X_in=tf.reshape(X_in,[-1,n_steps,n_hidden])
    # 此时,X_in = (128 batch ,28 steps ,128 hidden)
    #采用LSTM循环神经网络单元 basic LSTM Cell
    lstm_cell = tf.contrib.rnn.BasicLSTMCell(n_hidden, forget_bias=1.0,state_is_tuple=True)
    # 初始化为0  lstm 单元 由 h_cell,h_state两部分组成
    init_state=lstm_cell.zero_state(batch_size,dtype=tf.float32)

    # dynamic_rnn接受张量(batch ,steps,inputs)或者(steps,batch,inputs) 作为X_in
    outputs,final_state=tf.nn.dynamic_rnn(lstm_cell,X_in,initial_state=init_state,time_major=False)
    results=tf.matmul(final_state[1], weights['out']) + biases['out']
    return results

x=mnist.train.next_batch(batch_size)
pred=RNN(x,weights,biases)

动态RNN的线性操作需要考虑两个过程:输入层到隐藏层,隐藏层到输出层
同时因为有参数 time_major=False的存在,输入数据不需要经过复杂繁琐的变换
这里的输入好理解多了:把第一个样本从第一行到最后一行作为一个整体完整输入,然后把第二个样本从第一行到最后一行作为整体完整输入......

参考资料:
静态RNN源码--MNIST数据集:第六章随书代码
动态RNN源码--MNIST图片数据集

 
4 months ago

一、循环神经网络RNN介绍

1.RNN网络结构介绍

常规的机器学习算法和卷积神经网络都没有考虑序列数据的情况
为了处理序列数据,我们将扩展神经网络以存储前一次迭代输出,这样的神经网络称之为递归神经网络(比循环更贴切)
下面从传统全连接神经网络引入到递归神经网络


通常我们只考虑最后一个输出S(t)作为目标数值或目标分类

我们也可以吧序列输出结果反馈回模型生成多个输出结果,用来预测一个序列 [S(t+1),S(t+2),...]

总之,我们的关注点都是在最后一次输出S(t)

为了方便后续介绍,我偷个懒换张图,思路要及时跟上呀


上图的左边部分是RNN的结构简图,也是我们常见的描述RNN的结构简图
简单来说:输入为X(t),输出为O(t),隐藏层S(t)是网络中的一个向量状态,可视为系统的一种记忆模式。
隐藏层S(t)含有输入序列中所有之前的元素中的信息。
同时,左图中的循环并不是指中间层单纯的自我循环,而是代表了序列输入X(t)从网络中的每一步移动到下一步,从而导致中间层和输出层的变化的结构示意,这一点从上图的右边部分可以清晰看出。因此右边部分被称为RNN的展开形式

上面听起来可能还是比较复杂,下面有个简单的解释
在传统的神经网络,无论是全连接神经网络还是卷积神经网络,层与层之间直接相连,而层内部神经元相互不连接的
在RNN神经网络中,层内部神经元是彼此连接的
这样的解释就好理解多了,而且上面所有的图示展示的都是包含一层隐藏层的RNN神经网络

2.RNN的参数共享

从刚刚的结构图也可以看出,RNN网络在每个时刻使用的参数均相同,即U、V、W相同
正是由于这种参数共享(每个时刻,对同一系列的不同输入执行同样的运算),使得RNN极大减少了网络在训练过程中需要学习的参数数量,由此缩短了网络训练时间

3.RNN的梯度爆炸和梯度消失

在梯度下降过程中的反向传播过程中,权重是根据梯度误差按照比例调整的,梯度计算需要注意以下两点:
1)如果权重很小,梯度可能“消失”,即梯度信号太小导致学习变得很慢甚至停止,这种现象称为“梯度消失”
2)如果权重过大,可能会因梯度过大而导致学习不收敛,这种现象称为“梯度爆炸”

所以RNN的时序不能太长,若超过十步,很容易出现梯度消息或者梯度爆炸(导数链式法则导致连乘,造成梯度指数级变化)
为了解决梯度消失问题,人们为基本的RNN模型提出了几个扩展,如LSTM、GRU、CW-RNN

二、Tensorflow API

1.创建RNN对象

tf.contrib.rnn.BasicRNNCell()
tf.nn.rnn_cell.BasicRNNCell()
有两个API可以调用,参数和返回一致,用官方说法叫“别名”

__init__(
    num_units, #RNN单元中的单元数(隐藏层数量)
    activation=None, #使用非线性激活函数,默认值:tanh
    reuse=None, #布尔值,是否复用变量
    name=None,
    dtype=None,
    **kwargs
)

对于GPU版本,另有接口 tf.contrib.cudnn_rnn.CudnnRNNTanh,今天暂不讨论

2.开始计算

1)静态RNN:输入固定长度的序列数据

两个相同的API tf.contrib.rnn.static_rnn tf.nn.static_rnn
这两个API参数和返回一致,用官方说法叫“别名”

tf.nn.static_rnn(
    cell, #刚刚定义的RNN对象
    inputs, #待训练数据
    initial_state=None, #RNN初试状态,如果不指定默认会是
                        #initial_state = rnn_cell.zero_state(batch_size, dtype=tf.float32)
    dtype=None,
    sequence_length=None, #指定输入序列数据的长度,这个一般就不需要的吧
    scope=None
)

返回参数
(1)output:一个元祖,包含所有输入数据对应的输出,通常使用最后一个元素
(2)state:最后一个隐藏层的状态

2)动态RNN:输入变长序列数据
tf.nn.dynamic_rnn(
    cell, #刚刚定义的RNNCell的一个实例
    inputs, #RNN输入
    sequence_length=None,
    initial_state=None,
    dtype=None,
    parallel_iterations=None, #并行处理的数量,默认为32
    swap_memory=False,
    time_major=False,
    scope=None
)

返回参数
(1)output:一个元祖,包含所有输入数据对应的输出,通常使用最后一个元素
(2)state:最后一个隐藏层的状态

tips:无论输入的序列数据是固定长度还是可变长度,推荐使用动态RNN,实践证明运算速度快

3.举个例子

# create a BasicRNNCell
rnn_cell = tf.nn.rnn_cell.BasicRNNCell(hidden_size)

# 'outputs' is a tensor of shape [batch_size, max_time, cell_state_size]

# defining initial state
initial_state = rnn_cell.zero_state(batch_size, dtype=tf.float32)

# 'state' is a tensor of shape [batch_size, cell_state_size]
outputs, state = tf.nn.dynamic_rnn(rnn_cell, input_data,
                                   initial_state=initial_state,
                                   dtype=tf.float32)

三、LSTM

1.原理介绍

看一下RNN最常用的变体LSTM,详细介绍请见参考资料,这里简单描述
在标准的 RNN 中,循环的模块只有一个非常简单的结构,例如一个 tanh 层


LSTM 同样是这样的结构,但是重复的模块拥有一个不同的结构。这里是有四个神经网络层,以一种非常特殊的方式进行交互。

传说中这个复杂的结构用到三个门:输入门、输出门、遗忘门,来决定保留多少以往的信息
反正我也不是很懂,大概就是这样吧,不然怎么预防梯度消失,你说是吧

2.API

整个过程跟刚才一摸一样,仅仅是把 BasicRNNCell替换为 BasicLSTMCell即可
另外还多了一个参数 forget_bias,取值范围[0,1]
千万不要被这个名字迷惑,其实它的真实意思是“保留率”,即保留多少以往的信息
这个值设为1的话,就是完全保留以往的信息,就是最基本的RNN了

1.创建LSTM对象

tf.contrib.rnn.BasicLSTMCell()
tf.nn.rnn_cell.BasicLSTMCell()

__init__(
    num_units,
    forget_bias=1.0, #以往信息的保留率
    state_is_tuple=True, #若为True,接受和返回二维元祖c_state 和 m_state
    activation=None, #激活函数,默认为tanh
    reuse=None,
    name=None,
    dtype=None,
    **kwargs
)
2.开始计算

同RNN,不再赘述

3.简单举例
import tensorflow as tf
 
batch_size = 4 
input = tf.random_normal(shape=[3, batch_size, 6], dtype=tf.float32)
cell = tf.nn.rnn_cell.BasicLSTMCell(10, forget_bias=1.0, state_is_tuple=True)
init_state = cell.zero_state(batch_size, dtype=tf.float32)
output, final_state = tf.nn.dynamic_rnn(cell, input, initial_state=init_state, time_major=True) #time_major如果是True,就表示RNN的steps用第一个维度表示,建议用这个,运行速度快一点。

四、使用MNIST数据集构建RNN图像分类器

为了使用RNN来分类图片,我们把每张图片的行堪称一个像素序列(sequence)
MNIST图片大小是28x28像素,所以每张图片看成28个(step)像素序列(input,输入层数)

1.代码调试

import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import pandas as pd

# 1.数据准备
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("../data/mnist/", one_hot=True)
batch_size=128 
step=28 #输入步数
input=28 #输入层神经元数量
hidden=128 #隐藏层神经元数量
classes=10 #输出层神经元数量
# 2.输入值占位
x=tf.placeholder(tf.float32,[None,step*input])
y=tf.placeholder(tf.float32,[None,classes])

x=tf.reshape(x,[-1,step,input])
# 截止目前,输入数据的尺寸为(batch_size, step, input)
# LSTM需要的数据形状是长度为step的张量列表,每个张量的尺寸为(batch_size,input)
x=tf.transpose(x,[1,0,2])
x=tf.reshape(x,[-1,input])
x=tf.split(axis=0,num_or_size_splits=step,value=x)
# 3.构建LSTM网络(forget_bias=1.0,所以实际上还是普通RNN)
rnn_cell=tf.nn.rnn_cell.BasicLSTMCell(hidden,forget_bias=1.0)
outputs,last_state=tf.nn.static_rnn(rnn_cell,x,dtype=tf.float32)

程序到这里,调试过程中会报一个错误

ValueError: Variable rnn/basic_lstm_cell/kernel already exists, disallowed.

这个错误出现在创建LSTM对象的阶段,第一次运行的时候没问题,等到再次运行就出现了,这个属于命名空间的问题
这里没有指定命名空间,大家都在默认命名空间里
运行一次后已经产生了一个LSTM对象,再次运行就再次产生一个新LSTM对象,就冲突了
解决问题很简单,新建一个命名空间,设置参数为“reuse=True”
还有一个更简单的:重置整张图,程序第一行加入代码 tf.reset_default_gaph()
或者这里暂不处理,等到下面的问题解决了(把LSTM放到一个函数里),这个问题自然而然就没有了
程序继续往下走

w=tf.Variable(tf.random_normal([hidden,classes]))
b=tf.Variable(tf.random_normal([classes,]))
l=tf.matmul(rnn(x),w)+b
pre=tf.nn.softmax(l)
loss=tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=l,labels=y))
opti=tf.train.AdamOptimizer(0.01).minimize(loss)
temp=tf.equal(tf.arg_max(y,1),tf.arg_max(pre,1))
accurance=tf.reduce_mean(tf.cast(temp,tf.float32))
sess=tf.Session()
sess.run(tf.global_variables_initializer())
loss_map=[]
accu_map=[]
for i in range(1000):
    batch_x,batch_y=mnist.train.next_batch(batch_size)
    sess.run(opti,{x:batch_x,y:batch_y})

好了,程序刚刚走到这里,又开始报错了 Tensorflow unhashable type 'list' in sess.run
这个错误报的有些莫名其妙,其实并不是这一行出问题了,而是在代码运行的过程中出了问题
关键就在输入数据不断变化的过程中,让我们来看一下这个过程



可以看到在输入数据转换过程中,数据形状不断发现变化,已经偏离了最开始placeholder最初所定义的形状
数据形状从一开始定义的二维张量变成三维张量,又变会二维张量,这些tensorflow都可以忍,最后一步的split索性直接变成了list类型,严重背离了初心,tensorflow的小宇宙直接爆发了
这样看来,应该就是最后一步split的问题喽,那我直接把split这个函数名称换掉就好了哈

x=tf.placeholder(tf.float32,[None,step*input])
x=tf.reshape(x,[-1,step,input])
x=tf.transpose(x,[1,0,2])
x=tf.reshape(x,[-1,input])
# 我把这个变换后的名称换成x3
x3=tf.split(axis=0,num_or_size_splits=step,value=x)
rnn_cell=tf.nn.rnn_cell.BasicLSTMCell(hidden,forget_bias=1.0)
outputs1,last_state=tf.nn.static_rnn(rnn_cell,x3,dtype=tf.float32)

结果程序开始报split上一步reshape的问题

ValueError                                Traceback (most recent call last)
<ipython-input-26-600304ab85e1> in <module>
     37 for i in range(100):
     38     batch_x,batch_y=mnist.train.next_batch(batch_size)
---> 39     sess.run(opti,{x:batch_x,y:batch_y})
     40     cost,accu=sess.run([loss,accurance],{x:batch_x,y:batch_y})
     41     loss_map.append(cost)

ValueError: Cannot feed value of shape (128, 784) for Tensor 'Reshape_1:0', which has shape '(?, 28)'

由此可以看出,sess.run()函数的赋值顺序跟想象中的从上到下不一样
所以说不能偷懒,数据变化的每一步都需要重新定义一个变量名,就像下面这样

x=tf.placeholder(tf.float32,[None,step*input])
x0=tf.reshape(x,[-1,step,input])
x1=tf.transpose(x0,[1,0,2])
x2=tf.reshape(x1,[-1,input])
x3=tf.split(axis=0,num_or_size_splits=step,value=x2)
rnn_cell=tf.nn.rnn_cell.BasicLSTMCell(hidden,forget_bias=1.0)
outputs1,last_state=tf.nn.static_rnn(rnn_cell,x3,dtype=tf.float32)

当然第一份参考资料里面直接把数据变化的过程封装在一个函数了,这样眼不见为净看起来很规范

x=tf.placeholder(tf.float32,[None,step*input])

def rnn(x):
    x=tf.reshape(x,[-1,step,input])
    x=tf.transpose(x,[1,0,2])
    x=tf.reshape(x,[-1,input])
    x=tf.split(axis=0,num_or_size_splits=step,value=x)
    rnn_cell=tf.nn.rnn_cell.BasicLSTMCell(hidden,forget_bias=1.0)
    outputs,last_state=tf.nn.static_rnn(rnn_cell,x,dtype=tf.float32)
    return outputs[-1]
                                                    ········
sess.run(opti,{x:batch_x,y:batch_y})

而且这种函数封装的方法直接规避了上一个bug

2.完整代码

import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import pandas as pd

#tf.reset_default_graph()
# 1.准备数据
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("../data/mnist/", one_hot=True)
batch_size=128
step=28
input=28
hidden=128
classes=10
x=tf.placeholder(tf.float32,[None,step*input])
y=tf.placeholder(tf.float32,[None,classes])

def rnn(x):
    x=tf.reshape(x,[-1,step,input])
# 截止目前,输入数据的尺寸为(batch_size, step, input)
# LSTM需要的数据形状是长度为step的张量列表,每个张量的尺寸为(batch_size,input)
    x=tf.transpose(x,[1,0,2])
    x=tf.reshape(x,[-1,input])
    x=tf.split(axis=0,num_or_size_splits=step,value=x)
    # 3.构建LSTM网络(forget_bias=1.0,所以实际上还是普通RNN)
    rnn_cell=tf.nn.rnn_cell.BasicLSTMCell(hidden,forget_bias=1.0)
    outputs,last_state=tf.nn.static_rnn(rnn_cell,x,dtype=tf.float32)
    return outputs[-1]
    
w=tf.Variable(tf.random_normal([hidden,classes]))
b=tf.Variable(tf.random_normal([classes,]))
l=tf.matmul(rnn(x),w)+b
pre=tf.nn.softmax(l)
loss=tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=l,labels=y))
opti=tf.train.AdamOptimizer(0.01).minimize(loss)
temp=tf.equal(tf.arg_max(y,1),tf.arg_max(pre,1))
accurance=tf.reduce_mean(tf.cast(temp,tf.float32))
sess=tf.Session()
sess.run(tf.global_variables_initializer())
loss_map=[]
accu_map=[]
for i in range(1000):
    batch_x,batch_y=mnist.train.next_batch(batch_size)
    sess.run(opti,{x:batch_x,y:batch_y})
    cost,accu=sess.run([loss,accurance],{x:batch_x,y:batch_y})
    loss_map.append(cost)
    accu_map.append(accu)
plt.plot(loss_map[::20])
plt.show()
plt.plot(accu_map[::20])
plt.show()

简单看一下运行效果相当不错,小批量迭代200次以后准确率就很高了


五、动态RNN实现

关于动态RNN,有人提到运行效果快行文逻辑和思路略有不同,并不是当初设想的简单改一个API
刚刚的静态RNN的线性操作 tf.matmul()只需考虑数据从隐藏层到输出层
动态RNN的线性操作需要考虑两个过程:输入层到隐藏层,隐藏层到输出层
这里有源码可以运行

六、多层RNN

刚刚展示的是一层RNN网络,现在使用两层RNN做一次展示


tensorflow使用MultiRNNCell()函数轻松实现多层组合,该函数输入参数为RNN单元列表,如下图所示

rnn_cell=tf.nn.rnn_cell.BasicLSTMCell(hidden,forget_bias=1.0)
multi_rnn_cell=tf.nn.rnn_cell.MultiRNNCell([rnn_cell]*2) #产生两个一摸一样的RNN单元
outputs,last_state=tf.nn.static_rnn(multi_rnn_cell,x,dtype=tf.float32)

不过这种方式容易报维度错误 Dimensions must be equal,所以我们通常使用以下方式

# 产生三个完全不同的RNN单元
rnn_cell=[tf.nn.rnn_cell.BasicLSTMCell(hidden,forget_bias=1.0) for _ in range(3)]
multi_rnn_cell=tf.nn.rnn_cell.MultiRNNCell(rnn_cell)
outputs,last_state=tf.nn.static_rnn(multi_rnn_cell,x,dtype=tf.float32)

其余代码完全相同,不再赘述
现展示1000次迭代过程,效果有明显改善,最近准确率超过99%,比单层RNN至少提高1%

plt.plot(loss_map[200::20])
plt.grid()
plt.show()
plt.plot(accu_map[200::20])
plt.grid()
plt.show()


刚刚展示的三层隐藏层神经元数量都是128,当然每一层可以选用不同的神经元数量

   rnn_cell1=tf.nn.rnn_cell.BasicLSTMCell(65,forget_bias=1.0)
    rnn_cell2=tf.nn.rnn_cell.BasicLSTMCell(128,forget_bias=1.0)
    rnn_cell3=tf.nn.rnn_cell.BasicLSTMCell(256,forget_bias=1.0)
    multi_rnn_cell=tf.nn.rnn_cell.MultiRNNCell([rnn_cell1,rnn_cell2,rnn_cell3])
    outputs,last_state=tf.nn.static_rnn(multi_rnn_cell,x,dtype=tf.float32)
return outputs[-1]

w=tf.Variable(tf.random_normal([256,classes]))  #这里的参数尺寸别忘了和最后一层隐藏层神经元数量保持统一
                                        ......                  

七、双向RNN(Bidrectional RNN)

双向RNN的基本概念:t时刻的输出可能会同时依赖于序列前面和后面的元素
需要将两个RNN混合输出:一个在正向执行,另一个在反向执行
使用这种架构,输出层可以同时获得过去和未来的状态信息


代码部分并不复杂,下面仅标出修改部分

import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import pandas as pd

#tf.reset_default_graph()
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("../data/mnist/", one_hot=True)
batch_size=128
step=28
input=28
hidden=128
classes=10
x=tf.placeholder(tf.float32,[None,step*input])
y=tf.placeholder(tf.float32,[None,classes])

def rnn(x):
    x=tf.reshape(x,[-1,step,input])
    x=tf.transpose(x,[1,0,2])
    x=tf.reshape(x,[-1,input])
    x=tf.split(axis=0,num_or_size_splits=step,value=x)
    #构建两个RNN对象
    rnn_fw_cell=tf.nn.rnn_cell.BasicLSTMCell(hidden,forget_bias=1.0)
    rnn_bw_cell=tf.nn.rnn_cell.BasicLSTMCell(hidden,forget_bias=1.0)
    #构建双向网络
    outputs,_,_=tf.nn.static_bidirectional_rnn(rnn_fw_cell,rnn_bw_cell,x,dtype=tf.float32)
    return outputs[-1]
# 权重系数个数x2
w=tf.Variable(tf.random_normal([2*hidden,classes]))
b=tf.Variable(tf.random_normal([classes,]))
l=tf.matmul(rnn(x),w)+b
pre=tf.nn.softmax(l)
loss=tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=l,labels=y))
opti=tf.train.AdamOptimizer(0.01).minimize(loss)
temp=tf.equal(tf.arg_max(y,1),tf.arg_max(pre,1))
accurance=tf.reduce_mean(tf.cast(temp,tf.float32))
sess=tf.Session()
sess.run(tf.global_variables_initializer())
loss_map=[]
accu_map=[]
for i in range(1000):
    batch_x,batch_y=mnist.train.next_batch(batch_size)
    sess.run(opti,{x:batch_x,y:batch_y})
    cost,accu=sess.run([loss,accurance],{x:batch_x,y:batch_y})
    loss_map.append(cost)
    accu_map.append(accu)
plt.plot(loss_map[200::20])
plt.show()
plt.plot(accu_map[200::20])
plt.show()

效果也是不错的

参考资料:
三个老外:《TensorFlow深度学习》
李嘉旋:《Tensorflow技术解析与实战》
Nick McClure:Tensorflow机器学习实战指南
官方文档:tf.nn.rnn_cell.BasicRNNCell
理解LSTM网络
tf.nn.rnn_cell.BasicLSTMCell函数用法
ValueError: Variable rnn/basic_lstm_cell/kernel already exists, disallowed.
Tensorflow unhashable type 'list' in sess.run
Cannot stack LSTM with MultiRNNCell and dynamic_rnn
Value error from tf.nn.dynamic_rnn: Dimensions must be equal
用「动图」和「举例子」讲讲 RNN

 
4 months ago

一.背景介绍

dropout起源与2012年的AlexNet,为了防止或减轻过拟合而使用的函数,它一般用在全连接层
该函数简单理解就是每次迭代以一定的概率随机丢弃部分神经元,因为每次丢弃的神经元不确定,从而降低网络对个别特征的依赖


在实际操作过程中有一个“keep_prob”参数,该参数范围是 (0 ,1],左开右闭
表示每个神经元有“keep_prob”的概率变成原来的 1/keep_prob,有“1-keep_prob”的概率被赋值为0(即丢弃)

tf.nn.dropout(x, keep_prob, noise_shape=None, seed=None,name=None)

需要注意:不同的框架这里的参数代表意义不同,tensorflow这里指保留率,keras这个参数指舍弃率

二.使用经验小结

1.网络层级越深、结构约复杂,dropout使用效果越好

2.通常使用0.5-0.8的保留率,即保留多数神经元

Srivastava在论文中发现:使用0.4-0.8的保留率,效果最好

3.通常用于全连接层

除了过拟合考虑,全连接层参数最多,最需要dropout来降低计算量
通常使用0.5的保留率,应该0.5产生的网络多样性最丰富

4.可用于输入层

这里的 保留率需要设置的比较高,例如0.8

5.很少见到卷积层后接dropout

原因主要是:卷积参数少,不易过拟合。即使使用,也选择比较高的 保留率

Srivastava等人将Dropout应用于前馈神经网络和受限玻尔兹曼机,注意到隐层0.5,输入层0.8的保留率适用于各种任务。

dropout的缺点就在于训练时间是没有dropout网络的2-3倍(待考证)

参考资料:
TensorFlow学习---tf.nn.dropout防止过拟合
Keras官方中文文档:Dropout层
Dropout Regularization in Deep Learning Models With Keras
理解dropout
开篇论文:Dropout: A Simple Way to Prevent Neural Networks from Overfitting

 
4 months ago

1.背景介绍

所有的监督学习都面临一个巨大问题:维度灾难,即随着输入空间维度都增加,模型的性能逐渐变差
这是因为随着维度的增加,从输入中活的足够信息所需的样本呈植树机增加
为解决这些问题,出现了一些优化网络,如自编码网络、玻尔兹曼机、主成分分析
其中主成分分析和自编码网络类似,这里仅介绍自编码网络

2.自编码网络介绍


自编码网络是一个对称网络结构,中间若干隐藏层分为压缩和解压,或者叫编码和解码两个过程
对于一个训练好的自编码网络,最中间那个隐藏层代表了原数据的精髓
有时神经网络要接受高纬度的输入信息, 比如输入信息是高清图片时, 输入信息量可能达到上千万
让神经网络直接从上千万个信息源中学习是一件很吃力的工作
所以, 何不压缩一下, 提取出原图片中的最具代表性的信息, 缩减输入信息量, 再把缩减过后的信息放进神经网络学习
这样学习起来就简单轻松了

3.实现一个最基本的自编码网络

使用mnist数据集,构建包含四个隐藏层的自编码网络,各层神经元个数分别为:784,256,128,128,256,784
所谓的编码、解码在实际操作上就是 线性操作(对原始数据进行缩放和平移) 后使用sigmoid激活函数

import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import pandas as pd

# 1.准备mnist数据集
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("../data/mnist/", one_hot=True)
# 输入数据占位符
data=tf.placeholder(tf.float32,[None,784])
# 2.第一层:编码
w1=tf.Variable(tf.random_normal([784,256]))
b1=tf.Variable(tf.random_normal([256,]))
l1=tf.matmul(data,w1)+b1
encode1=tf.nn.softmax(l1)
# 3.第二层:编码
w2=tf.Variable(tf.random_normal([256,128]))
b2=tf.Variable(tf.random_normal([128,]))
l2=tf.matmul(encode1,w2)+b2
encode2=tf.nn.softmax(l2)
# 4.第三层:解码
w3=tf.Variable(tf.random_normal([128,256]))
b3=tf.Variable(tf.random_normal([256,]))
l3=tf.matmul(encode2,w3)+b3
decode1=tf.nn.softmax(l3)
# 5.第四层:解码
w4=tf.Variable(tf.random_normal([256,784]))
b4=tf.Variable(tf.random_normal([784,]))
l4=tf.matmul(encode1,w4)+b4
decode2=tf.nn.softmax(l4)
# 6.构建均方误差损失函数
loss=tf.reduce_mean(tf.pow(decode2-data,2))
loss_map=[]
# 7.创建优化器
opti=tf.train.RMSPropOptimizer(0.01).minimize(loss)
# 8.创建会话,采用小批量梯度下降开始训练
sess=tf.Session()
sess.run(tf.global_variables_initializer())
for i in range(100):
    batch_train_image, _ = mnist.train.next_batch(128)
    sess.run(opti,{data:batch_train_image})
    loss_map.append(sess.run(loss,{data:batch_train_image}))
plt.plot(list(range(len(loss_map))),loss_map)

先训练100次,小试一下效果


损失函数依然是惨不忍睹,测试集找个图片验证一下效果吧

# 9.测试:测试集中随机选取一张图片看一下测试效果
test_pic, _ =mnist.test.next_batch(1)
plt.imshow(test_pic.reshape(28,28))
plt.show()
pre_pic=sess.run(decode2,{data:test_pic})
plt.imshow(pre_pic.reshape(28,28))
plt.show()



上面是原图,下面是经过十次训练后生成的图形:虽然只模拟出了几个像素点,但是还是可以脑补出全貌的,😄😄

4.增加自编码器的鲁棒性(健壮性)

在每一个sigmoid函数后面(无论编码层还是解码层)增加dropout,关键代码只有一行,这里不做单独展示
tf.nn.dropout(x, keep_prob, noise_shape=None, seed=None, name=None)

5.构建去噪自编码器(输入数据加上随机噪音)

关键位置代码在这里

for i in range(100):
    batch_train_image, _ = mnist.train.next_batch(batch_size)
    batch_train_image += 0.3 * np.random.randn(batch_size,784) #输入数据加上随机 标准正态分布 噪音
    sess.run(opti,{data:batch_train_image})
    loss_map.append(sess.run(loss,{data:batch_train_image}))

6.卷积自编码器

结合以上所有知识点,我们构建卷积神经网络的自编码架构



编码器:三个卷积层----卷积后sigmoid激活
tf.nn.conv2d(input,filter,strides,padding,use_cudnn_on_gpu=True,data_format='NHWC',
dilations=[1, 1, 1, 1],name=None
)

解码层:三个反卷积层----反卷积后sigmoid激活
tf.nn.conv2d_transpose(value,filter,output_shape,strides,padding='SAME',
data_format='NHWC',name=None)

这里有一个重难点:反卷积操作 “tf.nn.conv2d_transpose”
1)反卷积层的卷积核尺寸跟常规理解不同:需要输入对应的卷积层的卷积核尺寸
2)跟常规的卷积操作相比,多了一个 "output_shape"参数,这里指的是:你期待反卷积操作生成的形状
开始时候不大好理解,我手绘了一张图,注意观察各层卷积核大小和各层输出数据尺寸,需要细细揣摩


下面开始撸代码

import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import pandas as pd

# 1.准备mnist数据集
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("../data/mnist/", one_hot=True)
# 输入数据占位符(需要转换成四维张量)
data=tf.placeholder(tf.float32,[None,28,28,1])
batch_size=128
keepprob=tf.placeholder(tf.float32,[])
# 2.第一层:卷积编码
k1=tf.Variable(tf.random_normal([5,5,1,16]))
conv1=tf.nn.conv2d(data,k1,[1,2,2,1],"SAME")
b1=tf.Variable(tf.random_normal([16,]))
l1=tf.add(conv1,b1)
encode1=tf.nn.sigmoid(l1)
# 为防止过拟合,每层输出加入dropout,随机丢弃神经元
drop1=tf.nn.dropout(encode1,keepprob)
# 3.第二层:卷积编码
k2=tf.Variable(tf.random_normal([5,5,16,32]))
conv2=tf.nn.conv2d(drop1,k2,[1,2,2,1],"SAME")
b2=tf.Variable(tf.random_normal([32,]))
l2=tf.add(conv2,b2)
encode2=tf.nn.sigmoid(l2)
drop2=tf.nn.dropout(encode2,keepprob)
# 4.第三层:卷积编码
k3=tf.Variable(tf.random_normal([5,5,32,64]))
conv3=tf.nn.conv2d(drop2,k3,[1,2,2,1],"SAME")
b3=tf.Variable(tf.random_normal([64,]))
l3=tf.add(conv3,b3)
encode3=tf.nn.sigmoid(l3)
drop3=tf.nn.dropout(encode3,keepprob)
# 5.第四层:反卷积解码
k4=tf.Variable(tf.random_normal([5,5,32,64]))
conv_trans1=tf.nn.conv2d_transpose(drop3,k4,[batch_size,7,7,32],[1,2,2,1],"SAME")
b4=tf.Variable(tf.random_normal([32,]))
l4=tf.add(conv_trans1,b4)
decode1=tf.nn.sigmoid(l4)
drop4=tf.nn.dropout(decode1,keepprob)
# 6.第五层:反卷积解码
k5=tf.Variable(tf.random_normal([5,5,16,32]))
conv_trans2=tf.nn.conv2d_transpose(drop4,k5,[batch_size,14,14,16],[1,2,2,1],"SAME")
b5=tf.Variable(tf.random_normal([16,]))
l5=tf.add(conv_trans2,b5)
decode2=tf.nn.sigmoid(l5)
drop5=tf.nn.dropout(decode2,keepprob)
# 7.第六层:反卷积解码
k6=tf.Variable(tf.random_normal([5,5,1,16]))
conv_trans3=tf.nn.conv2d_transpose(drop5,k6,[batch_size,28,28,1],[1,2,2,1],"SAME")
b6=tf.Variable(tf.random_normal([1,]))
l6=tf.add(conv_trans3,b6)
decode3=tf.nn.sigmoid(l6)
drop6=tf.nn.dropout(decode3,keepprob)
# 8.构建均方误差损失函数
loss=tf.reduce_mean(tf.pow(drop6-data,2))
loss_map=[]
# 9.创建优化器(adam)
opti=tf.train.AdamOptimizer(0.001).minimize(loss)
# 10.创建会话,采用小批量梯度下降开始训练
sess=tf.Session()
sess.run(tf.global_variables_initializer())
for i in range(100):
    batch_train_image, _ = mnist.train.next_batch(batch_size)
    #加入随机噪音
    batch_train_image += 0.3 * np.random.randn(batch_size,784)
    batch_train_image = batch_train_image.reshape([-1,28,28,1])
    sess.run(opti,{data:batch_train_image,keepprob:0.5})
    loss_map.append(sess.run(loss,{data:batch_train_image,keepprob:0.5}))
plt.plot(list(range(len(loss_map))),loss_map)

这段代码用于训练阶段完美运行


但是用来预测就会爆出问题

# 11.随机挑选一个测试集模型来预测
test_pic, _ =mnist.test.next_batch(1)
plt.imshow(test_pic.reshape(28,28))
plt.show()
pre_pic=sess.run(drop6,{data:test_pic.reshape([-1,28,28,1]),keepprob:0.5})
plt.imshow(pre_pic.reshape(28,28))
plt.show()


仔细看报错描述,问题出现在反卷积层:
定义的输出维度为128,7,7,32,但是我们预测时传进去的是[1,7,7,32]
也就是训练样本和预测样本数量不一致的情形,这样问题就好解决了
这里的传入参数不能为常量了,应该是一个变量 tf.shape(data)[0],问题完美解决
最后附上完美代码

import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import pandas as pd

# 1.准备mnist数据集
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("../data/mnist/", one_hot=True)
# 输入数据占位符(需要转换成四维张量)
data=tf.placeholder(tf.float32,[None,28,28,1])
batch_size=128
keepprob=tf.placeholder(tf.float32,[])
# 2.第一层:卷积编码
k1=tf.Variable(tf.random_normal([5,5,1,16]))
conv1=tf.nn.conv2d(data,k1,[1,2,2,1],"SAME")
b1=tf.Variable(tf.random_normal([16,]))
l1=tf.add(conv1,b1)
encode1=tf.nn.sigmoid(l1)
drop1=tf.nn.dropout(encode1,keepprob)
# 3.第二层:卷积编码
k2=tf.Variable(tf.random_normal([5,5,16,32]))
conv2=tf.nn.conv2d(drop1,k2,[1,2,2,1],"SAME")
b2=tf.Variable(tf.random_normal([32,]))
l2=tf.add(conv2,b2)
encode2=tf.nn.sigmoid(l2)
drop2=tf.nn.dropout(encode2,keepprob)
# 4.第三层:卷积编码
k3=tf.Variable(tf.random_normal([5,5,32,64]))
conv3=tf.nn.conv2d(drop2,k3,[1,2,2,1],"SAME")
b3=tf.Variable(tf.random_normal([64,]))
l3=tf.add(conv3,b3)
encode3=tf.nn.sigmoid(l3)
drop3=tf.nn.dropout(encode3,keepprob)
# 5.第四层:反卷积解码
k4=tf.Variable(tf.random_normal([5,5,32,64]))
conv_trans1=tf.nn.conv2d_transpose(drop3,k4,[tf.shape(data)[0],7,7,32],[1,2,2,1],"SAME")
b4=tf.Variable(tf.random_normal([32,]))
l4=tf.add(conv_trans1,b4)
decode1=tf.nn.sigmoid(l4)
drop4=tf.nn.dropout(decode1,keepprob)
# 6.第五层:反卷积解码
k5=tf.Variable(tf.random_normal([5,5,16,32]))
conv_trans2=tf.nn.conv2d_transpose(drop4,k5,[tf.shape(data)[0],14,14,16],[1,2,2,1],"SAME")
b5=tf.Variable(tf.random_normal([16,]))
l5=tf.add(conv_trans2,b5)
decode2=tf.nn.sigmoid(l5)
drop5=tf.nn.dropout(decode2,keepprob)
# 6.第六层:反卷积解码
k6=tf.Variable(tf.random_normal([5,5,1,16]))
conv_trans3=tf.nn.conv2d_transpose(drop5,k6,[tf.shape(data)[0],28,28,1],[1,2,2,1],"SAME")
b6=tf.Variable(tf.random_normal([1,]))
l6=tf.add(conv_trans3,b6)
decode3=tf.nn.sigmoid(l6)
drop6=tf.nn.dropout(decode3,keepprob)
# 8.构建均方误差损失函数
loss=tf.reduce_mean(tf.pow(drop6-data,2))
loss_map=[]
# 9.创建优化器
opti=tf.train.AdamOptimizer(0.001).minimize(loss)
# 10.创建会话,采用小批量梯度下降开始训练
sess=tf.Session()
sess.run(tf.global_variables_initializer())
for i in range(100):
    batch_train_image, _ = mnist.train.next_batch(batch_size)
    #加入随机噪音
    batch_train_image += 0.3 * np.random.randn(batch_size,784)
    batch_train_image = batch_train_image.reshape([-1,28,28,1])
    sess.run(opti,{data:batch_train_image,keepprob:0.5})
    loss_map.append(sess.run(loss,{data:batch_train_image,keepprob:0.5}))
plt.plot(list(range(len(loss_map))),loss_map)
# 11.随机挑选一个测试集模型来预测
test_pic, _ =mnist.test.next_batch(1)
plt.imshow(test_pic.reshape(28,28))
plt.show()
pre_pic=sess.run(drop6,{data:test_pic.reshape([-1,28,28,1]),keepprob:0.5})
plt.imshow(pre_pic.reshape(28,28))
plt.show()

7.卷积自编码器(GPU版本)

我们稍加修改,创建了一个GPU版本的卷积自编码器,拿到colab平台上运行100万次,看一看最终效果
由于是在线运行,mnist数据集也采取了在线版本 "tf.keras.datasets.mnist.load_data()"
该数据集存储格式略有不同,需要变更些许代码
同时在这里实现一个简单的“next_batch”,用于获取小批量训练数据

import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import pandas as pd
from tensorflow import keras

(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()

with tf.device('/gpu:0'):
    # 输入数据占位符(需要转换成四维张量)
    data = tf.placeholder(tf.float32, [None, 28, 28, 1])
    batch_size = 128
    keepprob = tf.placeholder(tf.float32, [])
    # 2.第一层:卷积编码
    k1 = tf.Variable(tf.random_normal([5, 5, 1, 16]))
    conv1 = tf.nn.conv2d(data, k1, [1, 2, 2, 1], "SAME")
    b1 = tf.Variable(tf.random_normal([16, ]))
    l1 = tf.add(conv1, b1)
    encode1 = tf.nn.sigmoid(l1)
    # 为防止过拟合,每层输出加入dropout,随机丢弃神经元
    drop1 = tf.nn.dropout(encode1, keepprob)
    # 3.第二层:卷积编码
    k2 = tf.Variable(tf.random_normal([5, 5, 16, 32]))
    conv2 = tf.nn.conv2d(drop1, k2, [1, 2, 2, 1], "SAME")
    b2 = tf.Variable(tf.random_normal([32, ]))
    l2 = tf.add(conv2, b2)
    encode2 = tf.nn.sigmoid(l2)
    drop2 = tf.nn.dropout(encode2, keepprob)
    # 4.第三层:卷积编码
    k3 = tf.Variable(tf.random_normal([5, 5, 32, 64]))
    conv3 = tf.nn.conv2d(drop2, k3, [1, 2, 2, 1], "SAME")
    b3 = tf.Variable(tf.random_normal([64, ]))
    l3 = tf.add(conv3, b3)
    encode3 = tf.nn.sigmoid(l3)
    drop3 = tf.nn.dropout(encode3, keepprob)
    # 5.第四层:反卷积解码
    k4 = tf.Variable(tf.random_normal([5, 5, 32, 64]))
    conv_trans1 = tf.nn.conv2d_transpose(drop3, k4, [batch_size, 7, 7, 32], [1, 2, 2, 1], "SAME")
    b4 = tf.Variable(tf.random_normal([32, ]))
    l4 = tf.add(conv_trans1, b4)
    decode1 = tf.nn.sigmoid(l4)
    drop4 = tf.nn.dropout(decode1, keepprob)
    # 6.第五层:反卷积解码
    k5 = tf.Variable(tf.random_normal([5, 5, 16, 32]))
    conv_trans2 = tf.nn.conv2d_transpose(drop4, k5, [batch_size, 14, 14, 16], [1, 2, 2, 1], "SAME")
    b5 = tf.Variable(tf.random_normal([16, ]))
    l5 = tf.add(conv_trans2, b5)
    decode2 = tf.nn.sigmoid(l5)
    drop5 = tf.nn.dropout(decode2, keepprob)
    # 7.第六层:反卷积解码
    k6 = tf.Variable(tf.random_normal([5, 5, 1, 16]))
    conv_trans3 = tf.nn.conv2d_transpose(drop5, k6, [batch_size, 28, 28, 1], [1, 2, 2, 1], "SAME")
    b6 = tf.Variable(tf.random_normal([1, ]))
    l6 = tf.add(conv_trans3, b6)
    decode3 = tf.nn.sigmoid(l6)
    drop6 = tf.nn.dropout(decode3, keepprob)
    # 8.构建均方误差损失函数
    loss = tf.reduce_mean(tf.pow(drop6 - data, 2))
    loss_map = []
    # 9.创建优化器(adam)
    opti = tf.train.AdamOptimizer(0.001).minimize(loss)
    
    def next_batch(data, step, batch_size):
        offset = (step * batch_size) % (data.shape[0] - batch_size)  # 精妙利用取余功能,赞叹
        batch_data = data[offset: offset + batch_size]
        return batch_data.reshape([-1,28,28,1])

# 10.创建会话,采用小批量梯度下降开始训练
sess=tf.Session(config=tf.ConfigProto(allow_soft_placement=True, log_device_placement=True))
sess.run(tf.global_variables_initializer())
for i in range(1000000):
    batch_train_image= next_batch(train_images,i,128)
    batch_train_image = batch_train_image.reshape([-1,28,28,1])
    sess.run(opti,{data:batch_train_image,keepprob:0.5})
    loss_map.append(sess.run(loss,{data:batch_train_image,keepprob:0.5}))
    
# 11.选取部分损失函数展示(毕竟100万数据同时展示太密集)
loss_map_2=loss_map[::1000]
plt.plot(list(range(len(loss_map_2))),loss_map_2)
plt.show()
# 12.测试单个样本
test_pic=test_images[:1,:,:]
plt.imshow(test_pic.reshape(28,28))
plt.show()
pre_pic=sess.run(decode2,{data:test_pic.reshape([-1,28,28,1]),keepprob:0.5})
plt.imshow(pre_pic.reshape(28,28))
plt.show()

8.自编码网络的使用场景

异常检测
数据去噪(例如图像,音频)
图像修复
信息检索

参考资料:
三个老外:TensorFlow深度学习
从自编码器到变分自编码器
自编码 (Autoencoder)
tf.nn.conv2d_transpose
Confused about conv2d_transpose
tensorflow卷积后的数据尺寸

 
4 months ago

1.先了解迭代器

迭代器是python中访问集合(列表、元祖、集合)的一种方式
不同于常规的一次性返回所有集合元素,迭代器是一次一个按顺序返回集合元素,调用迭代器一次生成一个
凡是包含iter()方法和next()方法的都可以称之为迭代器
python常用的列表、元祖、集合以及内置了这两种方法,我们直接调用就可以了

>>>list=[1,2,3,4]
>>> it = iter(list)    # 创建迭代器对象:调用iter方法
>>> print (next(it))   # 首次调用迭代器:输出迭代器的第一个元素
1
>>> print (next(it))   # 再次调用迭代器:输出迭代器的下一个元素
2
>>> print (next(it))   # 再次调用迭代器:输出迭代器的下一个元素
3
>>> print (next(it))   # 再次调用迭代器:输出迭代器的下一个元素
4
>>> print (next(it))   # 再次调用迭代器:迭代器输出全部元素后,开始报错
StopIteration

上面这种方法看起来太傻了,可以循环调用元素

import sys         # 引入 sys 模块
 
list=[1,2,3,4]
it = iter(list)    # 创建迭代器对象
 
while True:
    try:
        print(next(it))
    except StopIteration:  # 手动处理报错信息
        sys.exit()

除了使用next()方法,还可以使用for循环遍历迭代器对象内元素

list=[1,2,3,4]
it = iter(list)    # 创建迭代器对象
for x in it:       # for循环调用迭代器对象(元素调用完后自动退出)
    print(x, end=" ")

可以自己生成一个迭代器

class MyNumbers(object):
  def __iter__(self):
    self.a = 1
    return self
 
  def __next__(self):
    x = self.a
    self.a += 1
    return x
 
myclass = MyNumbers()
myiter = iter(myclass)
 
print(next(myiter))

2.生成器

正常函数使用return进行返回,使用yield返回的函数称之为生成器
调用生成器会返回一个迭代器对象,接下来就是调用迭代器的方法了

def Mynumber(): #yield创建生成器
    for i in range(10):
        yield i

myclass = Mynumber() #调用生成器,返回迭代器

print(next(myiter)) #调用迭代器内元素

思路要清晰,不然就晕了

参考资料:
python3 迭代器与生成器

 
4 months ago

深度学习中,需要解决一个最困难的问题不是有关神经网络的问题,而是如何获取具有正确格式的正确数据
本次从Kaggle平台获取一个表情识别数据集,构建CNN网络来识别图片里面人物的表情

1.数据集介绍

首先到Kaggle官网上下载该facial-keypoints-detector数据集

输入:48x48像素灰度值(0到255之间)
目标:情绪类别(0到6之间:愤怒= 0,厌恶= 1,恐惧= 2,快乐= 3,悲伤= 4,惊喜= 5,中立= 6)
可以使用的辅助信息,但不能作为在测试时预测情绪的输入:identity(-1:未知,正整数= ID,不是所有连续的整数值)。辅助文件包含与所有训练示例相关联的标识。

数据下载解压后出现三个文件,一个训练集,一个测试集,还有一个文件不知道是什么东东
而且测试集居然没有标签,看来这个测试集是没法用了,只能从训练集中手动划出一部分做为测试集来验证准确率


网上有现成的函数用来处理原始数据集,这里我们尝试手工处理

2.原始数据预处理

整个过程各种艰辛,还好本人有强大的内心和扎实的代码功底
现在简单回忆一下推理过程






最后的代码只有寥寥几行搞定

def dataset_preprocess(dataset,image_size=48):
    labels = dataset["Emotion"]
    labels = tf.one_hot(labels,depth=10,axis=1,dtype=tf.float32)
    images = dataset["Pixels"].str.split(' ', expand=True)
    images = images.values.reshape([-1,image_size,image_size,1]).astype(np.float32)

    return images,labels

3.CNN网络完整代码

构建两层卷积神经网络和两层全连接神经网络



由于原本的测试集没有标签,这次就暂不验证准确率了,仅仅展示一下损失函数变化过程

import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

def dataset_preprocess(dataset,image_size=48):
    labels = dataset["Emotion"]
    labels = tf.one_hot(labels,depth=10,axis=1,dtype=tf.float32)
    images = dataset["Pixels"].str.split(' ', expand=True)
    images = images.values.reshape([-1,image_size,image_size,1]).astype(np.float32)

    return images,labels

def face_detect_CNN_model(input_data):
    ## 第一层卷积
    # 初始化权重
    k1 = tf.Variable(tf.random_normal([5, 5, 1, 32]))
    b1 = tf.Variable(tf.random_normal([32, ]))
    # 卷积
    conv1 = tf.nn.conv2d(input_data, k1, [1, 1, 1, 1], "SAME")
    # 激活
    act1 = tf.nn.relu(conv1 + b1)
    # 池化
    pool1 = tf.nn.max_pool(act1, [1, 2, 2, 1], [1, 2, 2, 1], "SAME")

    ## 第二层卷积
    # 初始化权重
    k2 = tf.Variable(tf.random_normal([3, 3, 32, 64]))
    b2 = tf.Variable(tf.random_normal([64, ]))
    # 卷积
    conv2 = tf.nn.conv2d(pool1, k2, [1, 1, 1, 1], "SAME")
    # 激活
    act2 = tf.nn.relu(conv2 + b2)
    # 池化
    pool2 = tf.nn.max_pool(act2, [1, 2, 2, 1], [1, 2, 2, 1], "SAME")

    ## 第三层全连接
    # 把输入转化成一阶
    num = pool2.shape[1].value * pool2.shape[2].value * pool2.shape[3].value
    flat = tf.reshape(pool2, [-1, num])
    w3 = tf.Variable(tf.random_normal([num, 256]))
    b3 = tf.Variable(tf.random_normal([256, ]))
    # 线性操作
    l3 = tf.matmul(flat, w3) + b3
    # 激活
    act3 = tf.nn.relu(l3)

    ## 第四层全连接(输出层)
    w4 = tf.Variable(tf.random_normal([256, 10]))
    b4 = tf.Variable(tf.random_normal([10, ]))
    # 线性操作
    l4 = tf.matmul(act3, w4) + b4

    return l4


def main():
    image_size=48
    # 特征值占位
    input_data = tf.placeholder(tf.float32, [None, image_size, image_size, 1])
    # 目标值占位
    true = tf.placeholder(tf.float32, [None, 10])
    # 导入数据集
    train_dataset = pd.read_csv("./facial-keypoints-detector/train.csv")
    #test_dataset = pd.read_csv("./facial-keypoints-detector/test.csv")
    train_images,train_labels=dataset_preprocess(train_dataset,image_size)
    #test_images,test_labels=dataset_preprocess(test_dataset,image_size)

    # 损失函数
    model = face_detect_CNN_model(input_data)
    loss = tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(labels=true, logits=model))
    # 优化器
    opti = tf.train.GradientDescentOptimizer(0.001).minimize(loss)
    loss_map = []

    # 创建会话
    sess=tf.Session()
    sess.run(tf.global_variables_initializer())
    for i in range(10):
        sess.run(opti, {input_data: train_images, true: sess.run(train_labels)})
        loss_map.append(sess.run(loss, {input_data: train_images, true: sess.run(train_labels)}))

    plt.plot([i for i in range(len(loss_map))], loss_map)
    plt.show()


if __name__ == '__main__':
    main()


最后展示的图片是空白的,打印初损失函数后发现原理出现了梯度爆炸情况
(笨呀,其实从图上坐标就可以看出问题,无需打印损失函数)

[7.871837e+33, nan, nan, nan, nan, nan, nan, nan, nan, nan]

拼命尝试加入了许多办法,诸如加入l2正则和dropout,甚至喜出望外地发现原始数据忘了归一化,然后都没有解决问题
最后发现只需要将梯度下降法由传统方式改为AdamOptimizer,即可解决梯度爆炸问题

4.代码优化

上面的代码依然存在 训练时间太长,没有测试集等缺陷,现在着手进行优化

1.手动实现小批量梯度下降

next_batch函数的实现

def get_next_batch(data, step):
    offset = (step * BATCH_SIZE) % (images.shape[0] - BATCH_SIZE) #精妙利用取余功能,赞叹
    batch_data = data[offset: offset + BATCH_SIZE]
    return batch_data

2.从原来数据集中划出其中的0.3作为测试集

这个代码比较简单,不单独列出

3.手动实现one-hot编码

使用一下神奇的map函数

def create_onehot_label(x):
        label = np.zeros((1, labels_nums), dtype=np.float32)
    label[:, int(x)] = 1
    return label

train_labels = np.array(list(map(create_onehot_label, data_frame['Emotion'].values))).reshape(-1, labels_nums)

5.更新后完整代码

import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

def dataset_preprocess(dataset,image_size=48,labels_nums=7):
    # 原始数据中提取labels
    labels = dataset["Emotion"]
    # 构建one-hot编码
    def create_onehot_label(x):
        label = np.zeros((1, labels_nums), dtype=np.float32)

        label[:, int(x)] = 1
        return label
    labels = np.array(list(map(create_onehot_label, labels.values))).reshape(-1, labels_nums)

    # 原始数据中提取images
    images = dataset["Pixels"].str.split(' ', expand=True)
    images = images.values.reshape([-1,image_size,image_size,1]).astype(np.float32)/255.0

    # 划分训练集和测试集
    length=labels.shape[0]
    train_labels=labels[:int(length*0.8),:]
    test_labels=labels[int(length*0.8):length,:]
    train_images=images[:int(length*0.8),:,:,:]
    test_images=images[int(length*0.8):length,:,:,:]

    return train_images,train_labels,test_images,test_labels

def get_next_batch(data, step, batch_size):
    offset = (step * batch_size) % (data.shape[0] - batch_size) #精妙利用取余功能,赞叹
    batch_data = data[offset: offset + batch_size]
    return batch_data

def face_detect_CNN_model(input_data):
    ## 第一层卷积
    # 初始化权重
    k1 = tf.Variable(tf.random_normal([5, 5, 1, 32]))
    b1 = tf.Variable(tf.random_normal([32, ]))
    # 卷积
    conv1 = tf.nn.conv2d(input_data, k1, [1, 1, 1, 1], "SAME")
    # 激活
    act1 = tf.nn.relu(conv1 + b1)
    # 池化
    pool1 = tf.nn.max_pool(act1, [1, 2, 2, 1], [1, 2, 2, 1], "SAME")
    # 加入L2正则
    tf.add_to_collection("losses",tf.nn.l2_loss(k1))
    tf.add_to_collection("losses",tf.nn.l2_loss(b1))

    ## 第二层卷积
    # 初始化权重
    k2 = tf.Variable(tf.random_normal([3, 3, 32, 64]))
    b2 = tf.Variable(tf.random_normal([64, ]))
    # 卷积
    conv2 = tf.nn.conv2d(pool1, k2, [1, 1, 1, 1], "SAME")
    # 激活
    act2 = tf.nn.relu(conv2 + b2)
    # 池化
    pool2 = tf.nn.max_pool(act2, [1, 2, 2, 1], [1, 2, 2, 1], "SAME")
    # 加入L2正则
    tf.add_to_collection("losses",tf.nn.l2_loss(k2))
    tf.add_to_collection("losses",tf.nn.l2_loss(b2))

    ## 第三层全连接
    # 把输入转化成一阶
    num = pool2.shape[1].value * pool2.shape[2].value * pool2.shape[3].value
    flat = tf.reshape(pool2, [-1, num])
    w3 = tf.Variable(tf.random_normal([num, 256]))
    b3 = tf.Variable(tf.random_normal([256, ]))
    # 线性操作
    l3 = tf.matmul(flat, w3) + b3
    # 激活
    act3 = tf.nn.relu(l3)
    # 加入dropout
    drop=tf.nn.dropout(act3,keep_prob=0.5)

    ## 第四层全连接(输出层)
    w4 = tf.Variable(tf.random_normal([256, 7]))
    b4 = tf.Variable(tf.random_normal([7, ]))
    # 线性操作
    l4 = tf.matmul(drop, w4) + b4

    return l4

def main():
    image_size=48
    batch_size=128
    labels_nums=7
    # 特征值占位
    input_data = tf.placeholder(tf.float32, [None, image_size, image_size, 1])
    # 目标值占位
    true = tf.placeholder(tf.float32, [None, 7])
    # 导入数据集
    dataset = pd.read_csv("./facial-keypoints-detector/train.csv")
    train_images,train_labels,test_images,test_labels=dataset_preprocess(dataset,image_size,labels_nums)

    # 损失函数
    model = face_detect_CNN_model(input_data)
    loss = tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(labels=true, logits=model))
    ##损失函数加入L2正则
    reg_losses = tf.add_n(tf.get_collection("losses"))
    loss = loss + 0.01*reg_losses
    loss_map = []

    # 优化器
    opti = tf.train.AdamOptimizer(0.001).minimize(loss)

    # 准确率
    predict = tf.nn.softmax(model)
    temp = tf.equal(tf.arg_max(predict, 1), tf.arg_max(true, 1))
    temp = tf.cast(temp, tf.float32)
    accurancy = tf.reduce_mean(temp)
    acc_map = []

    # 创建会话
    sess=tf.Session()
    sess.run(tf.global_variables_initializer())
    for i in range(100):
        batch_train_images=get_next_batch(train_images,i,batch_size)
        batch_train_labels=get_next_batch(train_labels,i,batch_size)
        sess.run(opti, {input_data:batch_train_images , true: batch_train_labels})
        loss_map.append(sess.run(loss, {input_data: batch_train_images, true: batch_train_labels}))
        acc_map.append(sess.run(accurancy, {input_data: test_images, true: test_labels}))

    plt.plot(list(range(len(loss_map))), loss_map)
    plt.show()
    plt.plot(list(range(len(acc_map))), acc_map)
    plt.show()


if __name__ == '__main__':
    main()

训练100次简单看一下效果


6.三天后补记

有没有发现这里的损失函数依然是大的惊人,而且非常地不科学,是不是有什么问题
其实我早就发现了,只是多遍检查后百思不得其姐姐,今天突然回过神来,睁大眼睛看一下损失函数这里


把损失函数改成reduce_mean之后果然正常了好多,这种马大哈问题不知道之前有几次一摸一样的了
改完后是酱紫的,也就损失函数的数值被平均后稍微好看些了吧

参考资料:
三个老外:《tensorflow深度学习》
Kaggle官方数据集
Github:EmotionDetectorUtils.py

 
4 months ago

Keras是简易深度学习库,能快速建立深度神经网络模型,目前被tensorflow收编为高级API
还有一点比较神奇的是,keras能够无缝在CPU和GPU进行切换,非常友好,请看中文官方文档原话
如果你以 TensorFlow 或 CNTK 后端运行,只要检测到任何可用的 GPU,那么代码将自动在 GPU 上运行。

1.第一个Keras程序

import tensorflow as tf

#调用内置数据集
mnist = tf.keras.datasets.mnist
(x_train, y_train),(x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
#构建顺序模型--Sequential()构造器,用于线性堆叠多层网络
model = tf.keras.models.Sequential()
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(512, activation=tf.nn.relu))
model.add(tf.keras.layers.Dropout(0.2))
model.add(tf.keras.layers.Dense(10, activation=tf.nn.softmax))
#编译--配置学习过程,指定优化器、损失函数、评估标准
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
#训练模型
model.fit(x_train, y_train, epochs=5)
#评估模型
model.evaluate(x_test, y_test)

一个两层的全连接神经网络就这么搭建起来了,就是这么简单,一路add过去就ok了
先看一下内置数据集的样式


我们在colab上使用默认CPU(修改-笔记本设置里的“硬件加速器”选择“无”)运行一下


这里使用批量梯度下降,迭代了五次,每一次运行时间、损失函数、准确率都会自动显示

2.使用GPU运行

程序无需任何更改,只需要保证设备上有GPU:colab只需要选择“硬件加速器”为“GPU”,重新运行代码即可


这里可以看到,每一步的迭代时间减少了一半左右

3.使用TPU运行

代码需要少许更改,保证设备上有TPU:colab只需要选择“硬件加速器”为“TPU”

import tensorflow as tf

#调用内置数据集
mnist = tf.keras.datasets.mnist
(x_train, y_train),(x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
#构建顺序模型--Sequential()构造器,用于线性堆叠多层网络
model = tf.keras.models.Sequential()
model.add(tf.keras.layers.Flatten(input_shape=x_train.shape[1:]))#添加输入层尺寸,否则会报错
model.add(tf.keras.layers.Dense(512, activation=tf.nn.relu))
model.add(tf.keras.layers.Dropout(0.2))
model.add(tf.keras.layers.Dense(10, activation=tf.nn.softmax))

#关键代码在这里,需要一个TPU
tpu_model = tf.contrib.tpu.keras_to_tpu_model(
    model,
    strategy=tf.contrib.tpu.TPUDistributionStrategy(
        tf.contrib.cluster_resolver.TPUClusterResolver(tpu='grpc://' + os.environ['COLAB_TPU_ADDR'])
    )
)

#编译--配置学习过程,指定优化器、损失函数、评估标准
tpu_model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

#训练模型
tpu_model.fit(x_train, y_train, epochs=5)
#评估模型
tpu_model.evaluate(x_test, y_test)




这次提供的信息很多,还可以看到colab系统给我们提供了1个CPU,2个GPU,8个TPU,以及看到系统分配资源的过程
准确率并没有特大的变化,因为我们的数据集和算法并没有发生变化
训练时间似乎还不如CPU,应该消耗在并行任务管理用的时间比真实处理数据所用的时间更多吧
由于我们的数据集不够大,TPU的并行优势并没有体现出来

参考资料:
Keras中文官方文档
colab官方文档:Keras Fashion MNIST - TPU
使用TPU免费加速Keras模型训练

 
4 months ago

这次正式使用GPU开始训练,数据集采用mnist,构建五层设计网络,每层神经元个数:200,100,60,30,10

一、全连接神经网络(relu激活)

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
import datetime

t1_1 = datetime.datetime.now()
mnist = input_data.read_data_sets("./data/mnist", one_hot=True) #指定提前下载的数据集存放路径

config = tf.ConfigProto()
config.gpu_options.allow_growth = True

with tf.device('/gpu:0'):
# 特征值占位
    x=tf.placeholder(tf.float32,[None,784])
# 目标值占位
    true=tf.placeholder(tf.float32,[None,10])

# 初始化权重
    w1=tf.Variable(tf.random_normal([784,200]))
    b1=tf.Variable(tf.random_normal([200,]))
# 线性操作
    l1=tf.matmul(x,w1)+b1
# 激活
    act1=tf.nn.relu(l1)

    w2=tf.Variable(tf.random_normal([200,100]))
    b2=tf.Variable(tf.random_normal([100,]))
# 线性操作
    l2=tf.matmul(act1,w2)+b2
# 激活
    act2=tf.nn.relu(l2)
    
    w3=tf.Variable(tf.random_normal([100,60]))
    b3=tf.Variable(tf.random_normal([60,]))
# 线性操作
    l3=tf.matmul(act2,w3)+b3
# 激活
    act3=tf.nn.relu(l3)

    w4=tf.Variable(tf.random_normal([60,30]))
    b4=tf.Variable(tf.random_normal([30,]))
# 线性操作
    l4=tf.matmul(act3,w4)+b4
# 激活
    act4=tf.nn.relu(l4)

    w5=tf.Variable(tf.random_normal([30,10]))
    b5=tf.Variable(tf.random_normal([10,]))
# 线性操作
    l5=tf.matmul(act4,w5)+b5
# 激活
    #act5=tf.nn.softmax(l5)

# 损失函数
    loss=tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(labels=true,logits=l5))
# 优化器
    opti=tf.train.GradientDescentOptimizer(0.001).minimize(loss)
# 计算准确率
    predict=tf.nn.softmax(l5)
    temp=tf.equal(tf.arg_max(predict,1),tf.arg_max(true,1))
    temp=tf.cast(temp,tf.float32)
    accurancy=tf.reduce_mean(temp)
# 创建会话,开始训练
loss_map=[]
sess=tf.Session(config=tf.ConfigProto(allow_soft_placement=True, log_device_placement=True))
sess.run(tf.global_variables_initializer())
#定义每次传入的数据数量
batch_size=1000
num=int(mnist.train.images.shape[0]/batch_size)
for i in range(10):
    for i in range(num):
            # 默认从训练集中随机挑选batch_size个数据
        input,output=mnist.train.next_batch(batch_size)
        sess.run(opti,{x:input,true:output})
        loss_map.append(sess.run(loss,{x:input,true:output}))
        
print("准确率为:",sess.run(accurancy,{x:mnist.test.images,true:mnist.test.labels}))
t2_1 = datetime.datetime.now()
print(t2_1-t1_1)
plt.plot([i for i in range(len(loss_map))],loss_map)
plt.show()
sess.close()

运行结果:

Extracting ./data/mnist/train-images-idx3-ubyte.gz
Extracting ./data/mnist/train-labels-idx1-ubyte.gz
Extracting ./data/mnist/t10k-images-idx3-ubyte.gz
Extracting ./data/mnist/t10k-labels-idx1-ubyte.gz
准确率为: 0.1135
0:00:05.923043

同时损失函数变化图为空白
由于采用了relu激活,运算量相对小,我们接下来换成需要指数运算的sogmoid激活函数看一下

二、全连接神经网络(sigmoid激活)

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
t1_1 = datetime.datetime.now()
mnist = input_data.read_data_sets("./data/mnist", one_hot=True) #指定刚刚下载的数据集存放路径

# See https://www.tensorflow.org/tutorials/using_gpu#allowing_gpu_memory_growth
config = tf.ConfigProto()
config.gpu_options.allow_growth = True

with tf.device('/gpu:0'):
# 特征值占位
    x=tf.placeholder(tf.float32,[None,784])
# 目标值占位
    true=tf.placeholder(tf.float32,[None,10])

# 初始化权重
    w1=tf.Variable(tf.random_normal([784,200]))
    b1=tf.Variable(tf.random_normal([200,]))
# 线性操作
    l1=tf.matmul(x,w1)+b1
# 激活
    act1=tf.nn.sigmoid(l1)

    w2=tf.Variable(tf.random_normal([200,100]))
    b2=tf.Variable(tf.random_normal([100,]))
# 线性操作
    l2=tf.matmul(act1,w2)+b2
# 激活
    act2=tf.nn.sigmoid(l2)
    
    w3=tf.Variable(tf.random_normal([100,60]))
    b3=tf.Variable(tf.random_normal([60,]))
# 线性操作
    l3=tf.matmul(act2,w3)+b3
# 激活
    act3=tf.nn.sigmoid(l3)

    w4=tf.Variable(tf.random_normal([60,30]))
    b4=tf.Variable(tf.random_normal([30,]))
# 线性操作
    l4=tf.matmul(act3,w4)+b4
# 激活
    act4=tf.nn.sigmoid(l4)

    w5=tf.Variable(tf.random_normal([30,10]))
    b5=tf.Variable(tf.random_normal([10,]))
# 线性操作
    l5=tf.matmul(act4,w5)+b5
# 激活
    #act5=tf.nn.softmax(l5)

# 损失函数
    loss=tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(labels=true,logits=l5))
# 优化器
    opti=tf.train.GradientDescentOptimizer(0.001).minimize(loss)
# 计算准确率
    predict=tf.nn.softmax(l5)
    temp=tf.equal(tf.arg_max(predict,1),tf.arg_max(true,1))
    temp=tf.cast(temp,tf.float32)
    accurancy=tf.reduce_mean(temp)
# 创建会话,开始训练
loss_map=[]
sess=tf.Session(config=tf.ConfigProto(allow_soft_placement=True, log_device_placement=True))
sess.run(tf.global_variables_initializer())
#定义每次传入的数据数量
batch_size=1000
num=int(mnist.train.images.shape[0]/batch_size)
for i in range(10):
    for i in range(num):
            # 默认从训练集中随机挑选batch_size个数据
        input,output=mnist.train.next_batch(batch_size)
        sess.run(opti,{x:input,true:output})
        loss_map.append(sess.run(loss,{x:input,true:output}))
        
print("准确率为:",sess.run(accurancy,{x:mnist.test.images,true:mnist.test.labels}))
t2_1 = datetime.datetime.now()
print(t2_1-t1_1)
plt.plot([i for i in range(len(loss_map))],loss_map)
plt.show()
sess.close()


这次终于看到了明显结果:论激活函数正确选择的重要性

三、卷积神经网络

import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
import datetime
from tensorflow.examples.tutorials.mnist import input_data
t1_1 = datetime.datetime.now()
mnist = input_data.read_data_sets("./data/mnist", one_hot=True) #指定刚刚下载的数据集存放路径

# See https://www.tensorflow.org/tutorials/using_gpu#allowing_gpu_memory_growth
config = tf.ConfigProto()
config.gpu_options.allow_growth = True

with tf.device('/gpu:0'):
# 特征值占位
    x=tf.placeholder(tf.float32,[None,28,28,1])
# 目标值占位
    true=tf.placeholder(tf.float32,[None,10])

## 第一层卷积
# 初始化权重
    k1=tf.Variable(tf.random_normal([3,3,1,200]))
    b1=tf.Variable(tf.random_normal([200,]))
# 卷积
    conv1=tf.nn.conv2d(x,k1,[1,1,1,1],"SAME")
# 激活    
    act1=tf.nn.relu(conv1)+b1
# 池化
    pool1=tf.nn.max_pool(act1,[1,2,2,1],[1,2,2,1],"SAME")

## 第二层卷积
# 初始化权重
    k2=tf.Variable(tf.random_normal([3,3,200,100]))
    b2=tf.Variable(tf.random_normal([100,]))
# 卷积
    conv2=tf.nn.conv2d(pool1,k2,[1,1,1,1],"SAME")
# 激活    
    act2=tf.nn.relu(conv2)+b2
# 池化
    pool2=tf.nn.max_pool(act2,[1,2,2,1],[1,2,2,1],"SAME")

## 第三层卷积
# 初始化权重
    k3=tf.Variable(tf.random_normal([3,3,100,60]))
    b3=tf.Variable(tf.random_normal([60,]))
# 卷积
    conv3=tf.nn.conv2d(pool2,k3,[1,1,1,1],"SAME")
# 激活    
    act3=tf.nn.relu(conv3)+b3
# 池化
    pool3=tf.nn.max_pool(act3,[1,2,2,1],[1,2,2,1],"SAME")
    
## 第四层卷积
# 初始化权重
    k4=tf.Variable(tf.random_normal([3,3,60,30]))
    b4=tf.Variable(tf.random_normal([30,]))
# 卷积
    conv4=tf.nn.conv2d(pool3,k4,[1,1,1,1],"SAME")
# 激活    
    act4=tf.nn.relu(conv4)+b4
# 池化
    pool4=tf.nn.max_pool(act4,[1,2,2,1],[1,2,2,1],"SAME")

## 第五层全连接
    # 把输入转化成一阶
    print(pool4)
    num=pool4.shape[1].value * pool4.shape[2].value * pool4.shape[3].value
    flat=tf.reshape(pool4,[-1,num])
    w5=tf.Variable(tf.random_normal([num,10]))
    #b5=tf.Variable(tf.random_normal([10,]))
# 线性操作
    l5=tf.matmul(flat,w5)
# 激活
    #act5=tf.nn.softmax(l5)

# 损失函数
    loss=tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(labels=true,logits=l5))
# 优化器
    opti=tf.train.GradientDescentOptimizer(0.001).minimize(loss)
# 计算准确率
    predict=tf.nn.softmax(l5)
    temp=tf.equal(tf.arg_max(predict,1),tf.arg_max(true,1))
    temp=tf.cast(temp,tf.float32)
    accurancy=tf.reduce_mean(temp)
# 创建会话,开始训练
loss_map=[]
sess=tf.Session(config=tf.ConfigProto(allow_soft_placement=True, log_device_placement=True))
sess.run(tf.global_variables_initializer())
#定义每次传入的数据数量
batch_size=1000
num=int(mnist.train.images.shape[0]/batch_size)
for i in range(10):
    for i in range(num):
            # 默认从训练集中随机挑选batch_size个数据
        input,output=mnist.train.next_batch(batch_size)
        input=input.reshape([-1,28,28,1])
        sess.run(opti,{x:input,true:output})
        loss_map.append(sess.run(loss,{x:input,true:output}))
t2_1 = datetime.datetime.now()
print(t2_1-t1_1)
test=mnist.test.images.reshape([-1,28,28,1])
print("准确率为:",sess.run(accurancy,{x:test,true:mnist.test.labels}))
plt.plot([i for i in range(len(loss_map))],loss_map)
plt.show()
sess.close()

运行结果:

Extracting ./data/mnist/train-images-idx3-ubyte.gz
Extracting ./data/mnist/train-labels-idx1-ubyte.gz
Extracting ./data/mnist/t10k-images-idx3-ubyte.gz
Extracting ./data/mnist/t10k-labels-idx1-ubyte.gz
Tensor("MaxPool_15:0", shape=(?, 2, 2, 30), dtype=float32, device=/device:GPU:0)
0:02:28.746378
ResourceExhaustedError: OOM when allocating tensor with shape[10000,200,28,28] and type float on /job:localhost/replica:0/task:0/device:GPU:0 by allocator GPU_0_bfc
     [[{{node Conv2D_16}} = Conv2D[T=DT_FLOAT, data_format="NCHW", dilations=[1, 1, 1, 1], padding="SAME", strides=[1, 1, 1, 1], use_cudnn_on_gpu=true, _device="/job:localhost/replica:0/task:0/device:GPU:0"](Conv2D_16-0-TransposeNHWCToNCHW-LayoutOptimizer, Variable_66/read)]]

把55000个数据的数据集训练十次需要花费两分半
同时还遇到一个高大上的“ResourceExhaustedError”,据说是GPU内存用尽了
所以准确率和损失函数没法看到了

三、小结

1.注意形状变换:卷积神经网络接受四维张量,全连接神经网络/卷积网络全连接层 接受一维向量
2.天下没有免费的午餐:不同的算法使用与不同的网络结构

参考资料:
ResourceExhaustedError: OOM when allocating tensor with shape[10,256,400,528]

 
4 months ago

本来谷歌云有免费GPU可供我们个人开发者使用,可惜目前大陆“银联”卡无法注册
还好找到google旗下另一款产品colab提供免费GPU,直接使用谷歌账号登陆,先尝试使用一下
这里使用在线版jupyter notebook,系统默认按照各种深度学习库和框架,包括tensorflow,首先新建一个python3文件

1.判断当前是否有GPU设备

tensorflow提供了一行代码检测GPU设备 tf.test.gpu_device_name()
正常情况下会报错,colab需要手动选择设备 修改-笔记本设置


这里会发现colab支持GPU和TPU,这次选用GPU设备
然后再次运行代码 tf.test.gpu_device_name()
此时,显示有一个GPU设备可以 '/device:GPU:0'

2.简单使用GPU

import tensorflow as tf
# 定义Sesson配置对象
config = tf.ConfigProto()
# 启用最少的GPU显存来运行程序
config.gpu_options.allow_growth = True
config.log_device_placement=True #是否打印设备分配日志
config.allow_soft_placement=True #如果你指定的设备不存在,允许TF自动分配设备

with tf.device('/gpu:0'):
  random_image_gpu = tf.random_normal((100, 100, 100, 3))
  net_gpu = tf.layers.conv2d(random_image_gpu, 32, 7)
  net_gpu = tf.reduce_sum(net_gpu)

sess = tf.Session(config=config)
sess.run(tf.global_variables_initializer())
sess.run(net_gpu)

3.GPU实力展示

通过一段代码来测试GPU的威力

import tensorflow as tf
import timeit

# See https://www.tensorflow.org/tutorials/using_gpu#allowing_gpu_memory_growth
config = tf.ConfigProto()
config.gpu_options.allow_growth = True

with tf.device('/cpu:0'):
  random_image_cpu = tf.random_normal((100, 100, 100, 3))
  net_cpu = tf.layers.conv2d(random_image_cpu, 32, 7)
  net_cpu = tf.reduce_sum(net_cpu)

with tf.device('/gpu:0'):
  random_image_gpu = tf.random_normal((100, 100, 100, 3))
  net_gpu = tf.layers.conv2d(random_image_gpu, 32, 7)
  net_gpu = tf.reduce_sum(net_gpu)

sess = tf.Session(config=config)

# Test execution once to detect errors early.
try:
  sess.run(tf.global_variables_initializer())
except tf.errors.InvalidArgumentError:
  print(
      '\n\nThis error most likely means that this notebook is not '
      'configured to use a GPU.  Change this in Notebook Settings via the '
      'command palette (cmd/ctrl-shift-P) or the Edit menu.\n\n')
  raise

def cpu():
  sess.run(net_cpu)
  
def gpu():
  sess.run(net_gpu)
  
# Runs the op several times.
print('Time (s) to convolve 32x7x7x3 filter over random 100x100x100x3 images '
      '(batch x height x width x channel). Sum of ten runs.')
print('CPU (s):')
cpu_time = timeit.timeit('cpu()', number=10, setup="from __main__ import cpu")
print(cpu_time)
print('GPU (s):')
gpu_time = timeit.timeit('gpu()', number=10, setup="from __main__ import gpu")
print(gpu_time)
print('GPU speedup over CPU: {}x'.format(int(cpu_time/gpu_time)))

sess.close()

代码看起来稍嫌啰嗦,但处处透露出大气风范,运行结果如下

Time (s) to convolve 32x7x7x3 filter over random 100x100x100x3 images (batch x height x width x channel). Sum of ten runs.
CPU (s):
9.552004405999469
GPU (s):
2.0592819040002723
GPU speedup over CPU: 4x

一块GPU就可以节省我们3/4的生命,开心啊

参考资料:
TensorFlow with GPU
官方文档:使用GPU
google colab 免费使用GPU

 
4 months ago

参考资料:
Deep Learning Frameworks 2019