1.目标检测与YOLO
目标检测问题
目标检测是在给定的图片中精确找到物体所在位置,并标注出物体的类别。物体的尺寸变化范围很大,摆放物体的角度,姿态不定,而且可以出现在图片的任何地方,并且物体还可以是多个类别。
目标检测问题发展
① R-CNN
② SPP NET
③ Fast R-CNN
④ Faster R-CNN
⑤ 最终实现YOLO
YOLO是一个集大成的方法,不了解之前的方法,很难掌握YOLO的思路。
YOLO: You Only Look Once
2.目标检测实现:
预备知识:
① 目标检测基本原理
很多时候图像里有多个我们感兴趣的目标,我们不仅想知道它们的类别,还想得到它们在图像中的具体位置。在计算机视觉里,我们将这类任务称为目标检测(object detection)或物体检测。目标检测在多个领域中被广泛使用。例如,在无人驾驶里,我们需要通过识别拍摄到的视频图像里的车辆、行人、道路和障碍的位置来规划行进线路。机器人也常通过该任务来检测感兴趣的目标。安防领域则需要检测异常目标,如歹徒或者炸弹。
在目标检测里,我们通常使用边界框(bounding box)来描述目标位置。边界框是一个矩形框,可以由矩形左上角的x和y轴坐标与右下角的x和y轴坐标确定。我们根据下面的图的坐标信息来定义图中狗和猫的边界框。图中的坐标原点在图像的左上角,原点往右和往下分别为x轴和y轴的正方向。
dog_bbox, cat_bbox = [60, 45, 378, 516], [400, 112, 655, 493]
我们可以在图中将边界框画出来,以检查其是否准确。画之前,我们定义一个辅助函数bbox_to_rect。它将边界框表示成matplotlib的边界框格式。
锚框:
目标检测算法通常会在输入图像中采样大量的区域,然后判断这些区域中是否包含我们感兴趣的目标,并调整区域边缘从而更准确地预测目标的真实边界框(ground-truth bounding box)。
不同的模型使用的区域采样方法可能不同。这里我们介绍其中的一种方法:它以每个像素为中心生成多个大小和宽高比(aspect ratio)不同的边界框。这些边界框被称为(anchor box)。我们将在后面基于锚框实践目标检测。
导入相关包:
生成多个锚框:
假设输入图像高为h,宽为w。我们分别以图像的每个像素为中心生成不同形状的锚框。设大小为s∈(0,1]且宽高比为r>0,那么锚框的宽和高将分别为𝑤𝑠√𝑟 和ℎ𝑠/ √𝑟。当中心位置给定时,已知宽和高的锚框是确定的。
下面我们分别设定好一组大小s1,…,sn和一组宽高比r1,…,rm。如果以每个像素为中心时使用所有的大小与宽高比的组合,输入图像将一共得到w*h*n*m个锚框。虽然这些锚框可能覆盖了所有的真实边界框,但计算复杂度容易过高。因此,我们通常只对包含s1或r1的大小与宽高比的组合感兴趣,即
也就是说,以相同像素为中心的锚框的数量为n+m−1。对于整个输入图像,我们将一共生成w*h(n+m−1)个锚框。
以上生成锚框的方法实现在下面的MultiBoxPrior函数中。指定输入、一组大小和一组宽高比,该函数将返回输入的所有锚框。
shifts_x和shifts_y是将宽高进行归一化处理然后用meshgrid函数生成一个向量矩阵,最后reshape成一行向量。
将reshape之后的向量进行stack操作,之后将得到的shift与原始的base_anchors相加从而自动生成所有的anchor 。
我们看到,返回锚框变量y的形状为(1,锚框个数,4)。将锚框变量y的形状变为(图像高,图像宽,以相同像素为中心的锚框个数,4)后,我们就可以通过指定像素位置来获取所有以该像素为中心的锚框了。下面的例子里我们访问以(250,250)为中心的第一个锚框。它有4个元素,分别是锚框左上角的x和y轴坐标和右下角的x和y轴坐标,其中x和y轴的坐标值分别已除以图像的宽和高,因此值域均为0和1之间。
为了描绘图像中以某个像素为中心的所有锚框,我们先定义show_bboxes函数以便在图像上画出多个边界框。这里将数据打包成列表。
为了描绘图像中以某个像素为中心的所有锚框,我们先定义show_bboxes函数以便在图像上画出多个边界框。每个bbox进行迭代然后在图像上画出边界框 。
刚刚我们看到,变量boxes中xx和yy轴的坐标值分别已除以图像的宽和高。在绘图时,我们需要恢复锚框的原始坐标值,并因此定义了变量bbox_scale。现在,我们可以画出图像中以(250, 250)为中心的所有锚框了。可以看到,大小为0.75且宽高比为1的锚框较好地覆盖了图像中的狗。
交互比:
刚刚提到某个锚框较好地覆盖了图像中的狗。如果该目标的真实边界框已知,这里的“较好”该如何量化呢?一种直观的方法是衡量锚框和真实边界框之间的相似度。我们知道,Jaccard系数(Jaccard index)可以衡量两个集合的相似度。给定集合A和B,它们的Jaccard系数即二者交集大小除以二者并集大小:
实际上,我们可以把边界框内的像素区域看成是像素的集合。如此一来,我们可以用两个边界框的像素集合的Jaccard系数衡量这两个边界框的相似度。当衡量两个边界框的相似度时,我们通常将Jaccard系数称为交并比,即两个边界框相交面积与相并面积之比,如图所示。交并比的取值范围在0和1之间:0表示两个边界框无重合像素,1表示两个边界框相等。
下面我们对齐进行实现:
set1和set2分别为(n1,4),(n2,4)大小的张量,利用clamp函数和向量运算直接计算了相交面积大小,当二者不相交时clamp函数将二者的upper_boundslower_bounds置为零,IOU计算时就不会有负值。
计算IOU来衡量锚框与真实边界框以及锚框与锚框之间的相似度。
标注训练集的锚框:
在训练集中,我们将每个锚框视为一个训练样本。为了训练目标检测模型,我们需要为每个锚框标注两类标签:一是锚框所含目标的类别,简称类别;二是真实边界框相对锚框的偏移量,简称偏移量(offset)。
在目标检测时,我们首先生成多个锚框,然后为每个锚框预测类别以及偏移量。
接着根据预测的偏移量调整锚框位置从而得到预测边界框。
最后筛选需要输出的预测边界框。
依此类推,直到矩阵X中所有n[b]列元素全部被丢弃。这个时候,我们已为n[b]个锚框各分配了一个真实边界框。 接下来,我们只遍历剩余的n[a]−n[b]个锚框:给定其中的锚框Ai,根据矩阵X的第i行找到与Ai交并比最大的真实边界框Bj,且只有当该交并比大于预先设定的阈值时,才为锚框Ai分配真实边界框Bj。
如果一个锚框A被分配了真实边界框B,将锚框A的类别设为B的类别,并根据B和A的中心坐标的相对位置以及两个框的相对大小为锚框A标注偏移量。设锚框A及其被分配的真实边界框B的中心坐标分别为(x[a],y[a])和(x[b],y[b]),A和B的宽分别为w[a]和w[b],高分别为h[a]和h[b],一个常用的技巧是将A的偏移量标注为
其中常数的默认值为μx=μy=μw=μh=0,σx=σy=0.1,σw=σh=0.2,如果一个锚框没有被分配真实边界框,我们只需将该锚框的类别设为背景。类别为背景的锚框通常被称为负类锚框,其余则被称为正类锚框。
下面是一个真实例子的演示:
输入gt和anchor并且在图中显示,显示出的gt和anchor如下图:
下面实现MultiBoxTarget函数来为锚框标注类别和偏移量。该函数将背景类别设为0,并令从零开始的目标类别的整数索引自加1(1为狗,2为猫)。
实现MultiBoxTarget函数来为锚框标注类别和偏移量
我们根据锚框与真实边界框在图像中的位置来分析这些标注的类别。例如,在所有的“锚框—真实边界框”的配对中,锚框A4与猫的真实边界框的交并比最大,因此锚框A4类别标注为猫。
返回值的第二项为掩码(mask)变量,形状为(批量大小, 锚框个数的四倍)。掩码变量中的元素与每个锚框的4个偏移量一一对应。 由于我们不关心对背景的检测,有关负类的偏移量不应影响目标函数。通过按元素乘法,掩码变量中的0可以在计算目标函数之前过滤掉负类的偏移量。
labels[1]
输出:
tensor([[0., 0., 0., 0., 1., 1., 1., 1., 1.,1., 1., 1., 0., 0., 0., 0., 1., 1.,1., 1.]])
输出预测边界框:
锚框数量较多时,同一个目标上可能会输出较多相似的预测边界框。为了使结果更加简洁,我们可以移除相似的预测边界框。常用的方法叫作非极大值抑制。假设有ABCDEF6个预测边界框(已经按照得分从小到大排序)
1、从最大概率矩形框F开始,分别判断A~E与F的重叠度IOU是否大于某个设定的阈值;
2、假设B、D与F的重叠度超过阈值,那么就扔掉B、D;并标记第一个矩形框F,是我们保留下来的。
3、从剩下的矩形框A、C、E中,选择概率最大的E,然后判断E与A、C的重叠度,重叠度大于一定的阈值,那么就扔掉;并标记E是我们保留下来的第二个矩形框。
4、一直重复这个过程,找到所有曾经被保留下来的矩形框。
下面来看一个具体的例子。先构造4个锚框。简单起见,我们假设预测偏移量全是0:预测边界框即锚框。最后,我们构造每个类别的预测概率。
以下代码中:
cls_prob: 经过softmax后得到的各个锚框的预测概率,
shape:(bn, 预测总类别数+1, 锚框个数)
loc_pred: 预测的各个锚框的偏移量, shape:(bn, 锚框个数*4)
anchor: MultiBoxPrior输出的默认锚框, shape: (1, 锚框个数, 4)
nms_threshold: 非极大抑制中的阈值
c_p: (预测总类别数+1, 锚框个数)
l_p: (锚框个数*4, )
anc: (锚框个数, 4)
nms_threshold: 非极大抑制中的阈值
下面我们实现MultiBoxDetection函数来执行非极大值抑制。
然后我们运行MultiBoxDetection函数并设阈值为0.5。这里为输入都增加了样本维。我们看到,返回的结果的形状为(批量大小, 锚框个数, 6)。其中每一行的6个元素代表同一个预测边界框的输出信息。第一个元素是索引从0开始计数的预测类别(0为狗,1为猫),其中-1表示背景或在非极大值抑制中被移除。第二个元素是预测边界框的置信度。剩余的4个元素分别是预测边界框左上角的xx和yy轴坐标以及右下角的xx和yy轴坐标(值域在0到1之间)。
输出为:
我们移除掉类别为-1的预测边界框,并可视化非极大值抑制保留的结果。
目标检测数据集
数据集简介:
在目标检测领域并没有类似MNIST或Fashion-MNIST那样的小数据集。为了快速测试模型,我们合成了一个小的数据集。
• 首先,用一个开源的皮卡丘3D模型生成了1000张不同角度和大小的皮卡丘图像。
• 后我们收集了一系列背景图像,并在每张图的随机位置放置一张随机的皮卡丘图像。
皮卡丘数据集使用MXNet提供的im2rec工具将图像转换成了二进制的RecordIO格式,但是我们后续要使用PyTorch,所以我们先用脚本将其转换成了PNG图片并用json文件存放对应的label信息。最终pikachu文件夹的结构如下:
数据集的读取
首先定义一个数据集类PikachuDetDataset,数据集每个样本包含label和image。
label是一个 m×5m×5 的向量,即m个边界框,每个边界框由[class,x_min, y_min, x_max, y_max]表示,这里的皮卡丘数据集中每个图像只有一个边界框,因此m=1。
image是一个所有元素都位于[0.0, 1.0]的浮点tensor,代表图片数据。
数据集类PikachuDetDataset的定义如下:
然后我们通过创建DataLoader实例来读取目标检测数据集。我们将以随机顺序读取训练数据集,按序读取测试数据集。
下面我们读取一个小批量并打印图像和标签的形状。图像的形状和之前实验中的一样,依然是(批量大小, 通道数, 高, 宽)。而标签的形状则是(批量大小, m, 5),其中m等于数据集中单个图像最多含有的边界框个数。
其输出为:
torch.Size([32, 3, 256, 256]) torch.Size([32, 1, 5])
图示数据
我们用以下代码画出10张图像和它们中的边界框。
可以看到,皮卡丘的角度、大小和位置在每张图像中都不一样。当然,这是一个简单的人工数据集。实际中的数据通常会复杂得多。
小节总结
合成的皮卡丘数据集可用于测试目标检测模型。
目标检测的数据读取跟图像分类的类似。然而,在引入边界框后,标签形状和图像增广(如随机裁剪)发生了变化。
3.语义分割
语义分割关注如何将图像分割成属于不同语义类别的区域。值得一提的是,这些语义区域的标注和预测都是像素级的。下图展示了语义分割中图像有关狗、猫和背景的标签。可以看到,与目标检测相比,语义分割标注的像素级的边框显然更加精细。
计算机视觉领域还有2个与语义分割相似的重要问题,即图像分割和实例分割。我们在这里将它们与语义分割简单区分一下。
• 图像分割将图像分割成若干组成区域。这类问题的方法通常利用图像中像素之间的相关性。它在训练时不需要有关图像像素的标签信息,在预测时也无法保证分割出的区域具有我们希望得到的语义。
• 实例分割又叫同时检测并分割。它研究如何识别图像中各个目标实例的像素级区域。与语义分割有所不同。
• 以上一张ppt中的两只狗为例,图像分割可能将狗分割成两个区域:一个覆盖以黑色为主的嘴巴和眼睛,而另一个覆盖以黄色为主的其余部分身体。而实例分割不仅需要区分语义,还要区分不同的目标实例。如果图像中有两只狗,实例分割需要区分像素属于这两只狗中的哪一只。
语义分割数据集
语义分割的一个重要数据集叫作Pascal VOC2012 。为了更好地了解这个数据集,我们先导入实验所需的包或模块。
我们先下载这个数据集的压缩包。压缩包大小是2 GB左右,下载需要一定时间。下载后解压得到VOCdevkit/VOC2012文件夹,然后将其放置在data文件夹下。
进入../../data/VOCdevkit/VOC2012路径后,我们可以获取数据集的不同组成部分。
其中ImageSets/Segmentation路径包含了指定训练和测试样本的文本文件。
JPEGImages和SegmentationClass路径下分别包含了样本的输入图像和标签。
这里的标签也是图像格式,其尺寸和它所标注的输入图像的尺寸相同。标签中颜色相同的像素属于同一个语义类别。
下面定义read_voc_images函数将输入图像和标签读进内存。
我们画出前5张输入图像和它们的标签。在标签图像中,白色和黑色分别代表边框和背景,而其他不同的颜色则对应不同的类别。
接下来,我们列出标签中每个RGB颜色的值及其标注的类别。
有了上面定义的两个常量以后,我们可以很容易地查找标签中每个像素的类别索引。
例如,第一张样本图像中飞机头部区域的类别索引为1,而背景全是0。
y = voc_label_indices(train_labels[0], colormap2label)
y[105:115, 130:140], VOC_CLASSES[1]
输出:
在之前的章节中,我们通过缩放图像使其符合模型的输入形状。然而在语义分割里,这样做需要将预测的像素类别重新映射回原始尺寸的输入图像。这样的映射难以做到精确,尤其在不同语义的分割区域。为了避免这个问题,我们将图像裁剪成固定尺寸而不是缩放。具体来说,我们使用图像增广里的随机裁剪,并对输入图像和标签裁剪相同区域。
最终结果如下:
自定义语义分割数据集类
我们通过继承PyTorch提供的Dataset类自定义了一个语义分割数据集类VOCSegDataset。
通过实现__getitem__函数,我们可以任意访问数据集中索引为idx的输入图像及其每个像素的类别索引。
由于数据集中有些图像的尺寸可能小于随机裁剪所指定的输出尺寸,这些样本需要通过自定义的filter函数所移除。
此外,我们还对输入图像的RGB三个通道的值分别做标准化。
自定义语义分割数据集类VOCSegDataset:
读取数据集
我们通过自定义的VOCSegDataset类来分别创建训练集和测试集的实例。假设我们指定随机裁剪的输出图像的形状为320×480。下面我们可以查看训练集和测试集所保留的样本个数。
其输出为:
设批量大小为64,分别定义训练集和测试集的迭代器。
打印第一个小批量的类型和形状。不同于图像分类和目标识别,这里的标签是一个三维数组。
输出:
小节总结:
• 语义分割关注如何将图像分割成属于不同语义类别的区域。
• 语义分割的一个重要数据集叫作Pascal VOC2012。
• 由于语义分割的输入图像和标签在像素上一一对应,所以将图像随机裁剪成固定尺寸而不是缩放。
语义分割实例:
Deeplab v3是目前广泛使用的语义分割方法。
4.风格迁移
简介:
如果你是一位摄影爱好者,也许接触过滤镜。它能改变照片的颜色样式,从而使风景照更加锐利或者令人像更加美白。但一个滤镜通常只能改变照片的某个方面。如果要照片达到理想中的样式,经常需要尝试大量不同的组合,其复杂程度不亚于模型调参。
在本节中,我们将介绍如何使用卷积神经网络自动将某图像中的样式应用在另一图像之上,即风格迁移。
这里我们需要两张输入图像,一张是内容图像,另一张是样式图像,我们将使用神经网络修改内容图像使其在样式上接近样式图像。
方法:
• 首先,我们初始化合成图像,例如将其初始化成内容图像。该合成图像是样式迁移过程中唯一需要更新的变量,即样式迁移所需迭代的模型参数。
• 然后,我们选择一个预训练的卷积神经网络来抽取图像的特征,其中的模型参数在训练中无须更新。深度卷积神经网络凭借多个层逐级抽取图像的特征。我们可以选择其中某些层的输出作为内容特征或样式特征。
以之前放的图像为例,这里选取的预训练的神经网络含有3个卷积层,其中第二层输出图像的内容特征,而第一层和第三层的输出被作为图像的样式特征。
接下来,我们通过正向传播(实线箭头方向)计算样式迁移的损失函数,并通过反向传播(虚线箭头方向)迭代模型参数,即不断更新合成图像。
样式迁移常用的损失函数由3部分组成:
• 内容损失(content loss)使合成图像与内容图像在内容特征上接近
• 样式损失(style loss)令合成图像与样式图像在样式特征上接近
• 总变差损失(total variation loss)则有助于减少合成图像中的噪点。最后,当模型训练结束时,我们输出样式迁移的模型参数,即得到最终的合成图像。
定义代价函数:
定义内容代价函数:
选定隐藏层l,用表示图片C在第l层的激活项输出,表示图片G在第l层的激活项输出,则定义内容代价函数如下:
风格代价函数:
什么是风格?
神经网络各隐藏层输出激活项的含义。
怎样量化风格差异?
相关性的计算:
风格矩阵(Style matrix):
(1)什么是风格矩阵?
(2)符号规定
(3)计算
风格代价函数:
(1)隐藏层l的风格代价函数
(2)总体风格代价函数
总体代价函数:
实验:
下面,我们通过实验来进一步了解样式迁移的技术细节。实验需要用到一些导入的包或模块。
读取内容图像和样式图像:
首先,我们分别读取内容图像和样式图像。
内容图像读取:
样式图像读取:
从打印出的图像坐标轴可以看出,它们的尺寸并不一样。、
预处理和后处理图像:
预处理函数preprocess对先对更改输入图像的尺寸,然后再将PIL图片转成卷积神经网络接受的输入格式,再在RGB三个通道分别做标准化。
由于预训练模型是在均值为[0.485, 0.456, 0.406]的图片数据上预训练的,所以我们要将图片标准化保持相同的均值和标准差。
后处理函数postprocess则将输出图像中的像素值还原回标准化之前的值。由于图像每个像素的浮点数值在0到1之间,我们使用clamp函数对小于0和大于1的值分别取0和1。
抽取特征 :
我们使用基于ImageNet数据集预训练的VGG-19模型来抽取图像特征。
为了抽取图像的内容特征和样式特征,我们可以选择VGG网络中某些层的输出。
一般来说,越靠近输入层的输出越容易抽取图像的细节信息,反之则越容易抽取图像的全局信息。
为了避免合成图像过多保留内容图像的细节,我们选择VGG较靠近输出的层,也称内容层,来输出图像的内容特征。
我们还从VGG中选择不同层的输出来匹配局部和全局的样式,这些层也叫样式层。
实验中,我们选择第四卷积块的最后一个卷积层作为内容层,以及每个卷积块的第一个卷积层作为样式层。这些层的索引可以通过打印pretrained_net实例来获取。
pretrained_net
输出即为每一层的数据索引:
在抽取特征时,我们只需要用到VGG从输入层到最靠近输出层的内容层或样式层之间的所有层。下面构建一个新的网络net,它只保留需要用到的VGG的所有层。我们将使用net来抽取特征。
给定输入X,如果简单调用前向计算net(X),只能获得最后一层的输出。由于我们还需要中间层的输出,因此这里我们逐层计算,并保留内容层和样式层的输出。
下面定义两个函数,其中get_contents函数对内容图像抽取内容特征,由于合成图像是样式迁
移所需迭代的模型参数,我们只能在训练过程中通过调用;extract_features函数来抽取合成图像的内容特征和样式特征。
get_styles函数则对样式图像抽取样式特征。因为在训练时无须改变预训练的VGG的模型参数,所以我们可以在训练开始之前就提取出内容图像的内容特征,以及样式图像的样式特征。
定义损失函数
样式迁移的损失函数。它由3部分损失组成。
① 内容损失
② 样式损失
③ 总变差损失
内容损失:
与线性回归中的损失函数类似,内容损失通过平方误差函数衡量合成图像与内容图像在内容特征上的差异。平方误差函数的两个输入均为extract_features函数计算所得到的内容层的输出。
样式损失:
样式损失也一样通过平方误差函数衡量合成图像与样式图像在样式上的差异。为了表达样式层输出的样式,我们先通过extract_features函数计算样式层的输出。
我们用一个格拉姆矩阵表达样式层输出的样式。需要注意的是,当样本的高(h)和宽(w)的值较大时,格拉姆矩阵中的元素容易出现较大的值。此外,格拉姆矩阵的高和宽皆为通道数c。为了让样式损失不受这些值的大小影响,下面定义的gram函数将格拉姆矩阵除以了矩阵中元素的个数,即chw。
自然地,样式损失的平方误差函数的两个格拉姆矩阵输入分别基于合成图像与样式图像的样式层输出。这里假设基于样式图像的格拉姆矩阵gram_Y已经预先计算好了。
总变差损失:
有时候,我们学到的合成图像里面有大量高频噪点,即有特别亮或者特别暗的颗粒像素。一种常用的降噪方法是总变差降噪(total variationdenoising)。假设表示坐标为(i,j)的像素值,降低总变差损失
能够尽可能使邻近的像素值相似。
样式迁移的损失函数即内容损失、样式损失和总变差损失的加权和。通过调节这些权值超参数,我们可以权衡合成图像在保留内容、迁移样式以及降噪三方面的相对重要性。
创建和初始化合成图像:
在样式迁移中,合成图像是唯一需要更新的变量。因此,我们可以定义一个简单的模型GeneratedImage,并将合成图像视为模型参数。模型的前向计算只需返回模型参数即可。
下面,我们定义get_inits函数。该函数创建了合成图像的模型实例,并将其初始化为图像X。样式图像在各个样式层的格拉姆矩阵styles_Y_gram将在训练前预先计算好。
训练:
在训练模型时,我们不断抽取合成图像的内容特征和样式特征,并计算损失函数。
下面我们开始训练模型。首先将内容图像和样式图像的高和宽分别调整为150和225像素。合成图像将由内容图像来初始化。
其输出为:
下面我们查看一下训练好的合成图像。可以看到图中的合成图像保留了内容图像的风景和物体,并同时迁移了样式图像的色彩。因为图像尺寸较小,所以细节上依然比较模糊。
为了得到更加清晰的合成图像,下面我们在更大的300×450尺寸上训练。我们将原图的高和宽放大2倍,以初始化更大尺寸的合成图像。
输出:
可以看到,由于图像尺寸更大,每一次迭代需要花费更多的时间。
查看训练好的图像:
从训练得到的图中可以看到,此时的合成图像因为尺寸更大,所以保留了更多的细节。合成图像里面不仅有大块的类似样式图像的油画色彩块,色彩块中甚至出现了细微的纹理。
章节小结:
样式迁移常用的损失函数由3部分组成:内容损失使合成图像与内容图像在内容特征上接近,样式损失令合成图像与样式图像在样式特征上接近,而总变差损失则有助于减少合成图像中的噪点。
可以通过预训练的卷积神经网络来抽取图像的特征,并通过最小化损失函数来不断更新合成图像。
用格拉姆矩阵表达样式层输出的样式。
5.人脸识别
人脸验证与人脸识别
两类问题的区别:
人脸验证(Face verification):
人脸识别(Face recognition):
从人脸验证到人脸识别
for i in 数据库中的所有照片:
调用人脸验证检测输入与数据库中照片i上的是否为同一人
人脸验证:
构建神经网络:
旧有思路转化为分类问题
局限性:One-shot learning
相似度函数:
Siamese网络对输入图片进行编码
双输入结构:
相似度函数的具体定义
训练神经网络:
面部验证与二分类:
可以预先知道下面的网络输出(预存人脸),节省识别的计算量。
面部验证与二分类:不同图对训练集
训练目的:
通过训练神经网络,我们希望同一人的两张照片间的相似度函数值尽可能小,不同人的两张片间的相似度函数值尽可能大,下以此为目标制作训练集、定义Loss函数。
制作训练集:
定义代价函数
(1)符号规定:
A—待测照片
P—同一人照片
N—不同人照片
(2)我们希望A与P的差距永远不超过A与N,即:
(3)定义Loss函数如下:
将所有样本的loss函数累加得到cost函数如下:
运行神经网络:
利用人脸验证实现人脸识别
对于训练完毕的神经网络,输入照片,通过简单的for循环语句遍历数据库中存储的所有照片,依次通过相似度函数进行计算,记录遍历过程中相似程度最大的值,在遍历结束后与预先设定的阈值进行比较,得出预测结果,完成人脸识别。
6.视觉应用展望
①生成对抗网络
生成对抗网络(GANs, generative adversarial networks)是由Ian Goodfellow等人在2014年的Generative Adversarial Networks一文中提出。
模型通过框架中(至少)两个模块:生成模型(Generative Model)和判别模型(Discriminative Model)的互相博弈学习产生相当好的输出。
原始 GAN 理论中,并不要求 G 和 D 都是神经网络,只需要是能拟合相应生成和判别的函数即可。但实用中一般均使用深度神经网络作为 G 和 D。
一个优秀的GAN应用需要有良好的训练方法,否则可能由于神经网络模型的自由性而导致输出不理想。
生成对抗网络其整体结构如下:
生成对抗网络(GAN)的初始原理十分容易理解,即构造两个神经网络,一个生成器,一个鉴别器,二者互相竞争训练,最后达到一种平衡(纳什平衡)。
GAN 启发自博弈论中的二人零和博弈(two-player game),GAN 模型中的两位博弈方分别由生成式模型(generativemodel,G)和判别式模型(discriminative model,D)充当。
生成模型 G 捕捉样本数据的分布,用服从某一分布(均匀分布,高斯分布等)的噪声 z 生成一个类似真实训练数据的样本,追求效果是越像真实样本越好。
判别模型 D 是一个二分类器,估计一个样本来自于训练数据(而非生成数据)的概率,如果样本来自于真实的训练数据,D 输出大概率,否则,D 输出小概率。
生成对抗网络的效果如下图所示:
生成手写数字
生成人脸:
7.循环神经网络与NLP
数据处理基础
1.1 特征编码
数值特征与类别特征:
⚫ 第1列表示年龄,是一个数值特征。
⚫ 第2列表示性别,是一个只有一位(0,1)的特征:0 -> Male, 1 -> Female。
⚫ 第3列表示国籍,目前有197个国家:1 -> US, 2 -> China, …
⚫ 可以用一个整数来表示,或者用一个独热向量来表示, 如US: [1,0, ….,0]。
⚫ 数值特征不适合表示类别,因此一般使用独热编码。
⚫ 国家编码从1开始,1~197,因为实际国籍有可能不填(对应0)。
总结:使用199维特征向量表达一个人的特征
1.2 文本处理
第一步:文本切分 (tokenization)
⚫ 给定文本片段,如:S = “… to be or not to be…”.
⚫ 将文本切分为单词序列:L = […, to, be, or, not, to, be, …],
第二步:统计词频
⚫ 创建一个字典(实际中经常使用Hash表)进行词频统计
⚫ 初始字典为空
⚫ 遍历文本,并按如下更新字典:
• 如果单词不再字典中,加入该单词 (𝑤, 1)
• 否则将其词频加1
⚫ 直到完成整个遍历
⚫ 按照词频降序进行排序
⚫ 将词频索引化
⚫ 如果词汇太多,则只保留排序在前的k个单词 (如取𝑘 = 10000)
第三步:独热编码
⚫ 将每一个词映射到索引号上
⚫ 需要时,将索引转换为独热编码
⚫ 词典上没有的词(低频词或拼写错误,直接忽略,或编码为0)
文本预处理与词嵌入
2.1 文本预处理
文本预处理
一篇文章可以被简单地看作一串单词序列,甚至是一串字符序列。 我们将解析文本的常见预处理步骤。 这些步骤通常包括:
1.将文本作为字符串加载到内存中。
2.将字符串切分为词元(如单词和字符)。
3.建立一个字典,将拆分的词元映射到数字索引。
4.将文本转换为数字索引序列,方便模型操作。
第一步:读取数据集
以H.G.Well的时光机器为例,从中加载文本。 这是一个相当小的语料库,只有30000多个单词,而
现实中的文档集合可能会包含数十亿个单词。如Chatgpt包含65T数据。
第二步:词汇切分
将原始文本以文本行为单位进行切分
第三步:构建词索引表
⚫ 打印前几个高频词及索引
⚫ 将每一条文本行转换成一个数字索引列表
2.2 文本预处理实例
IMDB影评数据集
• 5万条电影评论
• 每条评论可分为正面或负面
• 是一个二分类问题
• 2.5万条训练数据,2.5万条测试数据
下载:
• http://ai.stanford.edu/~amaas/data/sentiment/
• http://s3.amazonaws.com/text-datasets/aclImdb.zip
文本编码
问题:训练样本非对齐(长度不同)
解决:做剪切或填充
对齐后的文本
2.3 文本嵌入
如何将词映射成向量?
直接想法:使用之前所述的独热向量
问题:维数过高
词嵌入(word embedding):将独热向量映射为低维向量
原始向量:𝑣维;映射后:𝑑维,𝑑 ≪ 𝑣;
映射矩阵:𝑑 × 𝑣,根据训练数据学习得到
理解映射参数矩阵:两列:正向;负向
词嵌入训练效果:
准确率74%(测试集),不好不差
RNN模型
3.1 RNN概要
如何建模序列数据?
图像分类中使用:1对1模型
输入和输出维度:固定
文本处理中:
输入维度不定(可能一直有单词输入);
输出维度不定或者是1(直接最终理解结果)
用RNN建模序列数据
输入:The cat sat on the mat
3.2 RNN模型
3.3 RNN示例
使用RNN进行IMDB评论
结果评价
• 训练精度: 89.2%
• 验证精度: 84.3%
• 测试精度: 84.4%
比之前的结果好一些
使用RNN进行IMDB评论
结果评价
• 训练精度: 96.3%
• 验证精度: 85.4%
• 测试精度: 84.7%
比之前的结果好一点点(差不多)
3.4 RNN问题
随着输入的增加,会产生“遗忘”问题
LSTM模型
4.1 LSTM模型
LSTM网络模型
LSTM:“传送带”