【TensorFlow-windows】学习笔记三——实战准备

前言

因为学习TensorFlow的内容较多,如果只看API会很无聊,可以结合实例去学习。但是在构建基本的模型之前,需要学一些准备知识:数据读取、预处理、优化器、损失函数

国际惯例,参考网址:

TensorFlow中文社区

TensorFlow官方文档

如何选择优化器 optimizer

TensorFlow-Examples

TensorFlow中的Neural Network

TensorFlow中的Train

深入浅出Tensorflow(三):训练神经网络模型的常用方法

理解tf.train.slice_input_producer()和tf.train.batch()

此外,为了模拟现实数据集,这里将mnist手写数字数据集转换成图片格式存储,放一下网盘链接:链接:https://pan.baidu.com/s/1ugEy85182vjcXQ8VoMJAbg 密码:1o83

扫描二维码关注公众号,回复: 2541060 查看本文章

处理数据集

从文本文件读取图像数据

按照caffe的读取习惯,把图片的路径和标签全部用txt文本文件存储起来,即路径\图片名 标签的格式:

./mnist/train/5/5_1.png 5
./mnist/train/0/0_2.png 0
./mnist/train/4/4_3.png 4
./mnist/train/1/1_4.png 1

然后参考TensorFlow-Examples中的build_an_image_dataset 方法按照文本文件中存储的路径和标签去按行读取

imagepaths, labels = list(), list()
data = open(dataset_path, 'r').read().splitlines()
for d in data:
    imagepaths.append(d.split(' ')[0])
    labels.append(int(d.split(' ')[1]))

在TF中打乱数据以及分批

因为我们要用tensorflow处理数据,所以必须将这些文本转换成tensorflow能处理的形式

 # 转换为张量
imagepaths = tf.convert_to_tensor(imagepaths, dtype=tf.string)
labels = tf.convert_to_tensor(labels, dtype=tf.int32)

打乱数据

# 建立TF队列,打乱数据
image, label = tf.train.slice_input_producer([imagepaths, labels],
                                                 shuffle=True)

这里看一下这个slice_input_producer函数:

tf.train.slice_input_producer(
    tensor_list,
    num_epochs=None,
    shuffle=True,
    seed=None,
    capacity=32,
    shared_name=None,
    name=None
)
  • 作用:为tensor_list中的每个Tensor建立切片
  • 部分参数:
    tensor_listTensor形式的列表,第一个维度必须相同
    num_epochs:每个切片创建多少次
    shuffle:打乱顺序

所以在打乱顺序的时候将此函数的shuffle参数设置成True即可

图片读取和处理

打乱数据路径以后,就可以读取了,tensorflow也提供了图像处理相关函数,这里列部分有用的:

  • 调整图像亮度:

    tf.image.adjust_brightness(
      image,
      delta
    )
  • 调整图像对比度:

    tf.image.adjust_contrast(
      images,
      contrast_factor
    )
  • 伽马校正:

    tf.image.adjust_gamma(
      image,
      gamma=1,
      gain=1
    )
  • 从中间向边缘裁剪图像:

    tf.image.central_crop(
      image,
      central_fraction
    )
  • 裁剪图片,分别是左上角纵坐标、横坐标,裁剪的高度、宽度:

    tf.image.crop_to_bounding_box(
      image,
      offset_height,
      offset_width,
      target_height,
      target_width
    )
  • 图像解码:将对应图像解码为uint8张量(还有对应编码的encode函数):

    1. tf.image.decode_bmp
    2. tf.image.decode_gif
    3. tf.image.decode_image
    4. tf.image.decode_jpeg
    5. tf.image.decode_png
  • 图像翻转:

    1. tf.image.flip_left_right左右翻转
    2. tf.image.flip_up_down上下翻转
    3. tf.image.rot90翻转90度,可自定义次数
    4. tf.image.transpose_image图像转置
  • 图像转换:

    1. tf.image.grayscale_to_rgb:灰度图转换为RGB
    2. tf.image.hsv_to_rgb:HSV图转换为RGB
    3. tf.image.rgb_to_grayscale:RGB转灰度图
    4. tf.image.rgb_to_hsv:RGB转HSV
    5. tf.image.rgb_to_yiq:RGB转YIQ
    6. tf.image.rgb_to_yuv:RGB转YUV
    7. tf.image.yiq_to_rgb:YIQ转RGB
    8. tf.image.yuv_to_rgb:YUV转RGB
  • 图像填充:

    tf.image.pad_to_bounding_box(
      image,
      offset_height,
      offset_width,
      target_height,
      target_width
    )

    在图像上面填充offset_height高度的0,在图像左边填充offset_width宽度的0,再按照最终目标宽度和高度在下边和右边填充图像。

  • 图像归一化:tf.image.per_image_standardization(image)

  • 图像大小调整

    tf.image.resize_images(
      images,
      size,
      method=ResizeMethod.BILINEAR,
      align_corners=False
    )

    其中插值方法有:

    AREA
    BICUBIC
    BILINEAR
    NEAREST_NEIGHBOR

    所以在图像预处理过程中,先按照路径读取图像以及解码,然后调整图像大小,归一化:

    
    # 读取数据
    
    image = tf.read_file(image)
    image = tf.image.decode_jpeg(image, channels=CHANNELS)
    
    # 将图像resize成规定大小
    
    image = tf.image.resize_images(image, [IMG_HEIGHT, IMG_WIDTH])
    
    # 手动归一化
    
    image = image * 1.0/127.5 - 1.0

创建批量数据

就一个函数:

tf.train.batch(
    tensors,
    batch_size,
    num_threads=1,
    capacity=32,
    enqueue_many=False,
    shapes=None,
    dynamic_pad=False,
    allow_smaller_final_batch=False,
    shared_name=None,
    name=None
)
  • 作用:利用队列的方法存储批数据
  • 部分参数:
    tensors:张量列表或者字典
    batch_size:批大小
    num_thread:并行入队,采用线程数
    capacity:整数,队列里面最多有多少个元素

在制作数据集中,直接这样:

 # 创建batch
X, Y = tf.train.batch([image, label], batch_size=batch_size,
                     capacity=batch_size * 8,
                     num_threads=4)

具体可以参考这篇博客的描述:TensorFlow 组合训练数据(batching)TensorFlow 笔记(九):数据读取

TF中神经网络相关函数

这里需要注意的是tf.layerstf.nn中均有相关层的实现,它们之间的区别我也不太清楚,但是从网上的观点来看,大部分人认为前者是以后者作为后端的,具体分析可看:

tf.nn.conv2d vs tf.layers.conv2d

tensorflow学习:tf.nn.conv2d 和 tf.layers.conv2d

Neural Network

层相关

目前构建神经网络还是使用前者的吧:

average_pooling1d(...): 一维输入平均池化
average_pooling2d(...): 二维输入平均池化(比如图像)
average_pooling3d(...): 三维输入平均池化(比如立体)
batch_normalization(...): 批归一化
conv1d(...): 一维卷积
conv2d(...): 二维卷积
conv2d_transpose(...): 2D反卷积
conv3d(...): 三维卷积
conv3d_transpose(...): 三维转置卷积
dense(...): 全连接
dropout(...): 随机丢失神经元
flatten(...): 保存第一个维度去展平数据
max_pooling1d(...): 一维输入最大池化
max_pooling2d(...): 二维输入最大池化
max_pooling3d(...): 三维输入最大池化
separable_conv1d(...): 按照深度独立方法一维卷积
separable_conv2d(...): 按照深度独立方法二维卷积

激活函数和损失相关

tf.nn也不是完全没用,它存储了激活函数以及部分损失函数:

sigmoid(...): sigmoid激活函数
tanh(...): tanh激活函数
softplus(...): 激活函数 log(exp(features) + 1).
relu(...): ReLU激活函数
leaky_relu(...): Leaky ReLU 激活函数max(features,leak*features)
elu(...): ELU激活函数:features if features>0 else alpha*(e^features-1)
selu(...): scaled exponential linear: scale * alpha * (exp(features) - 1)
crelu(...): 关联ReLU激活函数
relu6(...): Rectified Linear 6: min(max(features, 0), 6).
softmax(...): softmax 激活函数(一般用于最后一层)
softsign(...): 激活函数 features / (abs(features) + 1).
dropout(...):神经元抑制

ctc_loss(...): CTC (Connectionist Temporal Classification)损失
nce_loss(...):计算noise-contrastive estimation训练损失
l2_loss(...): L2 损失.
log_poisson_loss(...): log Poisson损失.
weighted_cross_entropy_with_logits(...): 加权交叉熵损失(默认已将features用softmax激活)
sparse_softmax_cross_entropy_with_logits(...): 计算logits 和 labels的稀疏交叉熵损失
softmax_cross_entropy_with_logits(...): 计算logits 和 labels的交叉熵损失(默认已将features用softmax激活)
softmax_cross_entropy_with_logits_v2(...):  计算logits 和 labels的交叉熵损失(默认已将features用softmax激活)
sigmoid_cross_entropy_with_logits(...): 经sigmoid 函数激活之后的交叉熵(默认已将features用softmax激活)

moments(...): 计算输入数据的均值和方差.
normalize_moments(...): 基于sufficient statistics计算输入数据的均值和方差

top_k(...): 返回最后一个维度的k个最大值和索引
in_top_k(...):查询目标值是否在k个预测值中
xw_plus_b(...): 计算 matmul(x, weights) + biases.
relu_layer(...): 计算 Relu(x * weight + biases).

这里几个地方需要注意:

  1. l2_loss损失函数:没有开方,并且只取一半 o u t p u t = 1 2 x 2
  2. xxx__with_logits:类似于这样的都是已经将输出用softmax激活,然后计算误差,相当于做了两步工作:softmax激活输出、计算其log值与原始标签的乘积的和,具体介绍戳这里

正则项

两种正则化方法,三种函数:

  • 一范式正则化:

    tf.contrib.layers.l1_regularizer(
      scale,
      scope=None
    )
  • 二范式正则化:

    tf.contrib.layers.l2_regularizer(
      scale,
      scope=None
    )
  • 两种范式一起用

    tf.contrib.layers.l1_l2_regularizer(
      scale_l1=1.0,
      scale_l2=1.0,
      scope=None
    )

最后要将正则化应用到权重中:

tf.contrib.layers.apply_regularization(
    regularizer,
    weights_list=None
)

TF中的训练方法

包含优化器、梯度计算、梯度裁剪、学习率及其衰减,参考这里

优化器

Optimizer
GradientDescentOptimizer
AdadeltaOptimizer
AdagradOptimizer
AdagradDAOptimizer
MomentumOptimizer
AdamOptimizer
FtrlOptimizer
ProximalGradientDescentOptimizer
ProximalAdagradOptimizer
RMSPropOptimizer

各种梯度优化器,注意他们是属于,对应有很多函数可调用,比如minimize

梯度计算

gradients
AggregationMethod
stop_gradient
hessians

梯度裁剪

clip_by_value
clip_by_norm
clip_by_average_norm
clip_by_global_norm
global_norm

学习率及其衰减

exponential_decay
inverse_time_decay
natural_exp_decay
piecewise_constant
polynomial_decay
cosine_decay
linear_cosine_decay
noisy_linear_cosine_decay

一般不用搞那么复杂,随便用两个就行:

  • 指数形式的衰减方法

    tf.train.exponential_decay(
      learning_rate,#初始学习率
      global_step,#非负,衰减指数
      decay_steps,#正数,衰减周期
      decay_rate,#衰减率
      staircase=False,#如果是True,就是离散形式的衰减
      name=None
    )
    '''
    decayed_learning_rate = learning_rate *
                          decay_rate ^ (global_step / decay_steps)
    staircase是True的时候(global_step / decay_steps)转换为整数
    '''
  • 多项式衰减:

    tf.train.polynomial_decay(
      learning_rate,
      global_step,
      decay_steps,
      end_learning_rate=0.0001,
      power=1.0,
      cycle=False,
      name=None
    )

    其中的cycle参数是决定lr是否在下降后重新上升的过程。cycle参数的初衷是为了防止网络后期lr十分小导致一直在某个局部最小值中振荡,突然调大lr可以跳出注定不会继续增长的区域探索其他区域。

    cycle时的计算方法:

    global_step = min(global_step, decay_steps)
    decayed_learning_rate = (learning_rate - end_learning_rate) *
                          (1 - global_step / decay_steps) ^ (power) +
                          end_learning_rate

    有cycle时的计算方法:

    decay_steps = decay_steps * ceil(global_step / decay_steps)
    decayed_learning_rate = (learning_rate - end_learning_rate) *
                          (1 - global_step / decay_steps) ^ (power) +
                          end_learning_rate

一般使用流程

一般是先定义学习率,然后使用优化器最小化损失

比如使用指数衰减:

...
global_step = tf.Variable(0, trainable=False)
starter_learning_rate = 0.1
learning_rate = tf.train.exponential_decay(starter_learning_rate, global_step,
                                           100000, 0.96, staircase=True)
# Passing global_step to minimize() will increment it at each step.
learning_step = (
    tf.train.GradientDescentOptimizer(learning_rate)
    .minimize(...my loss..., global_step=global_step)
)

比如使用多项式衰减

...
global_step = tf.Variable(0, trainable=False)
starter_learning_rate = 0.1
end_learning_rate = 0.01
decay_steps = 10000
learning_rate = tf.train.polynomial_decay(starter_learning_rate, global_step,
                                          decay_steps, end_learning_rate,
                                          power=0.5)
# Passing global_step to minimize() will increment it at each step.
learning_step = (
    tf.train.GradientDescentOptimizer(learning_rate)
    .minimize(...my loss..., global_step=global_step)
)

这个global_step随着训练自增,具体可以看这里

用这段代码可以看出来:

import tensorflow as tf;  
import numpy as np;  
import matplotlib.pyplot as plt;  

x = tf.placeholder(tf.float32, shape=[None, 1], name='x')
y = tf.placeholder(tf.float32, shape=[None, 1], name='y')
w = tf.Variable(tf.constant(0.0))

global_steps = tf.Variable(0, trainable=False)
learning_rate = tf.train.exponential_decay(0.1, global_steps, 10, 2, staircase=False)
loss = tf.pow(w*x-y, 2)

train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_steps)

with tf.Session() as sess:
    sess.run(tf.initialize_all_variables())
    for i in range(10):
        sess.run(train_step, feed_dict={x:np.linspace(1,2,10).reshape([10,1]),
            y:np.linspace(1,2,10).reshape([10,1])})
        print (sess.run(global_steps))

注意,经常无需人工计算梯度然后apply到相关参数列表上,因为minimize已经包含这两步骤了。

简单例子

按照这个例子很容易利用CNN实现一个手写数字识别网络
先引入相关包以及定义数据相关信息

import tensorflow as tf
import os

DATASET_PATH = './mnist/train_labels.txt' # the dataset file or root folder path.
N_CLASSES = 10 # 类别数
IMG_HEIGHT = 28 # 高
IMG_WIDTH = 28 # 宽
CHANNELS = 1 # 通道数

然后按照标签中定义的图片路径和标签,制作数据集:

def read_images(dataset_path, batch_size):
    imagepaths, labels = list(), list()
    data = open(dataset_path, 'r').read().splitlines()
    for d in data:
        imagepaths.append(d.split(' ')[0])
        labels.append(int(d.split(' ')[1]))


    # 转换为张量
    imagepaths = tf.convert_to_tensor(imagepaths, dtype=tf.string)
    labels = tf.convert_to_tensor(labels, dtype=tf.int32)
    # 建立TF队列,打乱数据
    image, label = tf.train.slice_input_producer([imagepaths, labels],
                                                 shuffle=True)

    # 读取数据
    image = tf.read_file(image)
    image = tf.image.decode_jpeg(image, channels=CHANNELS)

    # 将图像resize成规定大小
    image = tf.image.resize_images(image, [IMG_HEIGHT, IMG_WIDTH])

    # 手动归一化
    image = image * 1.0/127.5 - 1.0

    # 创建batch
    X, Y = tf.train.batch([image, label], batch_size=batch_size,
                          capacity=batch_size * 8,
                          num_threads=4)

    return X, Y

设置网络参数

#网络参数
learning_rate = 0.001#学习率
num_steps = 100#迭代次数
batch_size = 128#每批大小
display_step = 100#显示调试信息
dropout = 0.75 # dropout保留比率
X, Y = read_images(DATASET_PATH, batch_size)#读取数据集

定义网络结构:

# 创建卷积模型
def conv_net(x, n_classes, dropout, reuse, is_training):
    # Define a scope for reusing the variables
    with tf.variable_scope('ConvNet', reuse=reuse):

        # 第一层卷积
        conv1 = tf.layers.conv2d(x, 32, 5, activation=tf.nn.relu)
        # 最大池化
        conv1 = tf.layers.max_pooling2d(conv1, 2, 2)

        # 第二层卷积
        conv2 = tf.layers.conv2d(conv1, 64, 3, activation=tf.nn.relu)
        # 最大池化
        conv2 = tf.layers.max_pooling2d(conv2, 2, 2)

        # 拉成一维向量
        fc1 = tf.layers.flatten(conv2)

        # 全连接层
        fc1 = tf.layers.dense(fc1, 1024)
        # 应用dropout
        fc1 = tf.layers.dropout(fc1, rate=dropout, training=is_training)

        # 输出
        out = tf.layers.dense(fc1, n_classes)
        # softmax输出
        out = tf.nn.softmax(out) if not is_training else out

    return out

定义训练网络和评估网络,并进行训练

#训练网络
logits_train = conv_net(X, N_CLASSES, dropout, reuse=False, is_training=True)
# 测试网络
logits_test = conv_net(X, N_CLASSES, dropout, reuse=True, is_training=False)

# 定义损失和优化器
loss_op = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(
    logits=logits_train, labels=Y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
train_op = optimizer.minimize(loss_op)

# 评估模型
correct_pred = tf.equal(tf.argmax(logits_test, 1), tf.cast(Y, tf.int64))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))

# 初始化变量
init = tf.global_variables_initializer()

# 保存模型参数
saver = tf.train.Saver()

# 开始训练
with tf.Session() as sess:
    coord=tf.train.Coordinator()
    # 初始化参数
    sess.run(init)

    # 数据集队列
    tf.train.start_queue_runners(sess=sess,coord=coord)

    # 循环训练
    for step in range(1, num_steps+1):

        if step % display_step == 0:
            # Run optimization and calculate batch loss and accuracy
            _, loss, acc = sess.run([train_op, loss_op, accuracy])
            print("Step " + str(step) + ", Minibatch Loss= " + \
                  "{:.4f}".format(loss) + ", Training Accuracy= " + \
                  "{:.3f}".format(acc))
        else:            
            sess.run(train_op)

    coord.request_stop()#请求线程结束
    coord.join()#等待线程结束
    print("Optimization Finished!")

    # 保存模型
    saver.save(sess, './cnn_mnist_model/my_tf_model')

结果

Step 100, Minibatch Loss= 0.1098, Training Accuracy= 0.969
Step 200, Minibatch Loss= 0.1070, Training Accuracy= 0.969
Step 300, Minibatch Loss= 0.0393, Training Accuracy= 1.000
Step 400, Minibatch Loss= 0.0688, Training Accuracy= 0.984
Step 500, Minibatch Loss= 0.0559, Training Accuracy= 0.992
Step 600, Minibatch Loss= 0.0433, Training Accuracy= 0.984
Step 700, Minibatch Loss= 0.0341, Training Accuracy= 0.992
Step 800, Minibatch Loss= 0.0309, Training Accuracy= 0.984
Step 900, Minibatch Loss= 0.0825, Training Accuracy= 0.969
Step 1000, Minibatch Loss= 0.0211, Training Accuracy= 1.000
Optimization Finished!

个人感觉相对于theano使用tensorflow的好处在于,无需自己写梯度优化了,创建模型貌似不用自己去挨个权重定义和初始化,这里就没针对每个卷积核定义变量和单独初始化,除此之外,没感觉有啥其它便利性了。
还有一个重要问题是,这个模型保存了以后,好像无法拿过来单独测试一张图片,因为这里没有函数提供额外输入,即使想调用logits_test来测试单张图片,也不好弄,因为X无法在重载模型后指定单张图片,所以还得改代码,后续再研究研究模型保存与载入。

后记

这一部分主要了解一下在TensorFlow中构建神经网络可以使用的部分函数。
我嘞个乖乖,感觉好难啊,跟Theano差不多,好底层,想脱坑转TensorLayer或者TFLearn这两个基于tensorflow的二次封装库,知乎上有对应讨论如何比较Keras, TensorLayer, TFLearn ?,貌似大部分人都推荐TL,先继续折腾TensorFlow的保存和加载模型,如果实在不行,后续去了解一下对工程实现的支持程度以及模型的保存和载入难度,再选择框架。

猜你喜欢

转载自blog.csdn.net/zb1165048017/article/details/81356359
今日推荐