用TensorFlow和TensorBoard从零开始构建ConvNet(CNN)

简介: Tensorflow作为当下最流行的深度学习框架,实现ConvNet(CNN)自然是轻而易举,但是本文创造性的使用的TensorBoard来图形化展示CNN实现过程,极大的提高了研究者的对自己模型的管理能力。

更多深度文章,请关注:https://yq.aliyun.com/cloud


首先了解TensorBoard是什么?解开你心中的疑惑!

在本教程中,我将介绍如何使用TensorFlow,从头开始构建卷积神经网络,并使用TensorBoard可视化我们的图形及神经网络性能。如果您不了解完全神经网络的一些基础知识,我强烈建议您首先看另一个教程关于TensorFlow。在这篇文章中,我也把卷积神经网络的每个步骤讲的都很仔细,所以你可以在文章中完全了解每个步骤发生了什么。未来您可以看到每一层的卷积,并使用它们进行自己的网络模型构建,所以我从头开始构建该模型。我只会强调主要的代码,不过你可以GitHub下载源码。

1.选择数据集

一开始,我先要选择图像数据集。我决定用牛津大学的宠物数据集。我选择了这个数据集的原因很简单:标签非常简单,训练数据也不错,而且还有一些边框。另一个我认为对于创建第一个模型是非常好的数据集是Kaggle上发现的辛普森数据集

2.选择一个模型

第二步,必须决定我们的卷积神经网络的模型。一些非常受欢迎的型号例如:GoogLeNetVGG16,它们都具有多个卷积,可以用于检测1000种数据集imagenet中的图像。我决定一个更简单的卷积网络:

8bc7067b2414679aab060ae228ae5c461bae5a5f

我们先简要的分解这个模型,它以一个224x224x3的图像开始,它根据前三个通道卷积到32个特征图(Feature Map)。我们将这组32个特征图集合到另外32个特征中。然后将其汇总到112x112x32图像中,我们将卷入64个特征图,然后再次进行二次,最后汇总为56x56x64。然后将这个最终合并的层的每个单元完全连接到一个512个的神经元中,然后基于类的数​​量最后放入softmax层。整个过程就是如此,如果你对上述有一些疑问,可以进一步与我交流。

3.处理和构建数据集:

首先,我们要加载我们的依赖项,其中包括我所调用的功能函数imFunctions来处理图像数据。

import imFunctions as imf
import tensorflow as tf
import scipy.ndimage
from scipy.misc import imsave
import matplotlib.pyplot as plt
import numpy as np
AI 代码解读

我们可以使用imFunctions提供的下载的方式提取图像。

imf.downloadImages('annotations.tar.gz', 19173078)
imf.downloadImages('images.tar.gz', 791918971)
imf.maybeExtract('annotations.tar.gz')
imf.maybeExtract('images.tar.gz')
AI 代码解读

然后我们可以将图像分成不同的文件夹,其中包括训练文件夹和测试文件夹。sortImages函数中的数字表示的是您想从训练数据中分离出测试数据的百分比。

imf.sortImages(0.15)
AI 代码解读

然后,我们可以使用相应的一个热矢量(one-hot)将我们的数据集构建成一个numpy数组,以表示我们的类。该函数将会表现您要让神经网络做那些事,由于我有限的GPU RAM3GB),我选择了一个非常小的数据集,仅仅试图区分两种狗:来自萨摩耶ShibaInu

train_x, train_y, test_x, test_y, classes, classLabels = imf.buildDataset()
AI 代码解读

4.如何卷积实现功能

现在我们有一个数据集可以使用,其次就是了解卷积如何工作。在跳入彩色卷积滤波器之前,让我们来看一下灰度图。让我们制作一个应用四个不同特征图的7x7滤镜。TensorFlowconv2d功能相当简单,它包含四个变量:输入,过滤器,步幅和填充。在TensorFlow官方网站上,他们描述的conv2d功能如下:

1.计算给定4-D输入和滤波张量的2-D卷积。

2.给定一个形状为[batchin_heightin_widthin_channels]的输入张量以及形状为[filter_heightfilter_widthin_channelsout_channels]的过滤器/内核张量。

由于我们正在使用灰度图,所以in_channels1,因为我们使用了四个过滤器,所以我们的out_channels将是4。我们将以下四个过滤器/内核应用到我们的一个图像中:

e735a76c8ddcae68bde2ece4b626a642a9677c44

让我们看看这个过滤器如何处理我们的灰度图像输入:

gray = np.mean(image,-1)
X = tf.placeholder(tf.float32, shape=(None, 224, 224, 1))
conv = tf.nn.conv2d(X, filters, [1,1,1,1], padding="SAME")
test = tf.Session()
test.run(tf.global_variables_initializer())
filteredImage = test.run(conv, feed_dict={X: gray.reshape(1,224,224,1)})
tf.reset_default_graph()
AI 代码解读

这将返回一个4d张量(1,224,224,4),我们可以使用它来可视化四个过滤器:

6536817c978115dac241a34fbc5392e799024bb3

我们可以看到过滤器的内核卷积非常强大。为了打破它,我们的7x7内核一次跨越图像像素的49个步长,然后将每个像素的值乘以内核值,然后将所有49个值加在一起,生成一个像素。

现在,本质上,大多数卷积神经网络只包括卷积和池化(pooling)。最常见的是3x3内核过滤器用于卷积。特别是步长为2×2,内核大小为2×2只是基于内核中最大像素值来减少图像大小的一种有效的方法。这是一个2x2内核的基本示例,在两个维度上都具有步长2

ab9cb5aeb835ff6b7d0c4134677021658cb219c9

现在,对于两个conv2d和最大池化,有两个选项可用于填充:“VALID”:这将缩小输入和“SAME”:这将通过在输入边缘添加来保持输入大小。这是一个具有3x3内核的最大池的示例,步长为1x1以比较填充选项:

19dfd8ede9b365bfcb488443a418e74ba3575d93

5.创建ConvNet

我们已经介绍了基础知识,那么让我们开始构建我们的卷积神经网络模型。我们可以从占位符开始。X将是我们的输入占位符,我们将把图像提供给XY_将成为一组图像的类。

X = tf.placeholder(tf.float32, shape=(None, 224, 224, 3))
Y_ = tf.placeholder(tf.float32, [None, classes])
keepRate1 = tf.placeholder(tf.float32)
keepRate2 = tf.placeholder(tf.float32)
AI 代码解读

范围(scope)对于在TensorBoard中的图形可视化非常有用,因为它们将所有内容分组成一个可扩展对象。我们创建了第一套内核大小为3x3的过滤器,它采用三个通道并输出32个过滤器。这意味着对于32个滤波器中的每一个,RGB通道将有3x3的内核权重。另外非常重要的一点是,我们的过滤器的权重值是使用截断的正常值进行初始化的,所以我们有多个随机过滤器,这意味着TensorFlow将适应我们的模型。

# CONVOLUTION 1 - 1
with tf.name_scope('conv1_1'):
    filter1_1 = tf.Variable(tf.truncated_normal([3, 3, 3, 32], dtype=tf.float32,stddev=1e-1), name='weights1_1')
    stride = [1,1,1,1]
    conv = tf.nn.conv2d(X, filter1_1, stride, padding='SAME')
    biases = tf.Variable(tf.constant(0.0, shape=[32], dtype=tf.float32),trainable=True, name='biases1_1')
    out = tf.nn.bias_add(conv, biases)
    conv1_1 = tf.nn.relu(out)
AI 代码解读

在我们第一次卷积conv1_1结束时,我们通过应用relu完成,通过将每个负数分配给零来作为阈值。然后我们将这32个特征集合到另外32个特征中。您可以看到第一个卷积层的输出将作为conv2d的输入。

# CONVOLUTION 1 - 2
with tf.name_scope('conv1_2'):
    filter1_2 = tf.Variable(tf.truncated_normal([3, 3, 32, 32], dtype=tf.float32,stddev=1e-1), name='weights1_2')
    conv = tf.nn.conv2d(conv1_1, filter1_2, [1,1,1,1], padding='SAME')
    biases = tf.Variable(tf.constant(0.0, shape=[32], dtype=tf.float32),trainable=True, name='biases1_2')
    out = tf.nn.bias_add(conv, biases)
    conv1_2 = tf.nn.relu(out)
AI 代码解读

然后我们将图像缩小一半。

# POOL 1
with tf.name_scope('pool1'):
    pool1_1 = tf.nn.max_pool(conv1_2,
                             ksize=[1, 2, 2, 1],
                             strides=[1, 2, 2, 1],
                             padding='SAME',
                             name='pool1_1')
    pool1_1_drop = tf.nn.dropout(pool1_1, keepRate1)
AI 代码解读

最后一部分涉及在池化层上使用dropout(稍后会详细介绍)。然后我们再进行两次卷积,共有64个特征和另一个池。请注意,第一次卷积必须将先前的32个特征通道转换为64

# CONVOLUTION 2 - 1
with tf.name_scope('conv2_1'):
    filter2_1 = tf.Variable(tf.truncated_normal([3, 3, 32, 64], dtype=tf.float32,stddev=1e-1), name='weights2_1')
    conv = tf.nn.conv2d(pool1_1_drop, filter2_1, [1, 1, 1, 1], padding='SAME')
    biases = tf.Variable(tf.constant(0.0, shape=[64], dtype=tf.float32),trainable=True, name='biases2_1')
    out = tf.nn.bias_add(conv, biases)
    conv2_1 = tf.nn.relu(out)
# CONVOLUTION 2 - 2
with tf.name_scope('conv2_2'):
    filter2_2 = tf.Variable(tf.truncated_normal([3, 3, 64, 64], dtype=tf.float32,stddev=1e-1), name='weights2_2')
    conv = tf.nn.conv2d(conv2_1, filter2_2, [1, 1, 1, 1], padding='SAME')
    biases = tf.Variable(tf.constant(0.0, shape=[64], dtype=tf.float32),trainable=True, name='biases2_2')
    out = tf.nn.bias_add(conv, biases)
    conv2_2 = tf.nn.relu(out)  
# POOL 2
with tf.name_scope('pool2'):
    pool2_1 = tf.nn.max_pool(conv2_2,
                             ksize=[1, 2, 2, 1],
                             strides=[1, 2, 2, 1],
                             padding='SAME',
                             name='pool2_1')
    pool2_1_drop = tf.nn.dropout(pool2_1, keepRate1)
AI 代码解读

接下来,我们创建一个512个神经元完全连接的神经网络层,它将为56x56x64 pool2_1层的每个像素设置一个权重连接。这将是超过1亿不同的权重值!为了计算我们的完全连接的网络,我们必须将输入转化一维,然后我们可以乘以我们的权重并加上我们的偏差(Bias)。

with tf.name_scope('fc1') as scope:
    shape = int(np.prod(pool2_1_drop.get_shape()[1:]))
    fc1w = tf.Variable(tf.truncated_normal([shape, 512], dtype=tf.float32,stddev=1e-1), name='weights3_1')
    fc1b = tf.Variable(tf.constant(1.0, shape=[512], dtype=tf.float32),trainable=True, name='biases3_1')
    pool2_flat = tf.reshape(pool2_1_drop, [-1, shape])
    out = tf.nn.bias_add(tf.matmul(pool2_flat, fc1w), fc1b)
    fc1 = tf.nn.relu(out)
    fc1_drop = tf.nn.dropout(fc1, keepRate2)
AI 代码解读

最后,我们的softmax与其相关的权重和偏差,最后是我们的输出Y。

#FULLY CONNECTED 3 & SOFTMAX OUTPUT
with tf.name_scope('softmax') as scope:
    fc2w = tf.Variable(tf.truncated_normal([512, classes], dtype=tf.float32,stddev=1e-1), name='weights3_2')
    fc2b = tf.Variable(tf.constant(1.0, shape=[classes], dtype=tf.float32),trainable=True, name='biases3_2')
    Ylogits = tf.nn.bias_add(tf.matmul(fc1_drop, fc2w), fc2b)
    Y = tf.nn.softmax(Ylogits)
AI 代码解读

6.创建损失函数和优化器:

现在,我们可以开始开发我们的模型的训练部分。首先,我们必须决定训练批量大小;我自己的情况是不能使用超过10,因为GPU内存的缘故。那么我们必须决定训练次数,算法将分批地遍历所有训练数据的次数,最后是我们的学习速率α

numEpochs = 400
batchSize = 10
alpha = 1e-5
AI 代码解读

然后,我们为我们的交叉熵,精度检查器和反向传播优化器创建了一些范围。

with tf.name_scope('cross_entropy'):
    cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=Ylogits, labels=Y_)
    loss = tf.reduce_mean(cross_entropy)
with tf.name_scope('accuracy'):
    correct_prediction = tf.equal(tf.argmax(Y, 1), tf.argmax(Y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
with tf.name_scope('train'):
    train_step = tf.train.AdamOptimizer(learning_rate=alpha).minimize(loss)
AI 代码解读

然后,我们可以创建我们的会话(session)并初始化所有的变量。

sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
AI 代码解读

7. 准备TensorBoard

现在,我们想使用TensorBoard,这样我们可以看到我们的分类器在做什么。我们将创建plot模块:一个用于我们的训练集,一个用于我们的测试集。我们可以通过使用add_graph功能可视化我们的图形网络。我们将使用汇总标量(scalar)来衡量我们的损失值和准确度,并将我们的汇总合并在一起,所以我们只需要调用write_op记录我们的标量。

writer_1 = tf.summary.FileWriter("/tmp/cnn/train")
writer_2 = tf.summary.FileWriter("/tmp/cnn/test")
writer_1.add_graph(sess.graph)
tf.summary.scalar('Loss', loss)
tf.summary.scalar('Accuracy', accuracy)
tf.summary.histogram("weights1_1", filter1_1)
write_op = tf.summary.merge_all()
AI 代码解读

8.训练模型

然后我们可以对我们的模型进行评估和训练。我们不想在每个时间步骤中总结损失和准确性,因为这将大大减慢分类器的速度。所以相反,我们每五个步骤记录一次。

steps = int(train_x.shape[0]/batchSize)
for i in range(numEpochs):
    accHist = []
    accHist2 = []
    train_x, train_y = imf.shuffle(train_x, train_y)
    for ii in range(steps):
        #Calculate our current step
        step = i * steps + ii
        acc = sess.run([accuracy], feed_dict={X: train_x[(ii*batchSize):((ii+1)*batchSize),:,:,:], Y_: train_y[(ii*batchSize):((ii+1)*batchSize)], keepRate1: 1, keepRate2: 1})
        accHist.append(acc)        
        if step % 5 == 0:
            # Get Train Summary for one batch and add summary to TensorBoard
            summary = sess.run(write_op, feed_dict={X: train_x[(ii*batchSize):((ii+1)*batchSize),:,:,:], Y_: train_y[(ii*batchSize):((ii+1)*batchSize)], keepRate1: 1, keepRate2: 1})
            writer_1.add_summary(summary, step)
            writer_1.flush()
            
            # Get Test Summary on random 10 test images and add summary to TensorBoard
            test_x, test_y = imf.shuffle(test_x, test_y)
            summary = sess.run(write_op, feed_dict={X: test_x[0:10,:,:,:], Y_: test_y[0:10], keepRate1: 1, keepRate2: 1})
            writer_2.add_summary(summary, step)
            writer_2.flush()
 sess.run(train_step, feed_dict={X: train_x[(ii*batchSize):((ii+1)*batchSize),:,:,:], Y_: train_y[(ii*batchSize):((ii+1)*batchSize)], keepRate1: 0.2, keepRate2: 0.5})
AI 代码解读

9.可视化

当训练的时候,我们来看看TensorBoard的结果,在终端上激活TensorBoard

tensorboard --logdir="/tmp/cnn/"
AI 代码解读

然后我们可以将我们的网页浏览器指向默认的TensorBoard地址http://0.0.0.0/6006。我们先看看我们的图形化模型。正如你所看到的,通过使用范围(scope),我们可视化了一个漂亮的干净版本的图形。

f98c40eb4aa253a4c2125ed1842fcf6a5740bc78

10.性能测量

我们来看看我们准确度和损失的标量历史:

27d47652fcf9fc3a102397f14597fba49e2da299

你可能说我们有一个很严峻的问题。对于我们的培训数据,分类器获得了100%的准确性和0损失,但是我们的测试数据最多只能达到80%,并且会有很大的损失。这是一个常见的现象原,因包括训练数据不足或神经元太多。

我们可以通过调整、缩放和旋转训练数据以此来获得更多的训练数据,但是我认为更容易的方法是添加dropout功能到我们的池化层和完全连接层的输出。这将使每个训练步骤在每一层中随机地完全删除或退出一部分神经元。这将使我们的分类器一次只训练一小组的神经元,而全部的神经元。这使得专门神经元从事特定任务,而不是将所有神经元泛化在一起,同时进行某项工作。减少80%的卷积层和50%的完全连接的层可能会产生一些惊人的结果。

通过drop-out神经元,我们可以在测试数据上实现90%以下的性能,几乎提高了10%!但也有一个缺点,分类器花了大约6倍的时间来训练。

4.可视化进化过滤器

为了增加一些乐趣,每50个训练步骤,我通过一个过滤器传递了一个图像,并将过滤器的权重变化gif化。这出现了一些非常酷的效果和一些非常好的洞察例如:观察如何卷积网络的工作。以下是两个过滤器conv1_2

b0dedf4cbcaa4fd6361c4f9b9e12bf6bf18aa995

您可以看到加权初始化显示了大量图像,但权重随着时间的推移而变化,他们更加专注于检测某些边缘。令我吃惊的是,我发现第一个卷积内核filter1_1几乎没有改变。为了进一步了解网络,这里是conv2_2,您可以看到它开始检测更多的抽象的泛化功能。

abda88fea07daa2c060685d61f46fa21da3f6b5e

总而言之,让我印象深刻的是,我能够使用少于400个训练图像训练几乎90%精度的模型。我相信有更多的训练数据及更多的调整超参数,我可以取得更好的结果。

最后,重要的是要记住,在制作具有少量数据的分类器时,更容易的方法是采用已经在具有多个GPU(例如GoogLeNetVGG16的巨大数据集上进行训练的模型和权重,并在最后一层用自己的类替换它们的类。那么,这就要求所有的分类器都要做的是学习最后一层的权重,并使用预先存在的训练过的权重。最后希望你能在本文中学到一些知识,have fun

本文由北邮@爱可可-爱生活老师推荐,@阿里云云栖社区组织翻译。

文章原标题《Visualizing convolutional neural networks

作者:Justin Francis

Justin Francis目前是加拿大阿尔伯塔大学的本科生。贾斯汀还在大学工程俱乐部自主机器人车辆项目arvp.org)的软件团队中帮助实施和实验了深入学习和强化学习算法

译者:袁虎 审阅:坯子

文章为简译,更为详细的内容,请查看原文


相关文章
【大作业-04】手把手教你构建垃圾分类系统-基于tensorflow2.3
本文介绍了基于TensorFlow 2.3的垃圾分类系统,通过B站视频和博客详细讲解了系统的构建过程。系统使用了包含8万张图片、245个类别的数据集,训练了LeNet和MobileNet两个卷积神经网络模型,并通过PyQt5构建了图形化界面,用户上传图片后,系统能识别垃圾的具体种类。此外,还提供了模型和数据集的下载链接,方便读者复现实验。垃圾分类对于提高资源利用率、减少环境污染具有重要意义。
302 0
【大作业-04】手把手教你构建垃圾分类系统-基于tensorflow2.3
CNN构建网络
【8月更文挑战第10天】CNN构建网络。
82 22
TensorFlow,一款由谷歌开发的开源深度学习框架,详细讲解了使用 TensorFlow 构建深度学习模型的步骤
本文介绍了 TensorFlow,一款由谷歌开发的开源深度学习框架,详细讲解了使用 TensorFlow 构建深度学习模型的步骤,包括数据准备、模型定义、损失函数与优化器选择、模型训练与评估、模型保存与部署,并展示了构建全连接神经网络的具体示例。此外,还探讨了 TensorFlow 的高级特性,如自动微分、模型可视化和分布式训练,以及其在未来的发展前景。
534 5
使用TensorFlow构建一个简单的图像分类模型
【10月更文挑战第18天】使用TensorFlow构建一个简单的图像分类模型
189 1
基于tensorflow、CNN网络识别花卉的种类(图像识别)
基于tensorflow、CNN网络识别花卉的种类(图像识别)
164 1
使用 TensorFlow 和 Keras 构建图像分类器
【10月更文挑战第2天】使用 TensorFlow 和 Keras 构建图像分类器
深度学习实践:构建并训练卷积神经网络(CNN)对CIFAR-10数据集进行分类
本文详细介绍如何使用PyTorch构建并训练卷积神经网络(CNN)对CIFAR-10数据集进行图像分类。从数据预处理、模型定义到训练过程及结果可视化,文章全面展示了深度学习项目的全流程。通过实际操作,读者可以深入了解CNN在图像分类任务中的应用,并掌握PyTorch的基本使用方法。希望本文为您的深度学习项目提供有价值的参考与启示。
深度学习入门:使用Python和TensorFlow构建你的第一个神经网络
【8月更文挑战第31天】 本文是一篇面向初学者的深度学习指南,旨在通过简洁明了的语言引导读者了解并实现他们的第一个神经网络。我们将一起探索深度学习的基本概念,并逐步构建一个能够识别手写数字的简单模型。文章将展示如何使用Python语言和TensorFlow框架来训练我们的网络,并通过直观的例子使抽象的概念具体化。无论你是编程新手还是深度学习领域的新兵,这篇文章都将成为你探索这个激动人心领域的垫脚石。
【Tensorflow】解决Tensorboard: ValueError: Duplicate plugins for name projector
解决TensorBoard版本冲突的方法,即通过卸载冲突的TensorFlow相关包然后重新安装所需的版本。
283 1

热门文章

最新文章