关于cnn:AI识万物从0搭建和部署手语识别系统-⛵

作者:韩信子@ShowMeAI 深度学习实战系列:https://www.showmeai.tech/tutorials/42 计算机视觉实战系列: https://www.showmeai.tech/tutorials/46 本文地址:https://www.showmeai.tech/article-detail/292 申明:版权所有,转载请分割平台与作者并注明出处 珍藏ShowMeAI查看更多精彩内容据北京听力协会预估数据,我国听障人群数量已过千万。而在寰球范畴内有4.66亿人患有残疾性听力损失,约占全世界人口的5%。聋哑人士很非凡,他们须要应用手语进行交换,其余与常人无异,我国存在特殊教育程度在各城市中倒退力度具备较大差别,国家通用手语推广水平浅,但不懂手语,与听力障碍者交流会十分艰难。 在本篇内容中,ShowMeAI 借助深度学习与神经网络技术,针对这个问题从 0 构建 1 个应用程序,检测手语并将其翻译给其他人进而突破手语隔膜。 搭建和部署实现后,你能够通过摄像头,轻松测试模型,如下图所示,快来一起试试吧。这个动图中的手势代表的单词,见文末哦! 手语介绍咱们先来简略理解一下手语,它由 3 个次要局部组成: 手指拼写:这是一种手动的交换形式,用双手和手指拼写单词。每个字母都用指定的手地位示意。单词级符号词汇:这是一个大型视频数据集,用于辨认单词或字母的整个手势。非手部特色:包含任何面部表情、嘴巴、舌头或身材姿态。 在本文中,咱们先解决第①个局部的问题。咱们筹备应用的解决方案是基于视觉数据的神经网络 深度学习与计算机视觉人工智能和计算机视觉的最典型的模型是卷积神经网络(CNN),它在典型的计算机视觉利用中(如图像识别、指标检测等)利用宽泛。咱们在本次利用的核心技术也将采纳 CNN。 CNN 网络有着如上图所示的网络结构,典型的构造包含卷积层、池化层、激活层、全连贯层等,对于输出图像,能够无效抽取图像内容表征,并进行分类或其余解决。卷积层等非凡构造,能够在控制参数量的前提下,保障良好的图像特征提取能力。 对于卷积神经网络的具体常识能够参考ShowMeAI下述教程: ShowMeAI的 深度学习教程 | 吴恩达专项课程 · 全套笔记解读 中的文章 卷积神经网络解读ShowMeAI的 深度学习与计算机视觉教程 中的文章 卷积神经网络详解 小试牛刀,买通流程咱们来构建一个 CNN 辨认的流程,会分成以下根底步骤: 数据读取与切分数据可视化及预处理CNN网络构建与训练① 导入相干库咱们在这里次要应用 TensorFlow 构建网络与训练,会应用 Numpy 做数据计算与解决,以及应用 Matplotlib 进行简略可视化。 对于这些工具库,ShowMeAI都制作了快捷即查即用的速查表手册,大家能够在下述地位取得: Tensorflow 速查手册Numpy 速查手册Matplotlib 速查手册咱们先把这些工具库导入。 # 导入工具库import stringimport pandas as pdimport numpy as npimport tensorflow as tfimport matplotlib.pyplot as pltfrom tensorflow import kerasfrom functools import partialfrom tensorflow.keras.preprocessing.image import ImageDataGenerator, array_to_img② 读取数据集本数据集为手语字母对应的数据集,图片 size 不大,所以也叫做 sign_mnist 数据集(类比手写数字数据集 mnist),局部示例图片如下 ...

August 9, 2022 · 5 min · jiezi

关于cnn:产品实操三分钟教你快速搭建字符识别CNN模型

深度学习中最令人兴奋的畛域之一就是计算机视觉。通过卷积神经网络CNN(Convolutional Neural Networks),咱们曾经可能创立主动驾驶零碎、面部检测零碎和主动医学图像剖析等。但如果不是业余的数据工程师与行业人员,您简直没有机会理解CNN。明天,本文将从实践与实际两种形式,率领各位来理解CNN的外部结构,以及CNN是如何实现具体性能的。 01 第一局部:卷积神经网络CNN 什么是卷积神经网络 咱们经常据说的神经网络,全名人工神经网络ANN(Artificial Neural Network),是一种模拟生物神经网络的构造和性能的数学模型或计算模型。而卷积神经网络CNN是一种前馈神经网络,在图像处理方面CNN的体现非常杰出。 为什么应用卷积神经网络? 全连贯神经网络不适宜用于解决图像,因为会有以下三方面的问题:没有利用像素之间地位信息;参数过多效率低下,训练艰难;大量的参数导致网络过拟合。 应用卷积神经网络CNN能够解决下面的问题。CNN在原来的全连贯层后面退出多个卷积层、激活函数与池化层,其中每层的神经元将只与前一层中的一小块区域连贯,而不是采取全连贯形式,防止参数爆炸的同时扩大了神经网络的层数,使深度学习成为可能。 传统神经网络(左)与卷积神经网络CNN(右) 工作原理 卷积神经网络CNN中次要由输出层、卷积层、激活函数、池化层以及全连贯层组成。CNN的工作原理相似大脑辨认图像的过程,不同的大脑区域能够辨认图像不同维度的特色。CNN中的每个卷积层都蕴含多个卷积核。这些卷积核从左到右,从上到下扫描整个图像以失去特色图。在通过多个卷积层的计算后能够失去图像在不同维度的形象示意,从而获取图像特色以辨认图像。 卷积计算示意图(图片起源:github mlonotebook) 为了让各位对CNN有一个具体的印象,接下来本文将对CNN的每一层进行解说。 卷积层 通过卷积层,咱们能够实现对图像的去噪、锐化、边缘检测、含糊等操作,从而达到提取图像不同特色的目标。 在卷积层中,蕴含多个可学习的卷积核,让上一层输入的特色图与卷积核进行卷积操作,即输出项与卷积核之间进行点积运算,而后将后果送入激活函数,就能够失去输入特色图。 以下图为例。图中输出图像大小为32323,3是它的深度(RGB),卷积层的卷积核大小为553。通过一个卷积核与输出图像的卷积就能够失去一个28281的特色图。 卷积计算示例 第一层卷积层只能提取一些低级的特色,如边缘、线条和角等层级,而更多层的网路就能从低级特色中迭代提取更简单的特色。 下图是输出图像别离通过1个/2个/3个卷积核卷积后输入的特色图,能够看到通过迭代后咱们取得了更深层次的特色图。 卷积特色图 通过卷积核,CNN能够提取图片的一部分,相当于传统神经网络的一个特色。多个卷积核对应多个特色,产生多个特色的同时应用卷积的形式防止了参数爆炸。 激活函数 激活函数又称非线性映射。若干线性操作层(卷积层)的重叠只能起到线性映射的作用,无奈造成简单的函数。激活函数能够引入非线性因素,减少整个网络的表达能力。因而每次卷积实现后,都须要应用激活函数对后果进行解决。 常见的激活函数有Sigmoud、tanh、Maxout以及咱们最罕用的Relu等。 常见的激活函数 以ReLu为例,该函数能够将图片中的负像素值替换为零,以此对特征提取的后果进行修改。 ReLu解决特色图像 池化层 池化层又称下采样层,次要的作用是对激活函数解决后的后果进行降维,压缩数据和参数的量来保留最显著的特色,避免过拟合。 以罕用的最大池化为例,在最大池化过程中,输出图像大小为44,在每22的区域中计算最大值。最初输入的池化特色大小为2*2,这个过程中分辨率变为原来的一半。 最大池化示例 全连贯层 全连贯层能够把所有部分特色通过权值矩阵从新整合为一个残缺的特色视图,其输入值能够采纳softmax分类器进行分类。 全连贯层示意图 Softmax分类器罕用于多分类工作,它能够将多个神经元的输入映射到(0,1)的区间中,以概率的模式实现多分类。 Dropout函数 Dropout函数是指在深度学习网络的训练过程中,让某个神经元的激活值以肯定的概率p进行工作。这样能够使模型泛化性更强,因为它不会太依赖某些部分的特色。 Dropout函数示例 02 第二局部:试验 试验——手写数字辨认 读完了下面的内容,置信您对卷积神经网络CNN有了初步的理解。接下来,本文将通过试验的形式直观地展示CNN的搭建,训练以及测试过程。 本试验将在Sophon Base中构建一个简略的卷积神经网络,并应用一部分MNIST数据集训练它辨认手写数字。 数据集介绍MNIST数据集蕴含60000个训练集和10000测试数据集。分为图片和标签,图片是28*28的像素矩阵,标签为0~9共10个数字。 MINST数据集 本次试验应用的数据集为Sophon Base平台提供的images_small_minst数据集,该数据集选取了MINST数据集的一小部分图片,训练集加测试集总共500个。具体字段如下所示: small_minst数据集 试验环境星环科技Sophon Base数据迷信平台 开始试验在创立我的项目之前,本文默认您的sophon平台及各种配置文件曾经胜利装置,失常运行,并且您已胜利注册sophon 用户。 进入我的项目首页,点击 “+新建我的项目” 以新建一个我的项目。 在我的项目首页,点击创立好的我的项目。点击左侧的试验,点击试验以新建空白试验。试验创立后会主动进入该试验界面。 从左侧将算子“images_small_minst”、“字符串索引”、“设置角色”、“人工神经网络”、“利用模型”、“性能(分类)”拖动至右侧工作区并依照下图进行连贯: 算子连贯示意图 算子的连贯也能够参考下方视频进行操作: ,时长00:59 主流程算子连贯 算子参数设置如下:“字符串索引” 算子 ...

September 28, 2021 · 1 min · jiezi

关于cnn:技术博客目标检测算法RCNN介绍

指标检测算法R-CNN介绍作者:高雨茁 指标检测简介指标检测(Object Detection)的工作是找出图像中所有感兴趣的指标(物体),确定它们的类别和地位。计算机视觉中对于图像识别有四大类工作:1.分类-Classification:解决“是什么?”的问题,即给定一张图片或一段视频判断外面蕴含什么类别的指标。2.定位-Location:解决“在哪里?”的问题,即定位出这个指标的的地位。3.检测-Detection:解决“是什么?在哪里?”的问题,即定位出这个指标的的地位并且晓得指标物是什么。4.宰割-Segmentation:分为实例的宰割(Instance-level)和场景宰割(Scene-level),解决“每一个像素属于哪个指标物或场景”的问题。 以后指标检测算法分类1.Two stage指标检测算法先进行区域生成(region proposal,RP)(一个有可能蕴含待检物体的预选框),再通过卷积神经网络进行样本分类。工作:特征提取—>生成RP—>分类/定位回归。常见的two stage指标检测算法有:R-CNN、SPP-Net、Fast R-CNN、Faster R-CNN和R-FCN等。 2.One stage指标检测算法不必RP,间接在网络中提取特色来预测物体分类和地位。工作:特征提取—>分类/定位回归。常见的one stage指标检测算法有:OverFeat、YOLOv1、YOLOv2、YOLOv3、SSD和RetinaNet等。 本文后续将介绍其中的经典算法R-CNN并给出相应的代码实现。 R-CNNR-CNN(Regions with CNN features)是将CNN办法利用到指标检测问题上的一个里程碑。借助CNN良好的特征提取和分类性能,通过RegionProposal办法实现目标检测问题的转化。算法分为四个步骤: 从原图像生成候选区域(RoI proposal)将候选区域输出CNN进行特征提取将特色送入每一类别的SVM检测器,判断是否属于该类通过边界回归失去准确的指标区域算法前向流程图如下(图中数字标记对应上述四个步骤):在下文中咱们也会依照上述四个步骤的程序解说模型构建,在这之后咱们会解说如何进行模型训练。但在开始具体上述操作之前,让咱们简略理解下在训练中咱们将会应用到的数据集。 数据集简介原论文中应用的数据集为:1.ImageNet ILSVC(一个较大的辨认库) 一千万图像,1000类。2.PASCAL VOC 2007(一个较小的检测库) 一万图像,20类。训练时应用辨认库进行预训练,而后用检测库调优参数并在检测库上评测模型成果。 因为原数据集容量较大,模型的训练工夫可能会达到几十个小时之久。为了简化训练,咱们替换了训练数据集。与原论文相似,咱们应用的数据包含两局部:1.含17种分类的花朵图片 2.含2种分类的花朵图片。 咱们后续将应用17分类数据进行模型的预训练,用2分类数据进行fine-tuning失去最终的预测模型,并在2分类图片上进行评测。 模型构建步骤一该步骤中咱们要实现的算法流程局部如下图数字标记:R-CNN中采纳了selective search算法来进行region proposal。该算法首先通过基于图的图像宰割办法初始化原始区域,行将图像宰割成很多很多的小块。而后应用贪婪策略,计算每两个相邻的区域的类似度,而后每次合并最类似的两块,直至最终只剩下一块残缺的图片。并将该过程中每次产生的图像块包含合并的图像块都保留下来作为最终的RoI(Region of Interest)集。具体算法流程如下:区域合并采纳了多样性的策略,如果简略采纳一种策略很容易谬误合并不类似的区域,比方只思考纹理时,不同色彩的区域很容易被误合并。selective search采纳三种多样性策略来减少候选区域以保障召回: 多种色彩空间,思考RGB、灰度、HSV及其变种多种类似度度量规范,既思考色彩类似度,又思考纹理、大小、重叠状况等通过扭转阈值初始化原始区域,阈值越大,宰割的区域越少很多机器学习框架都内置实现了selective search操作。 步骤二该步骤中咱们要实现的算法流程局部如下图数字标记:在步骤一中咱们失去了由selective search算法生成的region proposals,但各proposal大小根本不统一,思考到region proposals后续要被输出到ConvNet中进行特征提取,因而有必要将所有region proposals调整至对立且合乎ConvNet架构的规范尺寸。相干的代码实现如下: import matplotlib.patches as mpatches# Clip Imagedef clip_pic(img, rect): x = rect[0] y = rect[1] w = rect[2] h = rect[3] x_1 = x + w y_1 = y + h # return img[x:x_1, y:y_1, :], [x, y, x_1, y_1, w, h] return img[y:y_1, x:x_1, :], [x, y, x_1, y_1, w, h]#Resize Imagedef resize_image(in_image, new_width, new_height, out_image=None, resize_mode=cv2.INTER_CUBIC): img = cv2.resize(in_image, (new_width, new_height), resize_mode) if out_image: cv2.imwrite(out_image, img) return imgdef image_proposal(img_path): img = cv2.imread(img_path) img_lbl, regions = selective_search( img, scale=500, sigma=0.9, min_size=10) candidates = set() images = [] vertices = [] for r in regions: # excluding same rectangle (with different segments) if r['rect'] in candidates: continue # excluding small regions if r['size'] < 220: continue if (r['rect'][2] * r['rect'][3]) < 500: continue # resize to 227 * 227 for input proposal_img, proposal_vertice = clip_pic(img, r['rect']) # Delete Empty array if len(proposal_img) == 0: continue # Ignore things contain 0 or not C contiguous array x, y, w, h = r['rect'] if w == 0 or h == 0: continue # Check if any 0-dimension exist [a, b, c] = np.shape(proposal_img) if a == 0 or b == 0 or c == 0: continue resized_proposal_img = resize_image(proposal_img,224, 224) candidates.add(r['rect']) img_float = np.asarray(resized_proposal_img, dtype="float32") images.append(img_float) vertices.append(r['rect']) return images, vertices让咱们抉择一张图片查看下selective search算法成果 ...

August 18, 2020 · 9 min · jiezi

关于cnn:基于keras平台CNN神经网络模型的服装识别分析

原文链接:http://tecdat.cn/?p=8493在许多介绍图像识别工作的介绍中,通常应用着名的MNIST数据集。然而,这些数据存在一些问题: 1.太简略了。例如,一个简略的MLP模型能够达到99%的准确度,而一个2层CNN能够达到99%的准确度。 2.它被适度应用。从字面上看,每台机器学习入门文章或图像识别工作都将应用此数据集作为基准。然而,因为取得近乎完满的分类后果非常容易,所以它的实用性会受到打折,并且对于古代机器学习/ AI工作并不真正有用。 因而,呈现Fashion-MNIST数据集。该数据集是作为MNIST数据的间接代替而开发的,其意义在于: 1.尺寸和格调雷同:28x28灰度图像 2.每个图像与10个类中的1个相关联,即:        0:T恤/上衣,        1:裤子,        2:套头衫,        3:连衣裙,        4 :外套,        5:凉鞋,        6:衬衫,        7:运动鞋,        8:背包,        9:脚靴 3. 60000训练样本和10000个测试样本以下是一些样本的快照: 自从它呈现以来,曾经有多份提交文件来对这些数据进行基准测试,其中一些可能达到95%以上的准确度 。  我也试图用keras来对这个数据进行基准测试。keras是构建深度学习模型的高级框架,在后端抉择TensorFlow,Theano和CNTK。它很容易装置和应用。对于我的应用程序,我应用了CNTK后端。  在这里,我将以两个模型为基准。一种是层构造为256-512-100-10的MLP,另一种是类VGG的CNN。  第一个模型在100个历元后的测试数据上达到了[0.89,0.90]的精度,而后者达到了45个期间后的测试数据的精度> 0.94。  咱们先用tSNE来看它。据说tSNE是最无效的尺寸放大工具。   我应用了1000个样本来疾速运行。如果您的PC速度足够快并且有工夫,则能够针对残缺数据集运行tSNE。  咱们看到,包含品质大小,底部决裂和对称性等几个特色将类别离开。  为了建设本人的网络,咱们首先导入一些库 该模型在大概100个期间的测试数据集上达到了近90%的准确度。当初,咱们来构建一个相似VGG的CNN模型。咱们应用相似于VGG的体系结构,但依然十分不同。因为图形数据很小,如果咱们应用原始VGG体系结构,它很可能会适度配合,并且在测试数据时体现不佳,这些数据在下面列出的公开提交的基准测试中察看到。在keras中构建这样一个模型是十分天然和容易的: 这个模型有150万个参数。咱们能够调用'fit'办法来训练模型: model3_fit=model3.fit(X_train, Y_train2, validation_data = (X_test, Y_test2), epochs=50, verbose=1, batch_size=500)通过40次当前,这个模型在测试数据上取得了0.94的精度。显然,这个模型也存在适度拟合问题。咱们稍后会解决这个问题。

July 17, 2020 · 1 min · jiezi

经验拾忆纯手工-CNNRNNNg

前言看Andrew Ng视频,总结的学习心得。虽然本篇文章可能不是那么细致入微,甚至可能有了解偏差。但是,我喜欢用更直白的方式去理解知识。上一篇文章传送门: https://segmentfault.com/a/11... 端到端首先聊一个面试经历我最开始接触的是ML (但只限于Sklearn的简单应用,工程化的内容当时一点都不了解。)后来有幸了解到DL (这个了解比较多)我面的是普通Python岗, 因为我的小项目中涉及到 (聊天机器人)。所以第二个面试官揪着这个聊了聊。与面试官交谈时,我也直接挑明了,模型是Github找的,当时自己爬了些问答对,处理后放入模型自己训练的。面试官一顿(特征提取,语义)等各种 ML-NLP工程化的过程,把我直接问懵了。。 怎么提取特征(问号脸,难道是TF-IDF,分词之类的??)?????我也不知道说啥,以仅有的能力,和他聊聊(LSTM、Embedding, Seq2Seq的 Encoder-Vector-Decoder)。。 面试官说:“你说了这些, 那你特征工程是怎么做的???”我感觉已经没有任何反驳的能力了。。。接下来的事情,我不说,大家也应该清楚了。 反思我回来后也反思过, 做了什么特征工程?? 我看视频中 也是,数据简单预处理下,然后分词,词频过滤,构建词典然后,直接就是构建NN层(包括Embedding层)。 直到最后了解了"端到端这个概念" 与 传统ML的区别。才清楚, 当时面试的场景是怎么个情况。。。 正式开篇端到端传统ML: 原数据 -> 数据特征工程(各种复杂的人工处理) ---> 模型端到端DL:原数据 -----------------------------------------------------> 模型 端到端:(一步到位): 传统的ML做的中间层人工"手动"特征工程处理出来的特征。 这些特征,端到端的NN都可能"自动学习"的到。 这也可能是当时为什么面试官一直追问我"特征如何处理"的原因吧。也肯能他另有目的QAQ...或者我们真的不在一个频道上。。。但是交流的过程真的使我受益匪浅,有了更广阔的视野(3Q!)强调一点: 虽然端到端 模型很便捷。但是需要大量的数据,才能训练出好的效果。CNN (卷积神经网络)构成卷积层(激活函数) + 池化层 + 全连接层Convolution + Pooling + Dense至于一些术语:有人喜欢把: 卷积层 + 池化层 作为一层网络 (因为池化层无训练训练,后面会提到)也有人喜欢把: 卷积层 和 池化层 各自单独算一个层(也是没问题的。Tensorflow的API就是这样设计的)卷积层(Convolution Layer)卷积过程卷积计算过程就不说了。没有案例图。但你可以理解为: 两个 正方体 的 对应位置的元素 (相乘再相加)的结果。。。 (互相关,,,)卷积的输出计算输出图像大小计算公式: h图片输出 = (h图片输入 - h卷积核 + 2padding) / strides + 1w图片输出 = (w图片输入 - w卷积核 + 2padding) / strides + 1首先声明: 这个式子由于不一定能够整除, 因此除不尽的情况下,向下取整, 也叫地板除因为有个原则: 卷积核滑动的时候(通常是,步长>1的情况下) 如果越界了一部分。则舍弃掉根据上面的公式,求一个输出图像大小的例子(此处,不做paddding, 并且步长为1) ...

October 9, 2019 · 7 min · jiezi

机器学习 | CNN卷积神经网络

测试结果最后两行分别为预测类别与真实类别。数据预览这里的数据使用的是mnist数据集,大家可以将代码中的DOWNLOAD_MNIST值修改为True进行自动下载。代码import torchimport torch.nn as nnimport torch.utils.data as Dataimport torchvision # 数据库模块import matplotlib.pyplot as plt#训练整批数据多少次,这里为了节约时间,只训练一次EPOCH=1#每次批处理50个数据BATCH_SIZE=50#学习效率LR=0.001# 如果已经下载好了mnist数据就写上FalseDOWNLOAD_MNIST = False #训练的数据集:Mnist手写数字train_data=torchvision.datasets.MNIST( #保存或提取数据集的位置 root=’./mnist/’, #该数据是训练数据 train=True, #转换PIL.Image or numpy.ndarray成torch.FloatTensor (C x H x W), 训练的时候 normalize 成 [0.0, 1.0] 区间 transform=torchvision.transforms.ToTensor(), #没下载就下载,下载了就不用再下了 download=DOWNLOAD_MNIST,)#绘制一下数据集#黑色的地方的值都是0, 白色的地方值大于0.print(train_data.train_data.size()) # (60000, 28, 28)print(train_data.train_labels.size()) # (60000)plt.imshow(train_data.train_data[2].numpy(), cmap=‘gray’)plt.title(’%i’ % train_data.train_labels[2])plt.show()#测试数据test_data=torchvision.datasets.MNIST(root=’./mnist/’,train=False)#批训练50samples,1 channel,28x28 (50, 1, 28, 28)train_loader=Data.DataLoader(dataset=train_data,batch_size=BATCH_SIZE,shuffle=True)#这里只测试了前2000个#特征test_x=torch.unsqueeze(test_data.test_data,dim=1).type(torch.FloatTensor)[:2000]/255.#标签test_y=test_data.test_labels[:2000]#构建CNN模型class CNN(nn.Module): def init(self): super(CNN,self).init() #input shape(1,28,28) self.conv1=nn.Sequential( #卷积 nn.Conv2d( in_channels=1, out_channels=16, #filter size kernel_size=5, #filter movement/step stride=1, #如果想要con2d出来的图片长宽没有变化, #padding=(kernel_size-1)/2当stride=1 padding=2, ), #output shape(16,28,28) #激励函数 nn.ReLU(), #池化 # 在2x2空间里向下采样,output shape(16,14,14) nn.MaxPool2d(kernel_size=2), ) #input shape(16,14,14) self.conv2=nn.Sequential( nn.Conv2d(16,32,5,1,2), #output shape(32,14,14) #激励函数 nn.ReLU(), #output shape(32,7,7) nn.MaxPool2d(2), ) #全连接层——进行分类。这里将其分成了10类 self.out=nn.Linear(3277,10) def forward(self,x): x=self.conv1(x) x=self.conv2(x) #展平多维的卷积图成(batch_size,3277) x=x.view(x.size(0),-1) output=self.out(x) return outputcnn=CNN()print(cnn)#训练#优化器optimizer=torch.optim.Adam(cnn.parameters(),lr=LR)#损失函数loss_func=nn.CrossEntropyLoss()#开始训练for epoch in range(EPOCH): for step,(b_x,b_y) in enumerate(train_loader): #将数据输入nn并且得到output output=cnn(b_x) #计算output与真实值之间的误差 loss=loss_func(output,b_y) #清空上一步残余更新参数值 optimizer.zero_grad() #误差反向传播,让参数进行更新 loss.backward() #将更新后的参数值施加到nn的parameters上 optimizer.step()#测试:选取10个数据test_output=cnn(test_x[:10])pred_y=torch.max(test_output,1)[1].data.numpy().squeeze()print(pred_y, ‘prediction number’)print(test_y[:10].numpy(), ‘real number’)# if name==’main’:# print(“hello word”) ...

April 3, 2019 · 1 min · jiezi

活体检测很复杂?仅使用opencv就能实现!(附源码)

摘要: 活体检测在各行各业应用比较广泛,如何实现一个活体检测系统呢?早期实现很困难,现在仅使用opencv即可实现,快来尝试一下吧。什么是活体检测,为什么需要它?随着时代的发展,人脸识别系统的应用也正变得比以往任何时候都更加普遍。从智能手机上的人脸识别解锁、到人脸识别打卡、门禁系统等,人脸识别系统正在各行各业得到应用。然而,人脸识别系统很容易被“非真实”的面孔所欺骗。比如将人的照片放在人脸识别相机,就可以骗过人脸识别系统,让其识别为人脸。为了使人脸识别系统更安全,我们不仅要识别出人脸,还需要能够检测其是否为真实面部,这就要用到活体检测了。目前有许多活体检测方法,包括:纹理分析(Texture analysis),包括计算面部区域上的局部二进制模式(LBP)并使用SVM将面部分类为真脸或假脸;频率分析(Frequency analysis),例如检查面部的傅里叶域;可变聚焦分析(ariable focusing analysis),例如检查两个连续帧之间的像素值的变化。基于启发式的算法(Heuristic-based algorithms),包括眼球运动、嘴唇运动和眨眼检测;光流算法(Optical Flow algorithms),即检查从3D对象和2D平面生成的光流的差异和属性;3D脸部形状,类似于Apple的iPhone脸部识别系统所使用的脸部形状,使脸部识别系统能够区分真人脸部和其他人的打印输出的照片图像;面部识别系统工程师可以组合上述方法挑选和选择适合于其特定应用的活体检测模型。但本教程将采用图像处理中常用方法——卷积神经网络(CNN)来构建一个能够区分真实面部和假面部的深度神经网络(称之为“LivenessNet”网络),将活体检测视为二元分类问题。首先检查一下数据集。活动检测视频为了让例子更加简单明了,本文构建的活体检测器将侧重于区分真实面孔与屏幕上的欺骗面孔。且该算法可以很容易地扩展到其他类型的欺骗面孔,包括打印输出、高分辨率打印等。活体检测数据集来源:iPhone纵向/自拍;录制了一段约25秒在办公室里走来走去的视频;重播了相同的25秒视频,iPhone重录视频;获得两个示例视频,一个用于“真实”面部,另一个用于“假/欺骗”面部。最后,将面部检测应用于两组视频,以提取两个类的单个面部区域。项目结构$ tree –dirsfirst –filelimit 10.├── dataset│ ├── fake [150 entries]│ └── real [161 entries]├── face_detector│ ├── deploy.prototxt│ └── res10_300x300_ssd_iter_140000.caffemodel├── pyimagesearch│ ├── init.py│ └── livenessnet.py├── videos│ ├── fake.mp4│ └── real.mov├── gather_examples.py├── train_liveness.py├── liveness_demo.py├── le.pickle├── liveness.model└── plot.png6 directories, 12 files项目中主要有四个目录:*dataset /:数据集目录,包含两类图像:在播放脸部视频时,手机录屏得到的假脸;手机自拍视频中真脸;face_detector /:由预训练Caffe面部检测器组成,用于定位面部区域;pyimagesearch /:模块包含LivenessNet类函数;video/:提供了两个用于训练了LivenessNet分类器的输入视频;另外还有三个Python脚本:gather_examples.py:此脚本从输入视频文件中获取面部区域,并创建深度学习面部数据集;train_liveness.py:此脚本将训练LivenessNet分类器。训练会得到以下几个文件:1.le .pickle:类别标签编码器;2.liveness.model:训练好的Keras模型;3.plot.png:训练历史图显示准确度和损失曲线;liveness_demo.py:该演示脚本将启动网络摄像头以进行面部实时活体检测;从训练数据集中检测和提取面部区域数据目录:1.dataset / fake /:包含假.mp4文件中的面部区域;2.dataset / real /:保存来自真实.mov文件的面部区域;打开 gather_examples.py文件并插入以下代码:# import the necessary packagesimport numpy as npimport argparseimport cv2import os# construct the argument parse and parse the argumentsap = argparse.ArgumentParser()ap.add_argument("-i", “–input”, type=str, required=True, help=“path to input video”)ap.add_argument("-o", “–output”, type=str, required=True, help=“path to output directory of cropped faces”)ap.add_argument("-d", “–detector”, type=str, required=True, help=“path to OpenCV’s deep learning face detector”)ap.add_argument("-c", “–confidence”, type=float, default=0.5, help=“minimum probability to filter weak detections”)ap.add_argument("-s", “–skip”, type=int, default=16, help="# of frames to skip before applying face detection")args = vars(ap.parse_args())首先导入所需的包:第8-19行解析命令行参数:input:输入视频文件的路径;output:输出目录的路径;detector:人脸检测器的路径;confidence:人脸检测的最小概率。默认值为0.5;skip:检测时略过的帧数,默认值为16;之后加载面部检测器并初始化视频流:# load our serialized face detector from diskprint("[INFO] loading face detector…")protoPath = os.path.sep.join([args[“detector”], “deploy.prototxt”])modelPath = os.path.sep.join([args[“detector”], “res10_300x300_ssd_iter_140000.caffemodel”])net = cv2.dnn.readNetFromCaffe(protoPath, modelPath)# open a pointer to the video file stream and initialize the total# number of frames read and saved thus farvs = cv2.VideoCapture(args[“input”])read = 0saved = 0此外还初始化了两个变量,用于读取的帧数以及循环执行时保存的帧数。创建一个循环来处理帧:# loop over frames from the video file streamwhile True: # grab the frame from the file (grabbed, frame) = vs.read() # if the frame was not grabbed, then we have reached the end # of the stream if not grabbed: break # increment the total number of frames read thus far read += 1 # check to see if we should process this frame if read % args[“skip”] != 0: continue下面进行面部检测: # grab the frame dimensions and construct a blob from the frame (h, w) = frame.shape[:2] blob = cv2.dnn.blobFromImage(cv2.resize(frame, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0)) # pass the blob through the network and obtain the detections and # predictions net.setInput(blob) detections = net.forward() # ensure at least one face was found if len(detections) > 0: # we’re making the assumption that each image has only ONE # face, so find the bounding box with the largest probability i = np.argmax(detections[0, 0, :, 2]) confidence = detections[0, 0, i, 2]为了执行面部检测,需要从图像中创建一个区域,该区域有300×300的宽度和高度,以适应Caffe面部检测器。此外脚本假设视频的每一帧中只有一个面部,这有助于防止误报。获得最高概率的面部检测指数,并使用索引提取检测的置信度,之后将低概率的进行过滤,并将结果写入磁盘: # ensure that the detection with the largest probability also # means our minimum probability test (thus helping filter out # weak detections) if confidence > args[“confidence”]: # compute the (x, y)-coordinates of the bounding box for # the face and extract the face ROI box = detections[0, 0, i, 3:7] * np.array([w, h, w, h]) (startX, startY, endX, endY) = box.astype(“int”) face = frame[startY:endY, startX:endX] # write the frame to disk p = os.path.sep.join([args[“output”], “{}.png”.format(saved)]) cv2.imwrite(p, face) saved += 1 print("[INFO] saved {} to disk".format(p))# do a bit of cleanupvs.release()cv2.destroyAllWindows()提取到面部区域后,就可以得到面部的边界框坐标。然后为面部区域生成路径+文件名,并将其写入磁盘中。构建活体检测图像数据集打开终端并执行以下命令来提取“假/欺骗”类别的面部图像:$ python gather_examples.py –input videos/real.mov –output dataset/real \ –detector face_detector –skip 1[INFO] loading face detector…[INFO] saved datasets/fake/0.png to disk[INFO] saved datasets/fake/1.png to disk[INFO] saved datasets/fake/2.png to disk[INFO] saved datasets/fake/3.png to disk[INFO] saved datasets/fake/4.png to disk[INFO] saved datasets/fake/5.png to disk…[INFO] saved datasets/fake/145.png to disk[INFO] saved datasets/fake/146.png to disk[INFO] saved datasets/fake/147.png to disk[INFO] saved datasets/fake/148.png to disk[INFO] saved datasets/fake/149.png to disk同理也可以执行以下命令获得“真实”类别的面部图像:$ python gather_examples.py –input videos/fake.mov –output dataset/fake \ –detector face_detector –skip 4[INFO] loading face detector…[INFO] saved datasets/real/0.png to disk[INFO] saved datasets/real/1.png to disk[INFO] saved datasets/real/2.png to disk[INFO] saved datasets/real/3.png to disk[INFO] saved datasets/real/4.png to disk…[INFO] saved datasets/real/156.png to disk[INFO] saved datasets/real/157.png to disk[INFO] saved datasets/real/158.png to disk[INFO] saved datasets/real/159.png to disk[INFO] saved datasets/real/160.png to disk注意,这里要确保数据分布均衡。执行脚本后,统计图像数量:假:150张图片真:161张图片总计:311张图片实施“LivenessNet”深度学习活体检测模型LivenessNet实际上只是一个简单的卷积神经网络,尽量将这个网络设计的尽可能浅,参数尽可能少,原因有两个:减少过拟合可能性;确保活体检测器能够实时运行;打开livenessnet .py并插入以下代码:# import the necessary packagesfrom keras.models import Sequentialfrom keras.layers.normalization import BatchNormalizationfrom keras.layers.convolutional import Conv2Dfrom keras.layers.convolutional import MaxPooling2Dfrom keras.layers.core import Activationfrom keras.layers.core import Flattenfrom keras.layers.core import Dropoutfrom keras.layers.core import Densefrom keras import backend as Kclass LivenessNet: @staticmethod def build(width, height, depth, classes): # initialize the model along with the input shape to be # “channels last” and the channels dimension itself model = Sequential() inputShape = (height, width, depth) chanDim = -1 # if we are using “channels first”, update the input shape # and channels dimension if K.image_data_format() == “channels_first”: inputShape = (depth, height, width) chanDim = 1 # first CONV => RELU => CONV => RELU => POOL layer set model.add(Conv2D(16, (3, 3), padding=“same”, input_shape=inputShape)) model.add(Activation(“relu”)) model.add(BatchNormalization(axis=chanDim)) model.add(Conv2D(16, (3, 3), padding=“same”)) model.add(Activation(“relu”)) model.add(BatchNormalization(axis=chanDim)) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) # second CONV => RELU => CONV => RELU => POOL layer set model.add(Conv2D(32, (3, 3), padding=“same”)) model.add(Activation(“relu”)) model.add(BatchNormalization(axis=chanDim)) model.add(Conv2D(32, (3, 3), padding=“same”)) model.add(Activation(“relu”)) model.add(BatchNormalization(axis=chanDim)) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) # first (and only) set of FC => RELU layers model.add(Flatten()) model.add(Dense(64)) model.add(Activation(“relu”)) model.add(BatchNormalization()) model.add(Dropout(0.5)) # softmax classifier model.add(Dense(classes)) model.add(Activation(“softmax”)) # return the constructed network architecture return model创建活体检测器训练脚本打开train_liveness .py文件并插入以下代码:# set the matplotlib backend so figures can be saved in the backgroundimport matplotlibmatplotlib.use(“Agg”)# import the necessary packagesfrom pyimagesearch.livenessnet import LivenessNetfrom sklearn.preprocessing import LabelEncoderfrom sklearn.model_selection import train_test_splitfrom sklearn.metrics import classification_reportfrom keras.preprocessing.image import ImageDataGeneratorfrom keras.optimizers import Adamfrom keras.utils import np_utilsfrom imutils import pathsimport matplotlib.pyplot as pltimport numpy as npimport argparseimport pickleimport cv2import os# construct the argument parser and parse the argumentsap = argparse.ArgumentParser()ap.add_argument("-d", “–dataset”, required=True, help=“path to input dataset”)ap.add_argument("-m", “–model”, type=str, required=True, help=“path to trained model”)ap.add_argument("-l", “–le”, type=str, required=True, help=“path to label encoder”)ap.add_argument("-p", “–plot”, type=str, default=“plot.png”, help=“path to output loss/accuracy plot”)args = vars(ap.parse_args())此脚本接受四个命令行参数:dataset:输入数据集的路径;model:输出模型文件保存路径;le:输出序列化标签编码器文件的路径;plot:训练脚本将生成一个图;下一个代码块将执行初始化并构建数据:# initialize the initial learning rate, batch size, and number of# epochs to train forINIT_LR = 1e-4BS = 8EPOCHS = 50# grab the list of images in our dataset directory, then initialize# the list of data (i.e., images) and class imagesprint("[INFO] loading images…")imagePaths = list(paths.list_images(args[“dataset”]))data = []labels = []for imagePath in imagePaths: # extract the class label from the filename, load the image and # resize it to be a fixed 32x32 pixels, ignoring aspect ratio label = imagePath.split(os.path.sep)[-2] image = cv2.imread(imagePath) image = cv2.resize(image, (32, 32)) # update the data and labels lists, respectively data.append(image) labels.append(label)# convert the data into a NumPy array, then preprocess it by scaling# all pixel intensities to the range [0, 1]data = np.array(data, dtype=“float”) / 255.0之后对标签进行独热编码并对将数据划分为训练数据(75%)和测试数据(25%):# encode the labels (which are currently strings) as integers and then# one-hot encode themle = LabelEncoder()labels = le.fit_transform(labels)labels = np_utils.to_categorical(labels, 2)# partition the data into training and testing splits using 75% of# the data for training and the remaining 25% for testing(trainX, testX, trainY, testY) = train_test_split(data, labels, test_size=0.25, random_state=42)之后对数据进行扩充并对模型进行编译和训练:# construct the training image generator for data augmentationaug = ImageDataGenerator(rotation_range=20, zoom_range=0.15, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.15, horizontal_flip=True, fill_mode=“nearest”)# initialize the optimizer and modelprint("[INFO] compiling model…")opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS)model = LivenessNet.build(width=32, height=32, depth=3, classes=len(le.classes_))model.compile(loss=“binary_crossentropy”, optimizer=opt, metrics=[“accuracy”])# train the networkprint("[INFO] training network for {} epochs…".format(EPOCHS))H = model.fit_generator(aug.flow(trainX, trainY, batch_size=BS), validation_data=(testX, testY), steps_per_epoch=len(trainX) // BS, epochs=EPOCHS)模型训练后,可以评估效果并生成仿真曲线图:# evaluate the networkprint("[INFO] evaluating network…")predictions = model.predict(testX, batch_size=BS)print(classification_report(testY.argmax(axis=1), predictions.argmax(axis=1), target_names=le.classes_))# save the network to diskprint("[INFO] serializing network to ‘{}’…".format(args[“model”]))model.save(args[“model”])# save the label encoder to diskf = open(args[“le”], “wb”)f.write(pickle.dumps(le))f.close()# plot the training loss and accuracyplt.style.use(“ggplot”)plt.figure()plt.plot(np.arange(0, EPOCHS), H.history[“loss”], label=“train_loss”)plt.plot(np.arange(0, EPOCHS), H.history[“val_loss”], label=“val_loss”)plt.plot(np.arange(0, EPOCHS), H.history[“acc”], label=“train_acc”)plt.plot(np.arange(0, EPOCHS), H.history[“val_acc”], label=“val_acc”)plt.title(“Training Loss and Accuracy on Dataset”)plt.xlabel(“Epoch #")plt.ylabel(“Loss/Accuracy”)plt.legend(loc=“lower left”)plt.savefig(args[“plot”])训练活体检测器执行以下命令开始模型训练:$ python train.py –dataset dataset –model liveness.model –le le.pickle[INFO] loading images…[INFO] compiling model…[INFO] training network for 50 epochs…Epoch 1/5029/29 [==============================] - 2s 58ms/step - loss: 1.0113 - acc: 0.5862 - val_loss: 0.4749 - val_acc: 0.7436Epoch 2/5029/29 [==============================] - 1s 21ms/step - loss: 0.9418 - acc: 0.6127 - val_loss: 0.4436 - val_acc: 0.7949Epoch 3/5029/29 [==============================] - 1s 21ms/step - loss: 0.8926 - acc: 0.6472 - val_loss: 0.3837 - val_acc: 0.8077…Epoch 48/5029/29 [==============================] - 1s 21ms/step - loss: 0.2796 - acc: 0.9094 - val_loss: 0.0299 - val_acc: 1.0000Epoch 49/5029/29 [==============================] - 1s 21ms/step - loss: 0.3733 - acc: 0.8792 - val_loss: 0.0346 - val_acc: 0.9872Epoch 50/5029/29 [==============================] - 1s 21ms/step - loss: 0.2660 - acc: 0.9008 - val_loss: 0.0322 - val_acc: 0.9872[INFO] evaluating network… precision recall f1-score support fake 0.97 1.00 0.99 35 real 1.00 0.98 0.99 43 micro avg 0.99 0.99 0.99 78 macro avg 0.99 0.99 0.99 78weighted avg 0.99 0.99 0.99 78[INFO] serializing network to ’liveness.model’…从上述结果来看,在测试集上获得99%的检测精度!合并起来:使用OpenCV进行活体检测最后一步是将所有部分组合在一起:访问网络摄像头/视频流;对每个帧应用面部检测;对于检测到的每个脸部,应用活体检测器模型;打开liveness_demo.py并插入以下代码:# import the necessary packagesfrom imutils.video import VideoStreamfrom keras.preprocessing.image import img_to_arrayfrom keras.models import load_modelimport numpy as npimport argparseimport imutilsimport pickleimport timeimport cv2import os# construct the argument parse and parse the argumentsap = argparse.ArgumentParser()ap.add_argument("-m", "--model", type=str, required=True, help="path to trained model")ap.add_argument("-l", "--le", type=str, required=True, help="path to label encoder")ap.add_argument("-d", "--detector", type=str, required=True, help="path to OpenCV's deep learning face detector")ap.add_argument("-c", "--confidence", type=float, default=0.5, help="minimum probability to filter weak detections")args = vars(ap.parse_args())上述代码导入必要的包,并加载模型。下面初始化人脸检测器、LivenessNet模型以及视频流:# load our serialized face detector from diskprint("[INFO] loading face detector...")protoPath = os.path.sep.join([args["detector"], "deploy.prototxt"])modelPath = os.path.sep.join([args["detector"], "res10_300x300_ssd_iter_140000.caffemodel"])net = cv2.dnn.readNetFromCaffe(protoPath, modelPath)# load the liveness detector model and label encoder from diskprint("[INFO] loading liveness detector...")model = load_model(args["model"])le = pickle.loads(open(args["le"], "rb").read())# initialize the video stream and allow the camera sensor to warmupprint("[INFO] starting video stream...")vs = VideoStream(src=0).start()time.sleep(2.0)之后开始循环遍历视频的每一帧以检测面部是否真实:# loop over the frames from the video streamwhile True: # grab the frame from the threaded video stream and resize it # to have a maximum width of 600 pixels frame = vs.read() frame = imutils.resize(frame, width=600) # grab the frame dimensions and convert it to a blob (h, w) = frame.shape[:2] blob = cv2.dnn.blobFromImage(cv2.resize(frame, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0)) # pass the blob through the network and obtain the detections and # predictions net.setInput(blob) detections = net.forward()使用OpenCV blobFromImage函数生成一个面部数据,然后将其传递到面部检测器网络继续进行推理。核心代码如下: # loop over the detections for i in range(0, detections.shape[2]): # extract the confidence (i.e., probability) associated with the # prediction confidence = detections[0, 0, i, 2] # filter out weak detections if confidence &gt; args["confidence"]: # compute the (x, y)-coordinates of the bounding box for # the face and extract the face ROI box = detections[0, 0, i, 3:7] * np.array([w, h, w, h]) (startX, startY, endX, endY) = box.astype("int") # ensure the detected bounding box does fall outside the # dimensions of the frame startX = max(0, startX) startY = max(0, startY) endX = min(w, endX) endY = min(h, endY) # extract the face ROI and then preproces it in the exact # same manner as our training data face = frame[startY:endY, startX:endX] face = cv2.resize(face, (32, 32)) face = face.astype("float") / 255.0 face = img_to_array(face) face = np.expand_dims(face, axis=0) # pass the face ROI through the trained liveness detector # model to determine if the face is "real" or "fake" preds = model.predict(face)[0] j = np.argmax(preds) label = le.classes_[j] # draw the label and bounding box on the frame label = "{}: {:.4f}".format(label, preds[j]) cv2.putText(frame, label, (startX, startY - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) cv2.rectangle(frame, (startX, startY), (endX, endY), (0, 0, 255), 2)首先过滤掉弱检测结果,然后提取面部图像并对其进行预处理,之后送入到活动检测器模型来确定面部是“真实的”还是“假的/欺骗的”。最后,在原图上绘制标签和添加文本以及矩形框,最后进行展示和清理。 # show the output frame and wait for a key press cv2.imshow("Frame", frame) key = cv2.waitKey(1) &amp; 0xFF # if the q` key was pressed, break from the loop if key == ord(“q”): break# do a bit of cleanupcv2.destroyAllWindows()vs.stop()将活体检测器应用到实时视频上打开终端并执行以下命令:$ python liveness_demo.py –model liveness.model –le le.pickle \ –detector face_detectorUsing TensorFlow backend.[INFO] loading face detector…[INFO] loading liveness detector…[INFO] starting video stream…可以看到,活体检测器成功地区分了真实和伪造的面孔。下面的视频作为一个更长时间的演示:视频地址进一步的工作本文设计的系统还有一些限制和缺陷,主要限制实际上是数据集有限——总共只有311个图像。这项工作的第一个扩展之一是简单地收集额外的训练数据,比如其它人,其它肤色或种族的人。此外,活体检测器只是通过屏幕上的恶搞攻击进行训练,它并没有经过打印出来的图像或照片的训练。因此,建议添加不同类型的图像源。最后,我想提一下,活体检测没有最好的方法,只有最合适的方法。一些好的活体检测器包含多种活体检测方法。总结在本教程中,学习了如何使用OpenCV进行活动检测。使用此活体检测器就可以在自己的人脸识别系统中发现伪造的假脸并进行反面部欺骗。此外,创建活动检测器使用了OpenCV、Deep Learning和Python等领域的知识。整个过程如下:第一步是收集真假数据集。数据来源有:智能手机录制自己的视频(即“真”面);手机录播(即“假”面);对两组视频应用面部检测以形成最终数据集。第二步,获得数据集之后,实现了“LivenessNet”网络,该网络设计的比较浅层,这是为了确保:减少了过拟合小数据集的可能性;该模型本身能够实时运行;总的来说,本文设计的活体检测器能够在验证集上获得99%的准确度。此外,活动检测器也能够应用于实时视频流。本文作者:【方向】阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

March 18, 2019 · 9 min · jiezi

使用权重正则化较少模型过拟合

介绍权重正则化可以减轻深度神经网络模型的过拟合问题,可以提升对新数据的泛化能力。有多种正则方法可供选择,如:L1,L2正则化,每种方法在使用前需要超参数配置。在这篇文章中,你将学习在keras如何使用权重正则化的方法来减轻模型过拟合问题。读完本篇文章,你将学习到:如何在keras中使用权重正则化应用到MLP,CNN,或者LSTM神经网络任务中一些常见论文在模型中使用权重正则化的方法和经验通过一个案例学习如何使用权重正则化解决过拟合问题## keras中权重正则化方法 ##keras提供了权重正则化方法,可以在损失函数中通过添加惩罚系数来使用。keras提供了三种正则化方法:L1:绝对值权重之和L2:平方权重之和L1L2:两者累加之和tf.keras.regularizers.l1(l=0.01)tf.keras.regularizers.l2(l=0.01)tf.keras.regularizers.l1_l2(l1=0.01,l2=0.01)keras中,权重正则化可以应用到任意一层,不过,模型默认不使用任何权重正则化。全连接层使用权重正则化全连接层使用L2权重正则化:import tensorflow as tfmodel=tf.keras.models.Sequential( # 权重正则化,bias正则化(应用较少)tf.keras.layers.Dense(512,activation=tf.nn.relu,kernel_regularizer=tf.keras.regularizers.l2(l=0.001),bias_regularizer=tf.keras.regularizers.l2(l=0.001)))卷积层使用权重正则化同全连接层一样,卷积层也使用kernel_regularizer和bias_regularizer参数添加正则化。代码展示在卷积层中使用L2正则化。import tensorflow as tfmodel=tf.keras.models.Sequential( tf.keras.layers.Conv2D(32,3,activation=tf.nn.relu,kernel_regularizer=tf.keras.regularizers.l2(l=0.001),bias_regularizer=tf.keras.regularizers.l2(l=0.001)))RNN网络中使用权重正则化代码展示在LSTM网络中使用L2权重正则化import tensorflow as tfmodel=tf.keras.models.Sequential( tf.keras.layers.LSTM(32,activation=tf.nn.tanh,recurrent_regularizer=tf.keras.regularizers.l2(l=0.001),kernel_regularizer=tf.keras.regularizers.l2(l=0.001),bias_regularizer=tf.keras.regularizers.l2(l=0.001)))权重正则化使用经验最常见的权重正则化是L2正则化,数值通常是0-0.1之间,如:0.1,0.001,0.0001。找到最优的系数并不容易,需要尝试不同的权重系数,找到模型表现最平稳优秀的系数L2正则化在CNN网络中,建议系数设置小一些,如:0.0005少量的权重正则对模型很重要,可以减少模型训练误差LSTM网络中L2权重系数通常更小,如:10^-6权重正则化案例学习我们将使用标准二元分类问题来定义两个半圆观察:每个类一个半圆。每个观测值都有两个输入变量,它们具有相同的比例,类输出值为0或1.该数据集称为“月亮”数据集,因为绘制时每个类中的观测值的形状。# 导入sklearn中的数据集from sklearn.datasets import make_moonsfrom matplotlib import pyplotfrom pandas import DataFrame# 生成2分类数据集X, y = make_moons(n_samples=100, noise=0.2, random_state=1)print(X.shape)print(X[:6])print(y.shape)print(y[:6])df = DataFrame(dict(x=X[:,0], y=X[:,1], label=y))colors = {0:‘red’, 1:‘blue’}fig, ax = pyplot.subplots()grouped = df.groupby(’label’)for key, group in grouped: group.plot(ax=ax, kind=‘scatter’, x=‘x’, y=‘y’, label=key, color=colors[key])pyplot.show()sklearn常用数据集:数据集格式:matplot结果显示:如图所示,该问题是非线性问题,可以使用神经网络来解决。我们只生成了100个样本,对神经网络来说数据量很少,这很容易造成过拟合问题。我们使用正则化,添加噪声数据来处理问题。虽然卷积神经网络(CNN)功能强大,并广泛应用于各种计算机视觉任务中,但由于参数过多而导致过度拟合[22]。神经网络的最初发展受到人脑机制的启发[18],它不像计算机那样精确。受到差异的启发,我们推断在训练过程中添加噪音可能会指示CNN学习更强大的特征表示以抵消噪音的影响,从而降低过度拟合的风险。许多正则化方法通过向训练数据添加噪声来防止过拟合。数据增强的输入图像,如随机裁剪,翻转和阻塞[9,21,30]已广泛用于提高CNNs的泛化能力。 Adversarial Training [1]被提出来通过在图像中添加基于梯度的扰动来调整网络。 DisturbLabel [26]随机地将样本的一小部分子集的标签改变为不正确的值,从而在损失层上规则化CNN。过拟合模型我们创建一个只有一层隐藏层的MLP模型,并让神经元数量大于样本数量,然后过长时间训练模型,以此来人为造成过拟合问题。训练模型之前,我们拆分下数据集,训练数据30%,验证数据70%,来训练模型表现。X, y = make_moons(n_samples=100, noise=0.2, random_state=1)# 拆分数据集n_train = 30trainX, testX = X[:n_train, :], X[n_train:, :]trainy, testy = y[:n_train], y[n_train:]模型隐藏层有500个神经元,激活函数使用relu,输出层使用sigmoid激活函数,输出一项类别。模型使用bind_crossentropy损失函数,adam优化器。model = Sequential()model.add(Dense(500, input_dim=2, activation=‘relu’))model.add(Dense(1, activation=‘sigmoid’))model.compile(loss=‘binary_crossentropy’, optimizer=‘adam’, metrics=[‘accuracy’])模型迭代数据集400次,batch_size=32。model.fit(trainX, trainy, epochs=4000, verbose=0)在测试集上评估模型表现:# model.evaluate返回:loss value;metrics value_, train_acc = model.evaluate(trainX, trainy, verbose=0), test_acc = model.evaluate(testX, testy, verbose=0)print(‘Train: %.3f, Test: %.3f’ % (train_acc, test_acc))模型输出结果:我们看到训练表现远大于测试表现,这是过拟合问题的典型标志。我们将train和test训练精度过程图形化展示出来。from sklearn.datasets import make_moonsfrom keras.layers import Densefrom keras.models import Sequentialfrom matplotlib import pyplotX, y = make_moons(n_samples=100, noise=0.2, random_state=1)n_train = 30trainX, testX = X[:n_train, :], X[n_train:, :]trainy, testy = y[:n_train], y[n_train:]# 创建模型model = Sequential()model.add(Dense(500, input_dim=2, activation=‘relu’))model.add(Dense(1, activation=‘sigmoid’))model.compile(loss=‘binary_crossentropy’, optimizer=‘adam’, metrics=[‘accuracy’])# 返回训练,验证集的损失和准确率history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=4000, verbose=0)pyplot.plot(history.history[‘acc’], label=‘train’)pyplot.plot(history.history[‘val_acc’], label=‘test’)pyplot.legend()pyplot.show()如图所示,在某一点,train和test的准确率出现分叉口。使用正则化的模型我们将在隐藏层中使用权重正则化,并设置系数为0.001,以此来减轻过拟合问题。model.add(Dense(500, input_dim=2, activation=‘relu’, kernel_regularizer=l2(0.001)))完整代码如下:from sklearn.datasets import make_moonsfrom keras.layers import Densefrom keras.models import Sequentialfrom keras.regularizers import l2# 创建数据集X, y = make_moons(n_samples=100, noise=0.2, random_state=1)# 拆分数据集n_train = 30trainX, testX = X[:n_train, :], X[n_train:, :]trainy, testy = y[:n_train], y[n_train:]# 创建模型model = Sequential()# 设置权重正则化model.add(Dense(500, input_dim=2, activation=‘relu’, kernel_regularizer=l2(0.001)))model.add(Dense(1, activation=‘sigmoid’))model.compile(loss=‘binary_crossentropy’, optimizer=‘adam’, metrics=[‘accuracy’])# 训练模型model.fit(trainX, trainy, epochs=4000, verbose=0)# 评估模型, train_acc = model.evaluate(trainX, trainy, verbose=0)_, test_acc = model.evaluate(testX, testy, verbose=0)print(‘Train: %.3f, Test: %.3f’ % (train_acc, test_acc))乍一看,好像除了test准确率降低些,其它也没什么了。让我们画图看下train和test的训练过程。from sklearn.datasets import make_moonsfrom keras.layers import Densefrom keras.models import Sequentialfrom keras.regularizers import l2from matplotlib import pyplotX, y = make_moons(n_samples=100, noise=0.2, random_state=1)n_train = 30trainX, testX = X[:n_train, :], X[n_train:, :]trainy, testy = y[:n_train], y[n_train:]model = Sequential()model.add(Dense(500, input_dim=2, activation=‘relu’, kernel_regularizer=l2(0.001)))model.add(Dense(1, activation=‘sigmoid’))model.compile(loss=‘binary_crossentropy’, optimizer=‘adam’, metrics=[‘accuracy’])history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=4000, verbose=0pyplot.plot(history.history[‘acc’], label=‘train’)pyplot.plot(history.history[‘val_acc’], label=‘test’)pyplot.legend()pyplot.show()如图所示,现在就很清楚了,test与train一致。网格搜索正则化超参数当确定权重正则化可以改善模型的时候,这时你可以尝试不同的权重系数值。首先对0.0到0.1之间的一些数量级进行网格搜索,然后再找到一个级别后进行网格搜索,这是一个很好的做法。# 待测权重正则化值values = [1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6]all_train, all_test = list(), list()for param in values: … model.add(Dense(500, input_dim=2, activation=‘relu’, kernel_regularizer=l2(param))) … all_train.append(train_acc) all_test.append(test_acc)我们依然可以图形化展示训练过程:pyplot.semilogx(values, all_train, label=‘train’, marker=‘o’)pyplot.semilogx(values, all_test, label=‘test’, marker=‘o’)完整代码如下:from sklearn.datasets import make_moonsfrom keras.layers import Densefrom keras.models import Sequentialfrom keras.regularizers import l2from matplotlib import pyplot# 创建数据集X, y = make_moons(n_samples=100, noise=0.2, random_state=1)# 拆分数据集n_train = 30trainX, testX = X[:n_train, :], X[n_train:, :]trainy, testy = y[:n_train], y[n_train:]# 待测权重系数值values = [1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6]all_train, all_test = list(), list()for param in values: # 创建模型 model = Sequential() model.add(Dense(500, input_dim=2, activation=‘relu’, kernel_regularizer=l2(param))) model.add(Dense(1, activation=‘sigmoid’)) model.compile(loss=‘binary_crossentropy’, optimizer=‘adam’, metrics=[‘accuracy’]) # 训练模型 model.fit(trainX, trainy, epochs=4000, verbose=0) # 评估模型 _, train_acc = model.evaluate(trainX, trainy, verbose=0) _, test_acc = model.evaluate(testX, testy, verbose=0) print(‘Param: %f, Train: %.3f, Test: %.3f’ % (param, train_acc, test_acc)) all_train.append(train_acc) all_test.append(test_acc)# plot train and test meanspyplot.semilogx(values, all_train, label=‘train’, marker=‘o’)pyplot.semilogx(values, all_test, label=‘test’, marker=‘o’)pyplot.legend()pyplot.show()总结降低过拟合问题,我们一般需要从“数据”,“模型结构”,“模型参数”,“模型训练方法”等角度,采用的方法如下:数据增强:图像的平移,旋转,裁剪等;利用GAN生成新数据;利用机器翻译生成新数据。降低模型复杂度:减少网络层数,神经元个数;添加正则化项,如:L1,L2集成学习:神经网络,dropout避免过长时间训练模型,设置提前终止。 ...

March 4, 2019 · 2 min · jiezi

PyTorch可视化理解卷积神经网络

摘要: 神经网络工具像一个黑匣子,无法知道它的中间是如何处理的。本文使用图片加代码的形式讲解CNN网络,并对每层的输出进行可视化,便于初学者理解,可以动手实践下哦!如今,机器已经能够在理解、识别图像中的特征和对象等领域实现99%级别的准确率。生活中,我们每天都会运用到这一点,比如,智能手机拍照的时候能够识别脸部、在类似于谷歌搜图中搜索特定照片、从条形码扫描文本或扫描书籍等。造就机器能够获得在这些视觉方面取得优异性能可能是源于一种特定类型的神经网络——卷积神经网络(CNN)。如果你是一个深度学习爱好者,你可能早已听说过这种神经网络,并且可能已经使用一些深度学习框架比如caffe、TensorFlow、pytorch实现了一些图像分类器。然而,这仍然存在一个问题:数据是如何在人工神经网络传送以及计算机是如何从中学习的。为了从头开始获得清晰的视角,本文将通过对每一层进行可视化以深入理解卷积神经网络。卷积神经网络在学习卷积神经网络之前,首先要了解神经网络的工作原理。神经网络是模仿人类大脑来解决复杂问题并在给定数据中找到模式的一种方法。在过去几年中,这些神经网络算法已经超越了许多传统的机器学习和计算机视觉算法。“神经网络”是由几层或多层组成,不同层中具有多个神经元。每个神经网络都有一个输入和输出层,根据问题的复杂性增加隐藏层的个数。一旦将数据送入网络中,神经元就会学习并进行模式识别。一旦神经网络模型被训练好后,模型就能够预测测试数据。另一方面,CNN是一种特殊类型的神经网络,它在图像领域中表现得非常好。该网络是由YanLeCunn在1998年提出的,被应用于数字手写体识别任务中。其它应用领域包括语音识别、图像分割和文本处理等。在CNN被发明之前,多层感知机(MLP)被用于构建图像分类器。图像分类任务是指从多波段(彩色、黑白)光栅图像中提取信息类的任务。MLP需要更多的时间和空间来查找图片中的信息,因为每个输入元素都与下一层中的每个神经元连接。而CNN通过使用称为局部连接的概念避免这些,将每个神经元连接到输入矩阵的局部区域。这通过允许网络的不同部分专门处理诸如纹理或重复模式的高级特征来最小化参数的数量。下面通过比较说明上述这一点。比较MLP和CNN因为输入图像的大小为28x28=784(MNIST数据集),MLP的输入层神经元总数将为784。网络预测给定输入图像中的数字,输出数字范围是0-9。在输出层,一般返回的是类别分数,比如说给定输入是数字“3”的图像,那么在输出层中,相应的神经元“3”与其它神经元相比具有更高的类别分数。这里又会出现一个问题,模型需要包含多少个隐藏层,每层应该包含多少神经元?这些都是需要人为设置的,下面是一个构建MLP模型的例子:Num_classes = 10Model = Sequntial()Model.add(Dense(512, activation=’relu’, input_shape=(784, )))Model.add(Dropout(0.2))Model.add(Dense(512, activation=’relu’))Model.add(Dropout(0.2))Model.add(Dense(num_classes, activation=’softmax’))上面的代码片段是使用Keras框架实现(暂时忽略语法错误),该代码表明第一个隐藏层中有512个神经元,连接到维度为784的输入层。隐藏层后面加一个dropout层,丢弃比例设置为0.2,该操作在一定程度上克服过拟合的问题。之后再次添加第二个隐藏层,也具有512谷歌神经元,然后再添加一个dropout层。最后,使用包含10个类的输出层完成模型构建。其输出的向量中具有最大值的该类将是模型的预测结果。这种多层感知器的一个缺点是层与层之间完全连接,这导致模型需要花费更多的训练时间和参数空间。并且,MLP只接受向量作为输入。卷积使用稀疏连接的层,并且其输入可以是矩阵,优于MLP。输入特征连接到局部编码节点。在MLP中,每个节点都有能力影响整个网络。而CNN将图像分解为区域(像素的小局部区域),每个隐藏节点与输出层相关,输出层将接收的数据进行组合以查找相应的模式。计算机如何查看输入的图像?看着图片并解释其含义,这对于人类来说很简单的一件事情。我们生活在世界上,我们使用自己的主要感觉器官(即眼睛)拍摄环境快照,然后将其传递到视网膜。这一切看起来都很有趣。现在让我们想象一台计算机也在做同样的事情。在计算机中,使用一组位于0到255范围内的像素值来解释图像。计算机查看这些像素值并理解它们。乍一看,它并不知道图像中有什么物体,也不知道其颜色。它只能识别出像素值,图像对于计算机来说就相当于一组像素值。之后,通过分析像素值,它会慢慢了解图像是灰度图还是彩色图。灰度图只有一个通道,因为每个像素代表一种颜色的强度。0表示黑色,255表示白色,二者之间的值表明其它的不同等级的灰灰色。彩色图像有三个通道,红色、绿色和蓝色,它们分别代表3种颜色(三维矩阵)的强度,当三者的值同时变化时,它会产生大量颜色,类似于一个调色板。之后,计算机识别图像中物体的曲线和轮廓。。下面使用PyTorch加载数据集并在图像上应用过滤器:# Load the librariesimport torchimport numpy as npfrom torchvision import datasetsimport torchvision.transforms as transforms# Set the parametersnum_workers = 0batch_size = 20# Converting the Images to tensors using Transformstransform = transforms.ToTensor()train_data = datasets.MNIST(root=‘data’, train=True, download=True, transform=transform)test_data = datasets.MNIST(root=‘data’, train=False, download=True, transform=transform)# Loading the Datatrain_loader = torch.utils.data.DataLoader(train_data, batch_size=batch_size, num_workers=num_workers)test_loader = torch.utils.data.DataLoader(test_data, batch_size=batch_size, num_workers=num_workers)import matplotlib.pyplot as plt%matplotlib inlinedataiter = iter(train_loader)images, labels = dataiter.next()images = images.numpy()# Peeking into datasetfig = plt.figure(figsize=(25, 4))for image in np.arange(20): ax = fig.add_subplot(2, 20/2, image+1, xticks=[], yticks=[]) ax.imshow(np.squeeze(images[image]), cmap=‘gray’) ax.set_title(str(labels[image].item()))下面看看如何将单个图像输入神经网络中:img = np.squeeze(images[7])fig = plt.figure(figsize = (12,12)) ax = fig.add_subplot(111)ax.imshow(img, cmap=‘gray’)width, height = img.shapethresh = img.max()/2.5for x in range(width): for y in range(height): val = round(img[x][y],2) if img[x][y] !=0 else 0 ax.annotate(str(val), xy=(y,x), color=‘white’ if img[x][y]<thresh else ‘black’)上述代码将数字'3’图像分解为像素。在一组手写数字中,随机选择“3”。并且将实际像素值(0-255 )标准化,并将它们限制在0到1的范围内。归一化的操作能够加快模型训练收敛速度。构建过滤器过滤器,顾名思义,就是过滤信息。在使用CNN处理图像时,过滤像素信息。为什么需要过滤呢,计算机应该经历理解图像的学习过程,这与孩子学习过程非常相似,但学习时间会少的多。简而言之,它通过从头学习,然后从输入层传到输出层。因此,网络必须首先知道图像中的所有原始部分,即边缘、轮廓和其它低级特征。检测到这些低级特征之后,传递给后面更深的隐藏层,提取更高级、更抽象的特征。过滤器提供了一种提取用户需要的信息的方式,而不是盲目地传递数据,因为计算机不会理解图像的结构。在初始情况下,可以通过考虑特定过滤器来提取低级特征,这里的滤波器也是一组像素值,类似于图像。可以理解为连接卷积神经网络中的权重。这些权重或滤波器与输入相乘以得到中间图像,描绘了计算机对图像的部分理解。之后,这些中间层输出将与多个过滤器相乘以扩展其视图。然后提取到一些抽象的信息,比如人脸等。就“过滤”而言,我们有很多类型的过滤器。比如模糊滤镜、锐化滤镜、变亮、变暗、边缘检测等滤镜。下面用一些代码片段来理解过滤器的特征:Import matplotlib.pyplot as pltImport matplotib.image as mpimgImport cv2Import numpy as npImage = mpimg.imread(‘dog.jpg’)Plt.imshow(image)# 转换为灰度图gray = cv2.cvtColor(image, cv2.COLOR_RB2GRAY)# 定义sobel过滤器sobel = np.array([-1, -2, -1],[0, 0, 0],[1, 2, 1]))# 应用sobel过滤器Filtered_image = cv2.filter2D(gray, -1, sobel_y)# 画图Plt.imshow(filtered_image, cmp=’gray’)以上是应用sobel边缘检测滤镜后图像的样子, 可以看到检测出轮廓信息。完整的CNN结构到目前为止,已经看到了如何使用滤镜从图像中提取特征。现在要完成整个卷积神经网络,cnn使用的层是:1.卷积层(Convolutional layer)2.池层(Pooling layer)3.全连接层(fully connected layer)典型的cnn网络结构是由上述三类层构成:下面让我们看看每个图层起到的的作用:* 卷积层(CONV)——使用过滤器执行卷积操作。因为它扫描输入图像的尺寸。它的超参数包括滤波器大小,可以是2x2、3x3、4x4、5x5(或其它)和步长S。结果输出O称为特征映射或激活映射,具有使用输入层计算的所有特征和过滤器。下面描绘了应用卷积的工作过程:池化层(POOL)——用于特征的下采样,通常在卷积层之后应用。池化处理方式有多种类型,常见的是最大池化(max pooling)和平均池化(ave pooling),分别采用特征的最大值和平均值。下面描述了池化的工作过程: 全连接层(FC)——在展开的特征上进行操作,其中每个输入连接到所有的神经元,通常在网络末端用于将隐藏层连接到输出层,下图展示全连接层的工作过程:在PyTorch中可视化CNN在了解了CNN网络的全部构件后,现在让我们使用PyTorch框架实现CNN。步骤1:加载输入图像:import cv2import matplotlib.pyplot as plt%matplotlib inlineimg_path = ‘dog.jpg’bgr_img = cv2.imread(img_path)gray_img = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2GRAY)# Normalisegray_img = gray_img.astype(“float32”)/255plt.imshow(gray_img, cmap=‘gray’)plt.show()步骤2:可视化过滤器对过滤器进行可视化,以更好地了解将使用哪些过滤器:import numpy as npfilter_vals = np.array([ [-1, -1, 1, 1], [-1, -1, 1, 1], [-1, -1, 1, 1], [-1, -1, 1, 1]])print(‘Filter shape: ‘, filter_vals.shape)# Defining the Filtersfilter_1 = filter_valsfilter_2 = -filter_1filter_3 = filter_1.Tfilter_4 = -filter_3filters = np.array([filter_1, filter_2, filter_3, filter_4])# Check the Filtersfig = plt.figure(figsize=(10, 5))for i in range(4): ax = fig.add_subplot(1, 4, i+1, xticks=[], yticks=[]) ax.imshow(filters[i], cmap=‘gray’) ax.set_title(‘Filter %s’ % str(i+1)) width, height = filters[i].shape for x in range(width): for y in range(height): ax.annotate(str(filters[i][x][y]), xy=(y,x), color=‘white’ if filters[i][x][y]<0 else ‘black’)步骤3:定义CNN模型本文构建的CNN模型具有卷积层和最大池层,并且使用上述过滤器初始化权重:import torchimport torch.nn as nnimport torch.nn.functional as Fclass Net(nn.Module): def init(self, weight): super(Net, self).init() # initializes the weights of the convolutional layer to be the weights of the 4 defined filters k_height, k_width = weight.shape[2:] # assumes there are 4 grayscale filters self.conv = nn.Conv2d(1, 4, kernel_size=(k_height, k_width), bias=False) # initializes the weights of the convolutional layer self.conv.weight = torch.nn.Parameter(weight) # define a pooling layer self.pool = nn.MaxPool2d(2, 2) def forward(self, x): # calculates the output of a convolutional layer # pre- and post-activation conv_x = self.conv(x) activated_x = F.relu(conv_x) # applies pooling layer pooled_x = self.pool(activated_x) # returns all layers return conv_x, activated_x, pooled_x# instantiate the model and set the weightsweight = torch.from_numpy(filters).unsqueeze(1).type(torch.FloatTensor)model = Net(weight)# print out the layer in the networkprint(model)Net((conv): Conv2d(1, 4, kernel_size=(4, 4), stride=(1, 1), bias=False)(pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False))步骤4:可视化过滤器快速浏览一下所使用的过滤器def viz_layer(layer, n_filters= 4): fig = plt.figure(figsize=(20, 20)) for i in range(n_filters): ax = fig.add_subplot(1, n_filters, i+1) ax.imshow(np.squeeze(layer[0,i].data.numpy()), cmap=‘gray’) ax.set_title(‘Output %s’ % str(i+1))fig = plt.figure(figsize=(12, 6))fig.subplots_adjust(left=0, right=1.5, bottom=0.8, top=1, hspace=0.05, wspace=0.05)for i in range(4): ax = fig.add_subplot(1, 4, i+1, xticks=[], yticks=[]) ax.imshow(filters[i], cmap=‘gray’) ax.set_title(‘Filter %s’ % str(i+1))gray_img_tensor = torch.from_numpy(gray_img).unsqueeze(0).unsqueeze(1)步骤5:每层过滤器的输出在卷积层和池化层输出的图像如下所示:卷积层:池化层:可以看到不同层结构得到的效果会有所差别,正是由于不同层提取到的特征不同,在输出层集合到的特征才能很好地抽象出图像信息。本文作者:【方向】阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

January 21, 2019 · 3 min · jiezi

论文<Learning to Promote Saliency Detectors>阅读

Learning to Promote Saliency Detectorshttps://github.com/lartpang/M…缩写标注:SD: Saliency DetectionZSL: Zero-Shot Learning关键内容:没有训练直接将图像映射到标签中的DNN。相反,将DNN拟合为一个嵌入函数,以将像素和显著/背景区域的属性映射到度量空间。显着/背景区域的属性被映射为度量空间中的锚点。然后,在该空间中构造最近邻(NN)分类器,将最近的锚点的标签分配给对应的像素.保持分辨率的手段:移除了最后两个卷积块的池化层, 使用扩张卷积来维持卷积滤波器的感受野添加亚像素卷积层到每个VGG特征提取器的卷积块后, 来上采样每个卷积块的特征图到输入图像大小.使用了迭代训练/测试的策略.这里没有提到训练迭代次数如何确定测试的迭代次数是人工给定的一些想法:类似于R3Net, 最后的添加的结构都是反复迭代测试后才确定使用多少次, 而且按照相关的测试可以看出来, 一定次数后, 提升的效果就趋于饱和了, 只能说这里的提到的方法对于现有网络的提升具有一定的助益.对于这里提到的, 这是类似于一种ZSL的方法, 也就是利用现有的SD算法产生的结果(“过去的知识”), 添加的新结构, 不断利用过去的知识迭代, 实现对于最终"后处理"后结果的一个促进(“来对现有的SD算法进行推广”).一些疑惑:如何将这个方法应用到现有的架构呢? 如何改造现有架构?改造后的结构, 训练的时候也要按照文中那样, 随机翻转真值中的像素标签么?这里的第6层是哪里来的?是前面的5C通道的特征输出汇总来的?AbstractThe categories and appearance of salient objects varyfrom image to image, therefore, saliency detection is animage-specific task. Due to lack of large-scale saliency training data, using deep neural networks (DNNs) with pre-training is difficult to precisely capture the image-specific saliency cues. To solve this issue, we formulate a zero-shot learning problem to promote existing saliency detectors.Concretely, a DNN is trained as an embedding function to map pixels and the attributes of the salient/background regions of an image into the same metric space, in which an image-specific classifier is learned to classify the pixels.Since the image-specific task is performed by the classifier, the DNN embedding effectively plays the role of a general feature extractor.Compared with transferring the learning to a new recognition task using limited data, this formulation makes the DNN learn more effectively from small data.Extensive experiments on five data sets showthat our method significantly improves accuracy of existing methods and compares favorably against state-of-the-art approaches.显着对象的类别和外观因图像而异,因此,显着性检测是特定于图像的任务。由于缺乏大规模显着性训练数据,使用具有预训练的深度神经网络(DNN)难以精确捕获图像特定显着性线索。为了解决这个问题,我们制定了一个零次学习问题来推广现有的显着性检测器。具体地,DNN被训练为一个嵌入函数,以将像素和图像的显着/背景区域的属性映射到相同的度量空间,其中, 图像特定的分类器被学习来对像素进行分类。由于图像特定任务由分类器执行,因此DNN嵌入有效地扮演一般特征提取器的角色。与使用有限数据将学习转移到新的识别任务相比,该设定使DNN从小数据中更有效地学习。对五个数据集进行的大量实验表明,我们的方法显着提高了现有方法的准确性,并且与最先进的方法相比具有优势。这里提到了一点, 使用 ZSL 问题来推广现有的SD器. 怎么推广?补充内容 ZSL[零次学习(Zero-Shot Learning)]假设小暗(纯粹因为不想用小明)和爸爸,到了动物园,看到了马,然后爸爸告诉他,这就是马;之后,又看到了老虎,告诉他:“看,这种身上有条纹的动物就是老虎。”;最后,又带他去看了熊猫,对他说:“你看这熊猫是黑白色的。”然后,爸爸给小暗安排了一个任务,让他在动物园里找一种他从没见过的动物,叫斑马,并告诉了小暗有关于斑马的信息:“斑马有着马的轮廓,身上有像老虎一样的条纹,而且它像熊猫一样是黑白色的。”最后,小暗根据爸爸的提示,在动物园里找到了斑马(意料之中的结局。。。)。上述例子中包含了一个人类的推理过程,就是利用过去的知识(马,老虎,熊猫和斑马的描述),在脑海中推理出新对象的具体形态,从而能对新对象进行辨认。ZSL就是希望能够模仿人类的这个推理过程,使得计算机具有识别新事物的能力。结合起来看, 也就是说可以利用过去的知识, 来对现有的SD器进行推广.这个过程是什么样的呢?Introduction传统的显着性检测方法通常是利用低级别的特征和启发式先验,它们不能在复杂的场景中发现显着的对象,也就不能够捕获语义对象。随着DNN的流行, 可以学习来自训练样本的更为高层的语义特征, 因此对于定位语义显著性区域更为有效, 在复杂场景下也会更为有效.使用DNN就要考虑一个问题, 数据. DNN通常在大量数据的基础上来训练, 而SD的数据是比较有限的, 这个问题通常用在其他任务的大数据集(如分类任务)上预训练的手段来解决, 然而这很容易导致其他问题:从图上可以看出来, 分类任务训练出来的模型, 最后的特征图中关注的重点对于SD任务而言, 在两列的特征图中不加区分地突出显示符号和人的区域。哪个图"不加区分地突出显示符号和人的区域"?预训练任务与SD的差异: 由预先训练的特征提取器产生的特征假定用于所有图像。例如,标志和人是图1第一列中的显着对象,而它们属于第二列中的背景。然而,在两列的特征图中不加区分地突出了标志和人的区域。使用这种特征提取器,可以强制预测模型学习将相似特征映射到相反标签,这对于小训练数据集来说是困难的。显着对象的类别和外观因图像而异,而小的训练数据不足以捕捉多样性. 例如,图1中所示的六个显着对象来自六个不同的类别,并且它们的外观差别很大。因此,可能很难学习统一的检测器来处理各种显着对象。考虑到显着对象的多样性,我们没有训练直接将图像映射到标签中的深度神经网络(DNN)。相反,我们将DNN训练为一个嵌入函数,以将像素和显著/背景区域的属性映射到度量空间。显着/背景区域的属性被映射为度量空间中的锚点。然后,在该空间中构造最近邻(NN)分类器,将最近的锚点的标签分配给对应的像素。作为非参数模型,NN分类器可以很好地适应新数据并处理显着对象的多样性。另外,由于分类任务是由NN分类器执行的,因此DNN的目标转向学习从显着/背景区域的属性到嵌入空间中的锚点的一般映射。与直接学习检测不同的显着对象相比,网络更容易学习有限的数据.补充内容: 嵌入(Embedding)嵌入意味着将数据转换为特征表示,其中某些属性可以用距离的概念表示。例如,针对用于说话者识别的语音信号训练的模型可以允许您将语音片段转换为数字向量,使得来自相同说话者的另一片段与原始向量具有小的距离(例如,欧几里德距离)。或者,不同的嵌入函数可能允许您根据信号中所说的单词转换语音信号。因此,如果在这些片段中使用相同的单词,则在两个语音信号的编码表示之间将获得小的欧几里德距离。另外, 你可能只想学习一种嵌入,它代表语音信号的“情绪”,例如:“快乐”与“悲伤”与“愤怒”等。两个语音信号的编码表示之间的小距离将意味着相似的情绪,反之亦然。或者例如,word2vec embeddings, 在一个空间中“映射”一个单词,其中这些单词之间的欧几里德距离代表语义相似性(同样, embedding ~ 为您提供给定单词的数字向量)。因此,如果你使用“德国”的word2vec表示,从中减去“柏林”,并将结果添加到“法国”,你会得到一个矢量, 在欧几里德空间非常接近“巴黎”的嵌入(the embedding for “Paris”)!同样,在需要分类成数十万或数百万个类的应用程序中,例如面部识别,一种常见的方法是使用“度量学习”技术(通常是具有所谓的对比或三重丢失的连体CNN(Siamese CNNs with so-called contrastive or triplet loss)),这在测试时允许您在面部的矢量表示上使用最近邻技术!另一个介绍:Embedding在数学上表示一个maping, f: X -> Y, 也就是一个function,其中该函数是injective(就是我们所说的单射函数,每个Y只有唯一的X对应,反之亦然)和structure-preserving (结构保存,比如在X所属的空间上X1 < X2,那么映射后在Y所属空间上同理 Y1 < Y2)。那么对于word embedding,就是将单词word映射到另外一个空间,其中这个映射具有injective和structure-preserving的特点。具体地说,我们在图2中显示了我们提出的方法的流程。在训练期间,DNN具有真实的显着区域和背景区域,其中几个随机选择的像素的标签被翻转,以产生锚点。NN分类器的输出构成了saliency map。DNN可以通过此显着性图和真值之间的损失进行端到端的监督训练。当在图像上进行测试时,每个图像的显着性图像按照训练的流程来获得,使用的是现有方法来检测的近似的显著性/背景区域。尽管近似显着/背景区域不完全正确,但是它通常与真实的显著性/背景区域有着相似的属性。因此,对应的嵌入向量(即锚点)将接近真实的显着/背景区域的向量。此外,为了产生更好的结果,我们提出了一个迭代测试的方案。NN分类器的结果用于修改锚点,从而产生越来越精确的结果。迭代测试的想法, 如何实现? 在测试的时候, 你的迭代只能使用测试集的图片数据和你预测出来的结果, 不可以在真实值上进行迭代, 那这里的迭代如何片定效果, 如何学习? 这里的进一步迭代测试的操作就是一种后处理手段, 但是如何迭代那? 何时终止迭代?这里的方法可以看作是一个ZSL问题,其中由现有方法检测到的近似显着/背景区域为未看到的显著性对象提供属性,并且模型从训练数据中学习去从属性中学习一个特定于图像的分类器来分类此图像的像素。对五个数据集的大量实验表明,该方法可以显着提高现有方法的准确性,并且与现有技术方法相比具有优势。Related worksGenerally, saliency detection methods can be categorized into two streams: top-down and bottom-up saliency. Since our work addresses bottom-up saliency, here we mainly review recent works on bottom-up saliency, meanwhile shortly mention top-down saliency. We also explore the relation between our proposed method and top-down saliency.BUBottom-up (BU) saliency is stimuli-driven(刺激驱动), where saliency is derived from contrast among visual stimuli(视觉刺激).Conventional bottom-up saliency detection methods often utilize low-level features and heuristic priors(启发式).Jiang et al. [12] formulate saliency detection via an absorbing Markov chain(吸收马尔可夫链http://www.vartang.com/2013/0…) on an image graph model, where saliency of each region is defined as its absorbed time from boundary nodes.Yang et al. [32] rank the similarity of the image regions with foreground cues or background cues via graph-based manifold ranking(通过基于图的流形排序对图像区域与前景线索或背景线索的相似性进行排序).Since the conventional methods are not robust in complex scenes neither capable of capturing semantic objects, deep neural networks (DNNs) are introduced to overcome these drawbacks.Li et al. [16] train CNNs with fully connected layers to predict saliency value of each superpixel, and to enhance the spatial coherence(空间连贯性) of their saliency results using a refinement method.Li et al. [18] propose a FCN trained under the multi-task learning framework for saliency detection.Zhang et al. [34] present a generic framework to aggregate multi-level convolutional features for saliency detection.Although the proposed method is also based on DNNs, the main difference between ours and these methods is that they learn a general model that directly maps images to labels, while our method learns a general embedding function as well as an image-specific NN classifier.TDTop-down (TD) saliency aims at finding salient regions specified by a task, and is usually formulated as a supervised learning problem.Yang and Yang [33] propose a supervised top-down saliency model that jointly learns a Conditional Random Field (CRF) and a discriminative dictionary.Gao et al. [9] introduced a top-down saliency algorithm by selecting discriminant features from a pre-defined filter bank(预定义的过滤器库).TD+BUIntegration of TD and BU saliency has been exploited by some methods.Borji [3] combines low-level features and saliency maps of previous bottom-up models with top-down cognitive visual features to predict fixations.Tong et al. [26] proposed a top-down learning approach where the algorithm is bootstrapped with training samples generated using a bottom-up model(该算法使用自下而上模型生成的训练样本进行引导) to exploit the strengths of both bottom-up contrast-based saliency models and top-down learning methods.Our method also can be viewed as an integration of TD and BU saliency. Although both our method and the method of Tonget al. [26] formulate the problem as top-down saliency detection specified by initial saliency maps, there are certain difference between the two.First, Tong’s method trains a strong model via boostrap learning(引导学习) with training samples generated by a weak model. In contrast, our method maps pixels and the approximate salient/background regions into a learned metric space, which is related to zero-shot learning.Second, thanks to deep learning, our method is capable of capturing semantically salient regions and does well on complex scenes, while Tong’s method uses hand-crafted features and heuristic priors, which are less robust.Third, our method produces pixel-level results, while Tong’s method computes saliency value of each image region to assemble a saliency map, which tends to be coarser.The Proposed MethodOur method consists of three components:a DNN as an embedding function i.e. the anchor network, that maps pixels and regions of the input image into a learned metric spacea nearest neighbor (NN) classifier in the embedding space learned specifically for this image to classify its pixelsan iterative testing scheme that utilizes the result of the NN classifier to revise anchors(修改锚点), yielding increasingly more accurate results.The anchor network这部分主要是进行了一个映射的操作. 一个是映射图像中的像素点, 一个是映射图像中的显著性/背景区域.像素点通过一个DNN建模的嵌入函数, 来映射到一个D维度量空间的向量上.图像中的显著性/背景区域也同样被DNN映射到了D维度量空间中的向量上, 也就是这里提到的锚点(anchors).We assume that in the embedding space, all pixels of an image cluster around the corresponding anchors of this image. Then a nearest neighbor classifier can be built specifically for this image by classifying each pixel according to its nearest anchor.我们假设在嵌入空间中,图像的所有像素都聚集在这个图像的相应锚点周围。然后,通过根据最近的锚对每个像素进行分类,可以为该图像特定地构建最近邻分类器.感觉可以这样理解: 因为并不知道哪个维度上可以看作是所谓的"embedding space", 所以目的就是去近似逼近这样一个空间的表示. 这也算是深度网络的一个拟合作用的体现, 告诉他应该存在这样一个维度的空间, 是可以满足这样的要求, 给它限制, 让它训练学习, 逐步逼近拟合到这样一个结果上.为了做出这样的约束, 给定一个概率, 也就是对应的像素x属于显著性/背景区域C的条件概率, 由该测度空间上两个向量的距离d对应的softmax函数表示得到:这里的目的是要不断的提升属于对应区域的像素被判定归属于该区域的概率, 这里使用梯度上升法:这里的t是一个指示变量, 类似与指示函数的作用, 等于1的时候, 表示该像素属于实际属于区域1, 也就是显著性区域, 等于0表示属于区域2, 也就是背景区域.但是这里要注意, 对于锚点而言, 是使用真实标注来转化生成的, 所以在测试的时候, 就得考虑如何处理了. 这里使用的是利用生成的显著性图来进行生成anchors, 这是不准确的.为了匹配训练和测试条件, 但是又不能改动测试的真值, 只能对训练的流程进行改进. 在训练期间, 当生成anchors时, 以概率p随机翻转每一个像素的标签. 这种处理额外带来的一个好处是, 一定程度上增加了训练样本的多样性, 有一定的抗过拟合的作用.下面是算法的流程训练需要迭代, 迭代的过程, 就是不断的随机翻转标签, 计算像素和区域锚点的映射向量, 最大化对数似然, 更新参数. 注意上图中真值使用的方法.Iterative testing scheme测试期间生成锚点是个需要注意的问题, 因为真值认为是未知的, 这里就尝试根据 现有方法生成的显著性图(先验) 选择的近似的显著性/背景区域来生成锚点(要注意, 本文提出的实际上是一个后处理的方法, 目标是对于现有架构的进一步提升). 这里使用了一个迭代的测试方案使用NN分类器, 来逐渐调整锚点,.在这个迭代的过程中, 会使用到一个随之不断迭代的先验显著性图(初始的先验显著性图$Y^{(0)}_m$是由现有的方法生成的), 流程中使用的先验显著性图, 是通过这个公式迭代计算的:这里的Y就是迭代中使用的先验显著性图, 被用来选择下一次迭代中的显著性和背景区域. 而且随着迭代不断地开始, 新的结果所占的比例越来越低, 这将确保迭代过程的稳定性.上图展示了提出的方法对于显著性图的不断的优化提升的过程.这个过程实际上就是先对像素映射向量, 然后开始迭代, 最开始的先验使用的是现有方法生成的显著性图, 也就是$Y^{0}$, 利用先验图选择前景背景, 也就是下图中的位置, 进而生成近似的锚点(利用先验图作为类似训练时的真值, 与从网络中提取出来的特征层相乘(实际上是选择了前景与背景区域之后)在送入区域嵌入结构生成锚点).根据公式3, 对每个像素计算其显著性值(也就是属于显著性区域的概率值), 来构建另一个新的显著性图, 利用迭代公式进行迭代计算(相当于是d位置与i位置显著图的合并). 之后按照上图那样送入结构中.要注意, 这里的迭代次数T是给定的.(这就有点不智能了)尽管初始的显著性图没能准确的区分出来前景和背景, 但是它通常能部分的区分它们, 因此可以提供关于图像中显著性目标的类别和外观的信息.例如,在图3的第一张图片中,尽管只有一小部分前景被突出显示,但初始显著性图可以告诉我们前景可能是一只大猩猩,而背景则包含一片绿色。(这应该算是合理推测)然后,其选定的前景/背景区域应该与真实的前景/背景区域相似,从而导致相应的锚点与学习到的度量空间中的真实区域相关联。因此,方程3给出的最近邻分类可以产生良好的结果。随着迭代的进展,近似锚点逐渐接近真实锚点,这将导致更好的结果。这反过来可以提供对锚点的越来越精确的近似,并且产生更准确的结果。如图3所示,初始显著性图并不吸引人,而迭代修改后的图看起来要好得多。对于数据较为有限的SD任务, 选择在尝试调整在分类数据集预训练的模型(这里使用ImageNet上预训练过的VGG16), 来拟合为像素嵌入函数和区域嵌入函数, 以实现数据的映射. 由于不再是分类器, 去掉全连接, 只保留特征提取组件, 包含五个卷积块. 并且实际中也使用了BN和ReLU操作.Pixel embedding由于VGG特征提取器本身会导致特征图不断地缩小, 这并不是想要的结果, 因为这里提出的方法是要实现对于输入图像的每个像素来映射一个向量, 需要这个嵌入CNN产生一个具有与输入图像相同分辨率的特征图.这里采用了两个策略来获取更大的特征图.移除了最后两个卷积块的池化层, 使用扩张卷积来维持卷积滤波器的感受野添加亚像素卷积层到每个VGG特征提取器的卷积块后, 来上采样每个卷积块的特征图到输入图像大小.要注意, 这里实际上最后剩下来的只有两个最大池化, 可见代码:self.proc_feats_list = nn.ModuleList([ # convtranspose2d=>out_size=(in_size-1)xstride-2xpadding+kernel_size nn.Sequential( # x4 512卷积块对应的输出 nn.ConvTranspose2d(dims[0], dims[0], 8, 4, 2), nn.Conv2d(dims[0], odims[0], kernel_size=3, padding=1)), nn.Sequential( # x4 512对应的输出 nn.ConvTranspose2d(dims[1], dims[1], 8, 4, 2), nn.Conv2d(dims[1], odims[1], kernel_size=3, padding=1)), nn.Sequential( # x4 256对应的输出 nn.ConvTranspose2d(dims[2], dims[2], 8, 4, 2), nn.Conv2d(dims[2], odims[2], kernel_size=3, padding=1)), nn.Sequential( # x2 128对应的输出 nn.ConvTranspose2d(dims[3], dims[3], 4, 2, 1), nn.Conv2d(dims[3], odims[3], kernel_size=3, padding=1)), # 使用亚像素卷积实现上采样 ############################################# # 不清楚这里为什么放弃了使用亚像素卷积的手段 # 这里的nn.PixelShuffle(up_scale)便是可以用来实现亚像素卷积的一个类 # nn.Sequential( # nn.Conv2d(dims[0], odims[0], kernel_size=3, padding=1), # nn.PixelShuffle(4)), # nn.Sequential( # nn.Conv2d(dims[1], odims[1], kernel_size=3, padding=1), # nn.PixelShuffle(4)), # nn.Sequential( # nn.Conv2d(dims[2], odims[2], kernel_size=3, padding=1), # nn.PixelShuffle(4)), # nn.Sequential( # nn.Conv2d(dims[3], odims[3], kernel_size=3, padding=1), # nn.PixelShuffle(2)), # x1 64 对应的输出 nn.Conv2d(dims[4], dims[4], kernel_size=3, padding=1),])Subpixel convolution is an upsampling strategy originally proposed in [Real-time single im-age and video super-resolution using an efficient sub-pixelconvolutional neural network] for image super-resolution.这里也提示了一点, 对于深度学习实现的显著性检测手段, 里面会涉及到分辨率的恢复操作, 也就是所谓的上采样, 由于网络的深层事实上需要更大的感受野, 所以说, 对于往常的池化层不能轻易去除, 而要找到合适的替代, 这里提供了几个思路, 一个是使用扩张卷积替代池化操作, 一个是使用超分辨率重建(本质都是低分辨率重建为高分辨率)的思想.为了生成C通道的N倍输入大小的tensor, 亚像素卷积首先通过一般的卷积得到一个 $N^2 \times C$ 通道的与原输入一致大小的tensor. 然后这个tensor被重新调整为C通道的N倍于输入大小的tensor.本小节补充内容:超分辨率技术(Super-Resolution)是指从观测到的低分辨率图像重建出相应的高分辨率图像,在监控设备、卫星图像和医学影像等领域都有重要的应用价值。SR可分为两类:从多张低分辨率图像重建出高分辨率图像和从单张低分辨率图像重建出高分辨率图像。基于深度学习的SR,主要是基于单张低分辨率的重建方法,即Single Image Super-Resolution (SISR)。如果做SR(超分辨率)的话,需要将一张低分辨率图像转换成一张高分辨率图像。如果直接用deconvolution作为upscale手段的话,通常会带入过多人工因素进来(有不少论文提到这个)。而sub-pixel conv会大大降低这个风险。Subpixel convolution是一种巧妙的图像及特征图upscale的方法,又叫做pixel shuffle(像素洗牌), 亚像素卷积操作就是通过生成更多的特征层, 然后利用它们数据的周期性组合, 来实现分辨率的提升.上图很直观得表达了sub-pixel convolution的做法,前面就是一个普通的CNN网络,到后面彩色部分就是sub-pixel conv的操作了。首先,如果我想对原图放大3倍,那么我需要生成出3^2=9个same size的特征图。将九个same size的特征图拼成一个X3的大图,这就是sub-pixel convolution的操作了。这是一种抽样的反思想,如果把一张x3的大图,每隔三个点抽样一个,那就会得到9张低分辨率的图像。于是,如果我们可以通过CNN来获得9张符合分布的低分辨率图像,那么就可以组成一张高分辨率的大图。通过在五个卷积块后添加一个亚像素卷积层, 这里会获取5个C通道的特征图, 5个特征图级联, 想成一个5C通道的特征图. 但是直接使用和这个级联的特征图不是最好的选择, 因为不同的卷积块之间有着不同的感受野(区域不同), 为了解决这个问题, 额外添加了两个卷积层来转化这个级联特征图为一个D通道的特征图(特征融合), 其中的每个D维向量就被认为是每个像素所对应的的D维表示.文中的实现里, 设定C为64, D为512.Region embedding为了简单, 对于两个嵌入操作(像素和区域), 共享相同的特征提取器和亚像素上采样层.新的层被添加在亚像素卷积层后, 来匹配图片区域的5C通道特征图到一个D维向量.这里考虑了两种结构:基于卷积的区域嵌入结构: 5C-channel->Convs->D-channel->channel-wise-average->D-vector基于全连接的区域嵌入结构 5C-channel->channel-wise-average->5C-vevtor->FC->D-vectorExperiments评价标准PR曲线F测度曲线F测度得分MAE得分The precision of a binary map is defined as the ratio of the number of salient pixels it correctly labels, to all salient pixels in this binary map. 预测的二值图像中的预测的显著性目标区域标注正确的比例. 就是预测为真值(预测为显著性区域)中的实际真值(实际的显著性区域)的比例.The recall value is the ratio ofthe number of correctly labeled salient pixels to all salient pixels in the ground-truth map. 实际真值中被正确标出的比例.in which TS denotes true salient pixels, DS denotes detected salient pixels by the binary map, and $|·|$ denotes cardinality of a set.F测度使用下式计算.曲线的计算按照动态阈值来计算.Given a saliency map whose intensities are in the rangeof 0 and 1, a series of binary maps can be produced by thresholding the saliency map with different values in [0,1].Precision and recall values of these binary maps can becomputed according to Eqn. 6. F-measure can be computed according to Eqn. 7.Plotting the (precision, recall) pairs of all the binary maps results in the precision-recall curve, and plotting the (F-measure, threshold) pairs results in theF-measure curve.Also as suggested in [1], we use twice the mean valueof the saliency maps as the threshold to generate binary maps for computing the F-measure.Notice that some works have reported slightly different F-measures using different thresholds. But as far as we know, twice the mean value isthe most commonly used threshold.As complementary to PR curves, mean absolute error(MAE) is used to quantitatively measure the average difference between the saliency map S and the ground truth map G.MAE indicates how similar a saliency map is compared to the ground truth. It is widely used in different pixel-level prediction tasks such as semantic segmentation and image cropping [22].Implementation detailsWe train our model on the training set of DUTS dataset.As in [20], we augment the training data by horizontal flipping and cropping the images to reduce overfitting.The probability $p$ of randomly flipping ground truth when producing anchors during training is set to 0.05.We comparetwo type of region embedding in Sec.4.4, and adopt theConv-based one in other experiments.Adam [14] optimization method is used for training our model.Learning rateis set to 1e-3.We do not use a validation set, and train our model until its training loss converges.The training process takes almost 16 hours and converges after around 300 kiterations with mini-batch of size 1.From this comparison we can see that the performance of FC-based and Conv-based region embedding is comparable.The FC-based region embedding yields relatively larger F-measureConv-based region embedding is more superior in terms of MAEWe show the effect of the proposed iterative approximation scheme in Figure 5. As shown in Figure 5, the first iteration improve the F-measure and decrease MAE most significantly. The improvement slows down with iterations, and saturates(饱和) gradually.PerformanceWe apply our method to promote the performance of each baseline method, by using its predicted saliency maps to generate initial anchors in Eqn.3.Figure 6 shows the PR curves of the baseline methods and the one promoted by our method.Table 2 shows the F-measure and MAE scores of 8 deep learning based methods and the corresponding promoted results.The quantified improvements in F-measure and MAE of applying our method to conventional methods are shown in Table 3.As shown in Figure 6, Table 2, and Table 3, our method drastically(大幅) promotes all the baseline methods.观察数据可知论文提出的方法, 对于已有的深度方法和传统的方法都有提升.Notice that the results shown here are obtained by iterating Alg. 2 only once for fast testing speed.As shown in Sec.4.4, better results can be achieved through iterating Alg. 2 more times.Figure 7 shows a visual comparison of saliency maps produced by some state-of-the-art methods and the promoted ones by our method.It can be seen that the saliency maps produced by our methods highlight salient regions that are missed by the baselines.Further, our method can suppress the background regions that are wrongly labeled as salient by the baseline methods(我们的方法可以抑制基线方法错误标记为显着的背景区域。).总结In this paper, we propose a novel learning method to promote existing salient object detection methods. Extensive experiments on five benchmark datasets show that our method can significantly improve accuracy of existing methods and compares favorably against state-of-the-arts(我们的方法可以显着提高现有方法的准确性,并且与现有最优技术相比具有优势).参考链接零次学习入门: https://zhuanlan.zhihu.com/p/...What is embedding | embedded space | feature embedding in deep neural architectures?: https://www.quora.com/What-is…有谁可以解释下word embedding? - 寒蝉鸣泣的回答 - 知乎: https://www.zhihu.com/questio...Sub-pixel Convolution(子像素卷积): https://blog.csdn.net/leviopk… ...

January 10, 2019 · 9 min · jiezi

目标检测算法图解:一文看懂RCNN系列算法

摘要: 本文简要介绍图像检测中常用的深度学习方法——RCNN家族系列算法,以图像讲解形式,便于理解。在生活中,经常会遇到这样的一种情况,上班要出门的时候,突然找不到一件东西了,比如钥匙、手机或者手表等。这个时候一般在房间翻一遍各个角落来寻找不见的物品,最后突然一拍大脑,想到在某一个地方,在整个过程中有时候是很着急的,并且越着急越找不到,真是令人沮丧。但是,如果一个简单的计算机算法可以在几毫秒内就找到你要找的物品,你的感受如何?是不是很惊奇!这就是对象检测算法(object detection)的力量。虽然上述举的生活例子只是一个很简单的例子,但对象检测的应用范围很广,跨越多个不同的行业,从全天候监控到智能城市的实时车辆检测等。简而言之,物体检测是强大的深度学习算法中的一个分支。在本文中,我们将深入探讨可以用于对象检测的各种算法。首先从属于RCNN系列算法开始,即RCNN、 Fast RCNN和 Faster RCNN。在之后的文章中,将介绍更多高级算法,如YOLO、SSD等。1.解决对象检测任务的简单方法(使用深度学习)下图说明了对象检测算法是如何工作。图像中的每个对象,从人到风筝都以一定的精度进行了定位和识别。下面从最简单的深度学习方法开始,一种广泛用于检测图像中的方法——卷积神经网络(CNN)。如果读者对CNN算法有点生疏,建议阅读此文。这里仅简要总结一下CNN的内部运作方式:首先将图像作为输入传递到网络,然后通过各种卷积和池化层处理,最后以对象类别的形式获得输出。对于每个输入图像,会得到一个相应的类别作为输出。因此可以使用这种技术来检测图像中的各种对象。1.首先,将图像作为输入;2.然后,将图像分成不同的区域;3.然后,将每个区域视为单独的图像;4.将所有这些区域传递给CNN并将它们分类为各种类别;5.一旦将每个区域划分为相应的类后,就可以组合所有这些区域来获取具有检测到的对象的原始图像:使用这种方法会面临的问题在于,图像中的对象可以具有不同的宽高比和空间位置。例如,在某些情况下,对象可能覆盖了大部分图像,而在其他情况下,对象可能只覆盖图像的一小部分,并且对象的形状也可能不同。基于此,需要划分大量的区域,这会花费大量的计算时间。因此,为了解决这个问题并减少区域数量,可以使用基于区域的CNN,它使用提议方法选择区域。2.基于区域的卷积神经网络2.1 RCNN的思想RCNN算法不是在大量区域上工作,而是在图像中提出了一堆方框,并检查这些方框中是否包含任何对象。RCNN 使用选择性搜索从图像中提取这些框。下面介绍选择性搜索以及它如何识别不同的区域。基本上四个区域形成一个对象:不同的比例、颜色、纹理和形状。选择性搜索在图像中识别这些模式,并基于此提出各种区域。以下是选择性搜索如何工作的简要概述:首先, 将图像作为输入:然后,它生成初始子分段,以便获得多个区域:之后,该技术组合相似区域以形成更大的区域(基于颜色相似性、纹理相似性、尺寸相似性和形状兼容性):最后,这些区域产生最终的对象位置(感兴趣的区域);下面是RCNN检测对象所遵循的步骤的简要总结:1.首先采用预先训练的卷积神经网络;2.重新训练该模型模型——根据需要检测的类别数量来训练网络的最后一层(迁移学习);3.第三步是获取每个图像的感兴趣区域。然后,对这些区域调整尺寸,以便其可以匹配CNN输入大小;4.获取区域后,使用SVM算法对对象和背景进行分类。对于每个类,都训练一个二分类SVM;最后,训练线性回归模型,为图像中每个识别出的对象生成更严格的边界框;[对上述步骤进行图解分析](http://www.robots.ox.ac.uk/~tvg/publications/talks/Fast-rcnn-slides.pdf):首先,将图像作为输入:然后,使用一些提议方法获得感兴趣区域(ROI)(例如,选择性搜索):之后,对所有这些区域调整尺寸,并将每个区域传递给卷积神经网络:然后,CNN为每个区域提取特征,SVM用于将这些区域划分为不同的类别:最后,边界框回归(Bbox reg)用于预测每个已识别区域的边界框:以上就是RCNN检测物体的全部流程。2.2 RCNN的问题从上节内容可以了解到RCNN是如何进行对象检测的,但这种技术有其自身的局限性。以下原因使得训练RCNN模型既昂贵又缓慢:基于选择性搜索算法为每个图像提取2,000个候选区域;使用CNN为每个图像区域提取特征;RCNN整个物体检测过程用到三种模型:CNN模型用于特征提取;线性svm分类器用于识别对象的的类别;回归模型用于收紧边界框;这些过程相结合使得RCNN非常慢,对每个新图像进行预测需要大约40-50秒,这实际上使得模型在面对巨大的数据集时变得复杂且几乎不可能应用。好消息是存在另一种物体检测技术,它解决了RCNN中大部分问题。3.了解Fast RCNN3.1Fast RCNN的思想RCNN的提出者Ross Girshick提出了这样的想法,即每个图像只运行一次CNN,然后找到一种在2,000个区域内共享该计算的方法。在Fast RCNN中,将输入图像馈送到CNN,CNN生成卷积特征映射。使用这些特征图提取候选区域。然后,使用RoI池化层将所有建议的区域重新整形为固定大小,以便将其馈送到全连接网络中。下面将其分解为简化概念的步骤:1.首先将图像作为输入;2.将图像传递给卷积神经网络,生成感兴趣的区域;3.在所有的感兴趣的区域上应用RoI池化层,并调整区域的尺寸。然后,每个区域被传递到全连接层的网络中;4.softmax层用于全连接网以输出类别。与softmax层一起,也并行使用线性回归层,以输出预测类的边界框坐标。因此,Fast RCNN算法中没有使用三个不同的模型,而使用单个模型从区域中提取特征,将它们分成不同的类,并同时返回所标识类的边界框。对上述过程进行可视化讲解:将图像作为输入:将图像传递给卷积神经网络t,后者相应地返回感兴趣的区域:然后,在提取的感兴趣区域上应用RoI池层,以确保所有区域具有相同的大小:最后,这些区域被传递到一个全连接网络,对其进行分类,并同时使用softmax和线性回归层返回边界框:上述过程说明了Fast RCNN是如何解决RCNN的两个主要问题,即将每个图像中的1个而不是2,000个区域传递给卷积神经网络,并使用一个模型来实现提取特征、分类和生成边界框。3.2Fast RCNN的问题Fast RCNN也存在一定的问题,它仍然使用选择性搜索作为查找感兴趣区域的提议方法,这是一个缓慢且耗时的过程,每个图像检测对象大约需要2秒钟。因此,又开发了另一种物体检测算法——Faster RCNN。4.了解Faster RCNN4.1. Faster RCNN的思想Faster RCNN是Fast RCNN的修改版本,二者之间的主要区别在于,Fast RCNN使用选择性搜索来生成感兴趣区域,而Faster RCNN使用“区域提议网络”,即RPN。RPN将图像特征映射作为输入,并生成一组提议对象,每个对象提议都以对象分数作为输出。以下步骤通常采用Faster RCNN方法:1.将图像作为输入并将其传递给卷积神经网络,后者返回该图像的特征图;2.在这些特征图上应用RPN,返回提议对象及其分数;3.在这些提议对象上应用RoI池层,以将所有提案降低到相同的大小;4.最后,将提议传递到全连接层,该层在其顶部具有softmax层和线性回归层,以对对象的边界框进行分类和输出;这里简要解释一下RPN是如何运作的:首先,Faster RCNN从CNN获取特征图并将它们传递到区域提议网络。RPN在这些特征图上使用滑动窗口,每个窗口生成不同形状和大小的k个方框( Anchor boxe):方框是固定尺寸的边界箱,具有不同的形状和尺寸。对于每个方框,RPN预测两件事:预测锚是对象的概率;用于边界框回归器调整锚点以更好地适合物体的形状;在有了不同形状和大小的边界框后,将其传递到RoI池层。对每个提案并对其进行裁剪,以便每个提案都包含一个对象。这就是RoI池层所做的事情,它为每个方框提取固定大小的特征图:然后将这些特征图传递到全连接层,该层具有softmax和线性回归层,最终对对象进行分类并预测已识别对象的边界框。4.2Faster RCNN的问题上述讨论过的所有对象检测算法都使用区域来识别对象,且网络不会一次查看完整图像,而是按顺序关注图像的某些部分,这样会带来两个复杂性的问题:该算法需要多次通过单个图像来提取到所有对象;由于不是端到端的算法,不同的系统一个接一个地工作,整体系统的性能进一步取决于先前系统的表现效果。5.总结下表是总结了本文中介绍的所有算法算法特征预测时间限制CNN将图像分成多个区域,将每个区域分类为不同的类别-需要很多区域来准确预测,因此计算时间长RCNN使用选择性搜索生成区域,从每个图像中提取大约2000个区域40-50秒每个区域分别传递给CNN的计算时间也很长,且使用三种不同的模型进行预测Fast RCNN每个图像只传递一次到CNN,并提取特征图。在这些地图上使用选择性搜索来生成预测。将RCNN中使用的所有三种模型组合在一起2秒选择性搜索很慢,因此计算时间仍然很长Faster RCNN用区域提议网络替换选择性搜索方法,使算法更快0.2秒对象提议需要时间,并且由于不同的系统一个接一个地工作,系统的性能取决于先前系统的表现效果本文作者:【方向】阅读原文本文为云栖社区原创内容,未经允许不得转载。

December 14, 2018 · 1 min · jiezi

使用Keras构建CNN网络识别森林卫星图

介绍本文我们将使用tf.keras构建一个卷积神经网络,用于识别森林卫星图。tf.keras是Tensorflow的高阶API,具有模块性,易扩展性,相比Tensorflow的Low-level API可以更快速的实现模型。Pytorch也是相当不错的框架,感兴趣的读者可以查看官方文档。伴随Tensorflow对Keras的支持,目前Keras功能已十分强大,比如对TPU,多GPU的分布式策略支持等。数据下载下载链接(文件较大)图片数据集包含4万张照片,每张照片包含两种标签:天气:晴天,阴天,雾霾等地面:农业区,居住区,道路等下载链接CSV文件包含“图片名字”,“天气标签”,“地面标签”我们希望训练的模型可以准确的预测新卫星图片的上述标签。我们的模型将会有天气,地面的两个输出,两种不同的损失函数。训练完成后,我们会导出模型使用Tensorflow Serving部署。创建模型import tensorflow as tfIMG_SIZE=128img_input=tf.keras.Input(shape=(IMG_SIZE,IMG_SIZE,3),name=‘input_layer’)conv_1_32=tf.keras.layers.Conv2D( filters=32, # 卷积核为奇数:1,奇数具有中心点 2,图像两边可以对称padding kernel_size=3, padding=‘same’, # 激活函数在输入为负值时激活值为0,此时神经元无法学习,LeakyReLU在输入为负值时不为0(但值很小) activation=‘relu’)(img_input)pool_1_2=tf.keras.layers.MaxPooling2D( # 默认过滤器大小和步长(2,2) padding=‘same’)(conv_1_32)conv_2_32=tf.keras.layers.Conv2D( filters=32, kernel_size=3, padding=‘same’, activation=‘relu’)(pool_1_2)pool_2_2=tf.keras.layers.MaxPooling2D( padding=‘same’)(conv_2_32)# 将输出展开conv_flat=tf.keras.layers.Flatten()(pool_2_2)fc_1_128=tf.keras.layers.Dense( units=128, activation=‘relu’)(conv_flat)# 仅训练时设置fc_1_drop=tf.keras.layers.Dropout( rate=0.2)(fc_1_128)fc_2_128=tf.keras.layers.Dense( units=128, activation=‘relu’)(fc_1_drop)fc_2_drop=tf.keras.layers.Dropout( rate=0.2)(fc_2_128)# 天气标签输出weather_output=tf.keras.layers.Dense( units=4, activation=‘softmax’, name=‘weather’)(fc_2_drop)# 地面标签输出ground_outpout=tf.keras.layers.Dense( units=13, # 对于类别大于2的分类问题,如果类别互斥使用softmax,反之使用sigmoid activation=‘sigmoid’, name=‘ground’)(fc_2_drop)model=tf.keras.Model( inputs=img_input, outputs=[weather_output,ground_outpout])各层参数数量模型配置:这里有必要简单介绍下Adam算法:Adam 通过计算梯度的一阶矩估计和二阶矩估计而为不同的参数设计独立的自适应性学习率。Adam 算法同时获得了 AdaGrad 和 RMSProp 算法的优点。Adam 不仅如 RMSProp 算法那样基于一阶矩均值计算适应性参数学习率,它同时还充分利用了梯度的二阶矩均值。梯度对角缩放的不变性适用于解决包含很高噪声或稀疏梯度的问题model.compile( # 也可尝试下带动量的SGD # Adam 默认参数值:lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.0. optimizer=‘adam’, loss={ # 注意这里损失函数对应的激活函数 “weather”:‘categorical_crossentropy’, ‘ground’:‘binary_crossentropy’ })模型训练import astimport numpy as npimport mathimport osimport randomimport pandas as pdfrom tensorflow.keras.preprocessing.image import img_to_arrayfrom tensorflow.keras.preprocessing.image import load_imgdef load_image(img_path,img_size): # /255 将像素值由 0-255 转为 0-1 区间 return img_to_array(load_img(img_path,target_size=(img_size,img_size)))/255.class KagglePlanetSequence(tf.keras.utils.Sequence): def init(self,df_path,data_path,img_size,batch_size,mode=‘train’): self.df=pd.read_csv(df_path) self.img_size=img_size self.batch_size=batch_size self.mode=mode # ast.literal_eval(x) 功能同 eval,如:"[1,2,3]“转为[1,2,3],但增加了非法字符处理 self.w_lables=self.df[‘weather_labels’].apply(lambda x:ast.literal_eval(x)).tolist() self.g_lables=self.df[‘ground_labels’].apply(lambda x:ast.literal_eval(x)).tolist() self.imges_list=self.df[‘image_name’].apply(lambda x:os.path.join(data_path,x+’.jpg’)).tolist() def len(self): # math.ceil 向上取整,返回:大于或等于输入数值 # 计算每个epoch内训练步数 return int(math.ceil(len(self.df)/float(self.batch_size))) # 打乱数据 def on_epoch_end(self): self.indexes=range(len(self.imges_list)) if self.mode == ’train’: self.indexes=random.sample(self.indexes,k=len(self.indexes)) # 以下较简单,别把区间算错就好 def get_batch_labels(self,idx): return [ self.w_lables[idx*self.batch_size:(idx+1)self.batch_size], self.g_lables[idxself.batch_size:(idx+1)self.batch_size] ] def get_batch_feature(self,idx): batch_images=self.imges_list[ idxself.batch_size:(idx+1)*self.batch_size ] return np.array([load_image(img,self.img_size) for img in batch_images]) def getitem(self, idx): batch_x=self.get_batch_feature(idx) batch_y=self.get_batch_labels(idx) return batch_x,batch_y seq=KagglePlanetSequence(’./KagglePlaneMCML.csv’,’./data/train/’, img_size=IMG_SIZE,batch_size=32)在训练期间通过添加callbacks,可以实现“保存模型”,‘提前停止训练’等功能。本次,我们使用ModelCheckPoint在每迭代一次训练集后保存模型。callbacks=[ # .h5 保存参数和图 # 1为输出进度条记录,2为每个epoch输出一行记录 tf.keras.callbacks.ModelCheckpoint(’./models.h5’,verbose=1)]# fit_generator分批次产生数据,可以节约内存model.fit_generator( generator=seq, verbose=1, epochs=1, # 使用基于进程的线程 use_multiprocessing=True, # 进程数量 workers=4, callbacks=callbacks)读取已保存的模型,用于训练anther_model=tf.keras.models.load_model(’./model.h5’)anther_model.fit_generator(generator=seq,verbose=1,epochs=1)测试模型test_sq=KagglePlanetSequence( ‘./KagglePlaneMCML.csv’, ‘./data/train/’, img_size=IMG_SIZE, batch_size=32, mode=‘test’)predictons=model.predict_generator( generator=test_sq,verbose=1)使用DataSet作为输入TFRecord文件中的数据是通过tf.train.Example Protocol Buffer格式存储,其中包含一个从属性名称到取值的字典,属性的取值可以为”BytesList“,”FloatList“或者”Int64List“。此外,TFRecord的值可以作为Cloud MLEngine的输入。我们首先将图片和标签保存为TFRecord文件def bytes_feature(value): return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))tf_records_filename=’./data/KajgglePlaneTFRecord{}’.format(IMG_SIZE)writer=tf.python_io.TFRecordWriter(tf_records_filename)# 获取对应数据df_train={}img_list=[os.path.join(’./data/train’,v+’.jpg’) for v in df_train[‘image_name’].tolist()]w_lables_arr=np.array([ast.literal_eval(l) for l in df_train[‘weather_labels’]])g_lables_arr=np.array([ast.literal_eval(l) for l in df_train[‘ground_labels’]])# 文件写入for i in range(len(df_train)): w_labels=w_lables_arr[i] g_lables=g_lables_arr[i] img=np.array([load_image(img_list[i],IMG_SIZE)]) example=tf.train.Example( features=tf.train.Feature( # 读取的时候使用该key feattures={ ‘image’:_bytes_feature(img.tostring()), ‘weather_labels’:_bytes_feature(w_lables_arr.tostring()), ‘ground_lables’:_bytes_feature(g_lables_arr.tostring()) } ) ) writer.write(example.SerizlizeToString())writer.close()DataSet读取TFRecord文件”“”提供两种解析方法,一种是tf.FixedLenFeature,解析结果为tensor,另一种是tf.VarLenFeature得到的结果是SparseTensor,用于处理稀疏数据。当然,读取数据和写入数据的格式要一致。“”“featdef={ ‘image’:tf.FixedLenFeature(shape=[],dtype=tf.string), ‘weather_lables’:tf.FixedLenFeature(shape=[],dtype=tf.string), ‘ground_labels’:tf.FixedLenFeature(shape=[],dtype=tf.string)}def parse_record(tfre_file,clip=False): file=tf.parse_single_example(tfre_file,features=featdef) # tf.decode_raw 将字符串解析成图像对应的像素数组 img=tf.reshape(tf.decode_raw(file[‘image’],tf.float32),shape=(IMG_SIZE,IMG_SIZE,3)) weather=tf.decode_raw(file[‘weather_lables’],tf.float32) ground=tf.decode_raw(file[‘ground_lables’],tf.float32) return img,weather,groundds_train=tf.data.TFRecordDataset(filenames=’./data/KanglePlaneTFRecord{}’.format(IMG_SIZE),map=_parse_record)ds_train=ds_train.shuffle(buffer_size=1000).batch(32)模型训练model=tf.keras.Model(inputs=img_input,outputs=[weather_output,ground_outpout])model.compile( optimizer=‘adam’, loss={ ‘weather’:‘categorical_crossentropy’, ‘ground’:‘binary_crossentropy’ })# history.history:loss values and metrics values# 通过history_rest.history可以获取loss value metrics value等信息history_rest=model.fit(ds_train,steps_per_epoch=100,epochs=1)模型导出Tensorflow Serving框架 如需了解更多有关内容,请查看官网介绍。 使用TensorFlow Serving 部署我们只能使用SaveModel方法。# 模型导出,官方推荐使用save_model# 如果需要更多自定义功能请使用SavedModelBuilder# 返回训练模式/测试模式的flag# learning_phase: 0 train model 1 test modeltf.keras.backend.set_learning_phase(1)model=tf.keras.models.load_model(’./model.h5’)export_path=’./PlaneModel/1’with tf.keras.backend.get_session() as sess: tf.saved_model.simple_save( session=sess, export_dir=export_path, inputs={ ‘input_image’:model.input }, outputs={ t.name:t for t in model.outputs } )模型保存后的文件结构:$ tree.└── 1 ├── saved_model.pb └── variables ├── variables.data-00000-of-00001 └── variables.index重新训练的模型可以放到”PlanetModel/2“文件夹内,此时TensorFlow Serving会自动更新。TensorFlow Serving安装1.安装Docker2.安装Serving imagedocker pull tensorflow/serving3,运行Tensorflow ServinggRPC默认端口:8500REST API默认端口:8501默认环境变量MODEL_NAME:“model” MODEL_BASE_PATH:"/models"tensorflow_model_server –model_base_path=$(pwd) –rest_api_port=9000 –model_name=PlanetModel4,模型预测请求格式要求{ # 如多人开发,最好自定义 “signature_name”: <string>, # 只可包含以下任意一项 “instances”: <value>|<(nested)list>|<list-of-objects> “inputs”: <value>|<(nested)list>|<object>}import requestsimport json# 不要忘了归一化处理image = img_to_array(load_img(’./data/train/train_10001.jpg’, target_size=(128,128))) / 255.payload={ ‘instances’:[{‘input_images’:image.tolist()}]}result=requests.post(‘http://localhost:9000/v1/models/PlanetModel:predict’,json=payload)json.load(result.content)5,预测结果返回格式{ “predictions”: <value>|<(nested)list>|<list-of-objects>}{u’predictions’: [ {u’ground_2/Sigmoid:0’: [ 0.153237, 0.000527727, 0.00555856, 0.00542973, 0.00105254, 0.000256282, 0.103614, 0.0325185, 0.998204, 0.072204, 0.00745501, 0.00326175, 0.0942268], u’weather_2/Softmax:0’: [ 0.963947, 0.000207846, 0.00113924, 0.0347063] }]}总结本次项目只是简单的案例,主要是为了熟悉相关模块的使用,项目本身还有很多需要优化的地方。keras清晰,友好可以快速实现你的想法,还可以结和“Eager Execution” “Estimator”使用。虽然keras优点很多,但由于它高度封装,所以想进一步了解Tensorflow的朋友,掌握Low-level API还是很有必要的。本文实现参考Stijn Decubber的文章,欢迎关注他的博客。 ...

December 4, 2018 · 2 min · jiezi

吃鸡决赛圈直播却卡屏的我心好痛,立马找来开发刚了一波

欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦本文由腾讯云视频发表于云+社区专栏关注公众号“腾讯云视频”,一键获取 技术干货 | 优惠活动 | 视频方案本是一名佛性型吃鸡选手,自从被三个妹子带着躺尸吃鸡之后,便立志要成为一名吃鸡高手,一大早便沉迷于各大网站的吃鸡直播中,正看到决赛圈激动人心的时刻,直播花屏了?然后游戏结束了?我的天,我是谁?我在哪?我错过了什么?作为一名有强迫症的IT小哥哥,怎能让直播花屏现象存在呢?一方面,为了自己能成为一名吃鸡高手。另一方面,不能错过每一个升职加薪的机会。就这样开始了一段漫长的长征之路……对于直播业务,“秒开、卡顿、时延、进房成功率"是我们经常关注的几个指标,这些指标可以说是从"一个用户能够优雅地进入直播间"的角度来考量的,然而进入直播间后"用户究竟看到的什么内容"也是很关键的一环,内容上除了涉及安全的一些指标外,还可能会有内容是否有花屏、绿屏等其他异常内容,这时我们便会考虑如何衡量和发现外网的花屏情况。本文主要针对花屏提出了一种基于CNN网络的检测方案。01花屏检测能力构建 无论是视频还是直播,都是由一帧帧图像组成的,之所以会以一种动态的形式展现到我们眼前,是因为了人类的视觉暂留现象。物体在快速运动时, 当人眼所看到的影像消失后,人眼仍能继续保留其影像0.1-0.4秒左右的图像,这种现象被称为视觉暂留现象。既然如此,检测直播中是否存在花屏,其实可以转换为检测直播中的帧画面是否是花屏的画面,即一个图像识别问题。那么如何识别一个图像是否是花屏呢?通常图像识别总是以特征为基础的,我们会先根据所设定的目标来提取相应的特征,用于我们后面来制定策略。不过好在现在的深度学习卷积神经网络CNN将提取特征和制定决策策略都帮我们完成了。而使用深度学习CNN网络则绕不开数据集和模型训练两大块1.1数据集准备困难要使用深度学习网络,一个门槛是需要足够的带有标签的数据集,否则学习出的网络很容易过拟合,从而泛化能力不强。说其是门槛是因为实际业务中更多情况是缺少数据集,就以现在的花屏为例,目前直播发生花屏的案例非常少,想要通过实际案例来收集足够的花屏图片作为训练集显得异常困难。因此必须探寻其他的路子来收集训练集。人类之所以能够分辨出花屏,是因为人类眼睛能够找到花屏图像的特征,虽然这些特征我们可能用语言都描述不出来,事实上,如果我们能用语言描述出特征,我们也很容易将其翻译成代码来找到花屏图像的特征。制作训练集机器学习其实也是通过特征来工作的,既然如此,我们可以制作一些花屏图像出来,让CNN网络找到它们区别于正常图片的特征,从而学习到花屏图片的检测能力。在使用YUVviewer工具时,发现当设置错误的分辨率来播放视频文件时会出现花屏情况。灵感来源于此,我们完全可以通过使用错误的分辨率从YUV文件中抽取帧,从而拿到花屏图片。整体流程如下:这里需要了解YUV文件的存储格式,从而根据格式来进行抽取对应的帧:YUV,分为三个分量,“Y”表示明亮度(Luminance或Luma),也就是灰度值;而“U”和“V” 表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。YUV采样样式有一下几种,以黑点表示采样该像素点的Y分量,以空心圆圈表示采用该像素点的UV分量,一般情况下我们都使用的是YUV420格式 YUV420格式存储方式如下所示:按照上面存储方式编写代码来提取帧,代码如下:def get_frames_from_YUV(filename, dims, numfrm, startfrm, frmstep): "”" 从给定的YUV文件中抽取对应的帧数据,帧数据格式仍然为YUV :param filename: YUV文件路径 :param dims: YUV文件的分辨率 :param numfrm: 要提取帧的数量 :param startfrm: 从哪一帧开始提取 :param frmstep: 抽取帧的帧间隔,即每隔几帧抽一帧 :return: 返回抽取帧的Y列表,U列表,V列表 """ filesize = os.path.getsize(filename) fp = open(filename, ‘rb’) blk_size = prod(dims) * 3 / 2 # 计算每帧大小 if (startfrm+1+(numfrm-1)*frmstep)*blk_size > filesize: numfrm = (filesize/blk_size - 1 - startfrm)/frmstep +1 util.log(‘文件读取越界–修改为%d’%numfrm) fp.seek(blk_size * startfrm, 0) # 跳转到指定开始帧 Y, U, V= [],[],[] d00 = dims[0] / 2 d01 = dims[1] / 2 for i in range(numfrm): util.log(‘文件读取第%d帧’ % i) Yt = zeros((dims[1], dims[0]), uint8, ‘C’) Ut = zeros((d01, d00), uint8, ‘C’) Vt = zeros((d01, d00), uint8, ‘C’) for m in range(dims[1]): for n in range(dims[0]): # print m,n Yt[m, n] = ord(fp.read(1)) for m in range(d01): for n in range(d00): Ut[m, n] = ord(fp.read(1)) for m in range(d01): for n in range(d00): Vt[m, n] = ord(fp.read(1)) Y = Y + [Yt] U = U + [Ut] V = V + [Vt] fp.seek(blk_size * (frmstep - 1), 1) # 跳出间隔帧 fp.close() return (Y, U, V)这里对分辨率错误的多种情况也做了下研究,发现如下规律:1)分辨率正确2)分辨率width+1情况3)分辨率width+n情况4)分辨率width-1情况5)分辨率width-n情况上面只是针对图片宽来进行错误干扰,可以看出宽变小花屏条纹方向是左下的,宽变大花屏条纹方向时右下的。所以我们队宽和高分别设置不同的错误值,会造成不同类型的花屏,于是可以用这种策略构造大量的花屏。我们用800多个视频,每个视频以一定的间隔来抽10帧,获得了8000多张花屏图片。这些图片标签为花屏,也就是我们的正样本,负样本可选取实际直播中的正常截图。至此,数据集准备差不多了。1.2 模型和训练模型上使用网上一些知名模型即可,这里我们使用了轻量的mobilenet模型,模型结构如下图所示:训练采用基于用imagenet已经训练好的模型来进行finetune训练,最后一层使用随机超参数来训练(这一层也无法读取pretrained模型的超参数,因为分类数不一致)。训练可以快速收敛,因为特征太明显了,而且测试集准确率很高。其实这里训练是一个不断迭代的过程,因为机器学习模型是一张白纸,它要具有怎样的能力完全是你教它的,而教的方式就是通过训练集(数据和标签),而想要让它能够应对更多的情况,你的训练集就要尽可能涵盖各种情况。而我们的训练集总是不足的,你总会有care不到的地方。训练集不足的情况会怎样?举个例子你训练个识别飞机的模型,而大部分关于飞机的图片都有天空,这样你给张天空的图片到模型,它也可能会认为是飞机,因为其实模型很可能学到的是天空的特征。而怎么让模型学到飞机的特征呢,当然需要调整训练集,让训练集里不仅包含背景为天空的飞机,还有陆地上的飞机等等。通过不断低迭代调优,我们在空间场景下检测准确率已经可以达到94%,NOW直播场景检测准确率也已达到90%。02直播检测方案 检测能力具备后想要真正地接入直播业务,还需要将直播流进行分帧,然后把对应的帧图片进行花屏检测。后台整体框架采用分帧截屏的手段,如果每天需要检测2000万张截图,即10s会有一张截图需要检测,而考虑花屏总是出现在很长的一段时间内,因此这里希望能够以更长的时间来抽样从而避免浪费算力。附一张目前业务检测花屏结果的截图:作为一名热爱工作的IT小哥哥,花了一个星期的时间,总算把基于CNN网络的直播花屏检测的工作告一段落了。工作使我开心,游戏使我快乐,终于可以再次流畅的游走在各大网址的吃鸡直播中啦问答游戏体系结构相关阅读团战开黑必备“良药”了解一下!再也不用担心网吧开黑队友听不清了!3行代码,为QQ轻游戏加上语音互动能力 【每日课程推荐】机器学习实战!快速入门在线广告业务及CTR相应知识 ...

October 12, 2018 · 1 min · jiezi