20191001-使用Keras演示基于AAM-Softmax的图像识别原理

2023-05-16

1. 前言

视觉领域内基本的深度学习方法无非包括:分类识别、目标检测、语义分割等。其中尤以分类识别最容易被人混淆,大多数人都以为两者是统一的概念,但实际上却天差地别。可以这么讲,“识别” 出于 “分类” 而胜于 “分类”!

以应用最广泛的人脸识别来讲,假如你需要针对公司做一个基于视觉的打卡应用,初学者最直观地想象无非是这不简单,采集好所有人的头像然后做分类不就搞定了!但是,如果公司的人员变动非常大,你岂不得每天都要在这上面耗时耗力耗资源了?难以更新和维护,这体验得多差啊!

众所周知,图像分类深度学习模型可以被表达为:特征提取器+分类器。分类器好说,把提取好的特征画一道道的分界线就完成任务了,近年最厉害最大的突破就在于这个特征提取器。参考《Additive Margin Softmax for Face Verification》中给出的一张图如下所示:

在这里插入图片描述

最左边的就是最原始的基于 Softmax 训练后抽取到的特征进行三维可视化的结果,这样的特征除了能用于分类之外毫无用处。想象一下如果同一个类别经过前面特征提取器的输出都能尽可能地是同一个特征向量该有多好,就像一个人无论年轻还是老去,素颜还是化妆,但都只有一个唯一标识的身份证号。即便达不到这样的要求,那能尽可能地接近也行啊,这样通过相似度就能比较了!这就是上图中靠右的方法结果。但是实际应用过程中训练时的类别太少了可能还不行,如果有几万、几十万甚至上百万的类别统统给这样训练一下,每个类别都被表达地非常紧凑,那岂不相当于拥有了针对此类数据集的完美编码器呢?所以传统的分类器某种程度上只完成了一小部分,而识别则再此基础上迈出了更远的一步:最大化类间距离,最小化类内距离。

在这方面的探索比较成功的有两类方法如下:

1.1 Metric Learning [度量学习]

  • Contrastive Loss, 2014-01-18, Deep learning face representation by joint identification-verification.
  • Triplet Loss, 2015-03-12, Facenet: A unified embedding for face recognition and clustering.
    • 2015-09-10, O. M. Parkhi, A. Vedaldi, and A. Zisserman. Deep face recognition.
  • Margin Based Loss, 2017-06-23, Sampling matters in deep embedding learning.
  • Soft-Margin Loss, 2017-11-21, In defense of the triplet loss for person re-identification.

1.2. Margin Based Classification [基于分类的边界最大化]

  • Centor Loss, 2016-09-16, A discriminative feature learning approach for deep face recognition.
  • L-Softmax, 2016-12-07, Large-margin softmax loss for convolutional neural networks.
  • Sphereface / A-Softmax, 2017-04-26, Sphereface: Deep hypersphere embedding for face recognition.
  • Normface, 2017-04-21, NormFace: L2 Hypersphere Embedding for Face Verification
  • AM-softmax, 2018-01-17, Additive Margin Softmax for Face Verification
  • CosFace, 2018-01-29, CosFace: Large Margin Cosine Loss for Deep Face Recognition
  • ArcFace / AAM-Softmax, 2018-01-23, ArcFace: Additive Angular Margin Loss for Deep Face Recognition

但度量学习既有组合爆炸等的问题又非常难以学习,所以基于最大化间隔的分类器自然成了当前的主流,其演化流程大致如下(可能有纰漏,还请指正),相比传统的图像分类只需要替换末尾的softmax改成自定义层,训练过程无需任何变化,但训练的结果却可以用来处理识别问题,岂不美哉!
在这里插入图片描述

2. 利用Keras自定义层并搭建简单的序列模型

在本文中是通过 MNIST 数据集作为演示,前面也提到过与搭建传统分类器的最大差别就在于模型的最后一层需要自己动手实现,个中的原理在网上已有非常多的推理,此处不表,所有代码如下:

# 参考1: https://keras.io/zh/layers/writing-your-own-keras-layers/
# 参考2: https://github.com/4uiiurz1/keras-arcface
from keras.layers import Layer, InputLayer, Conv2D, MaxPooling2D, Flatten, Dense, Dropout
from keras.layers import regularizers, BatchNormalization
from keras.models import Sequential
from keras.utils import plot_model
from keras.optimizers import Adam
from keras import backend as K


# Layer
class ArcLayer(Layer):
    def __init__(self, output_dim, s=30.0, m=0.5, regularizer=None, **kwargs):  # 初始化
        if 'input_shape' not in kwargs and 'input_dim' in kwargs:
            kwargs['input_shape'] = (kwargs.pop('input_dim'),)
        super(ArcLayer, self).__init__(**kwargs)
        self.output_dim = output_dim
        self.s = s
        self.m = m
        self.W = None
        self.regularizer = regularizers.get(regularizer)

    def build(self, input_shape):  # 定义本层的权
        assert len(input_shape) >= 2
        input_dim = input_shape[-1]
        self.W = self.add_weight(name="kernel",
                                 shape=(input_dim, self.output_dim),
                                 initializer='glorot_uniform',
                                 regularizer=self.regularizer,
                                 trainable=True
                                 )
        self.bias = None
        self.built = True

    def call(self, inputs, **kwargs):  # 实现本层从输入张量到输出张量的计算图
        inputs = K.tf.nn.l2_normalize(inputs, 1, 1e-10)  # X 归一化
        self.W = K.tf.nn.l2_normalize(self.W, 0, 1e-10)  # W 归一化
        # cos(θ) --------------------------------------------------------------
        cos_theta = K.dot(inputs, self.W)
        # CosFace ====================== 余弦距离 =====================
        # phi = cos_theta - self.m
        # ArcFace ====================== 角度距离 =====================
        # controls the (theta + m) should in range [0, pi]
        theta = K.tf.acos(K.clip(cos_theta, -1.0+K.epsilon(), 1.0-K.epsilon()))
        phi = K.tf.cos(theta + self.m)
        # e^φ -----------------------------------------------------------------
        e_phi = K.exp(self.s * phi)
        e_cos = K.exp(self.s * cos_theta)
        # output
        output = e_phi / (e_phi + (K.sum(e_cos, axis=-1, keepdims=True)-e_cos))
        return output

    def compute_output_shape(self, input_shape):  # 指定输入及输出张量形状变化的逻辑!
        return input_shape[0], self.output_dim


# Loss
def loss_defined(y_true, y_pred):
    loss = -K.mean(K.log(K.clip(K.sum(y_true * y_pred, axis=-1), K.epsilon(), None)), axis=-1)
    return loss


# model
def build_net():
    model = Sequential([InputLayer(input_shape=(28, 28, 1)),

                        Conv2D(32, (3, 3), activation='relu'),
                        Conv2D(32, (3, 3), activation='relu'),
                        BatchNormalization(),
                        MaxPooling2D(pool_size=(2, 2)),

                        Conv2D(64, (3, 3), activation='relu'),
                        Conv2D(64, (3, 3), activation='relu'),
                        BatchNormalization(),
                        MaxPooling2D(pool_size=(2, 2)),

                        Flatten(),
                        Dropout(0.25),
                        Dense(128, activation='relu'),

                        Dense(2, name="feature_embedding"),
                        ArcLayer(10, s=30.0, m=0.5)  # CosFace: s=24, m=0.2
                        ])
    # -------------------------------------
    model.summary()
    model.compile(loss=loss_defined, optimizer=Adam(), metrics=['accuracy'])
    return model


if __name__ == "__main__":
    model = build_net()
    plot_model(model, to_file="./model.png", show_shapes=True)

首先说一下在 Keras 中的自定义层,无非就是要继承并重写 build、call、compute_output_shape 这三个方法。众所周知,深度学习模型的基本单位一定是 “层”,所以 build 方法就是这个层中定义权重的地方,并指定哪些参数可训练哪些不需要之类。其次是 call 方法,用于实现改成的计算逻辑,说白了就是结合权重等怎么把输入变化到输出的过程。最后还有个 compute_output_shape 的方法,很简单,就是为了能显式打印输入输出张量的 shape 。

为了对比说明 CosFace 和 ArcFace,在上面的 call 方法中进行了对比,如果需要使用 CosFace 就注释掉下面关于 phi 的计算逻辑而改用上面的即可。

有了自定义的层,那实现模型的时候,只需要把传统分类器的最后一层进行替换即可,其它无需任何变化!

3. 训练及演示

此处用了 MNIST 数据集进行训练,训练完成后只需要对数据抽取特征向量并可视化即可,具体代码如下:

from aam_softmax_model import build_net
from keras.preprocessing import image
import matplotlib.pyplot as plt
from keras.models import Model
import pandas as pd
import numpy as np
import glob as gb
import os

# 准备
data = []
model_path = "./model.h5"
datasets_path = "./datasets/mnist_test"
# 加载
model = build_net()
model.load_weights(model_path)
vec_layer = Model(inputs=model.input, outputs=model.get_layer("feature_embedding").output)
# 计算
category_path = gb.glob(os.path.join(datasets_path, "**/"))
for category in category_path:
    y = int(category.split(os.sep)[-2])
    for idx, img_path in enumerate(gb.glob(os.path.join(category, "*"))):
        if idx >= 5:
            break
        img = image.load_img(img_path,
                             target_size=(28, 28),
                             grayscale=True,
                             )
        x = image.img_to_array(img)
        x *= 1./255
        x = np.expand_dims(x, axis=0)
        x2vec = vec_layer.predict(x)
        X_y = x2vec.flatten()
        X_y /= np.linalg.norm(X_y)  # 归一化:可不进行归一化即注释后对比观察
        X_y = X_y.tolist()
        X_y.append(y)
        data.append(X_y)
data = pd.DataFrame(data)
# 存储
# data.to_csv("vec.csv", sep='\t', header=False, index=False, float_format="%.7f")
# 显示
data[2] = data[2].astype('int')
classes = []
colors = ['tab:red', 'tab:green', 'tab:blue', 'tab:orange', 'tab:olive', 'tab:cyan', 'tab:purple', 'tab:brown', 'tab:pink', 'tab:gray']
markers = ['v', ',', 'o', '^', '.', '<', '>', 'p', '*', 'x']
for i in range(data[2].max() + 1):
    classes.append(data.loc[data[2] == i])
    plt.scatter(classes[i][0], classes[i][1], color=colors[i], marker=markers[i])
plt.title("Vector!")
plt.axis("square")
plt.show()

在这里插入图片描述

这儿对每个类别选取了五个数据进行展示,可以看到抽取到的特征是不非常紧凑呢?当然由于用到的模型非常简陋而且被压缩到两个维度,有几个类别出现了偏差,具体应用肯定需诸多调优的。

回到最初的提问,想象一下已经成功实现并训练好了这么一个强大的特征提取器 / 编码器,在实际生产时针对一个场景,第一步先有多少类别就先抽取并保存好对应的特征向量,第二步应用的时候只需用实时抽取到的特征向量与预录入的所有特征向量比较余弦相似度做查询即可!

题外话,像现今的很多客服机器人、聊天机器人啥的是不同一个道理呢?预先根据业务场景建立一个尽量完备的问答知识库,然后再仅针对 “问” 这一块儿设计一个识别模型能够把表达不同但含义相同的句子映射到相似的特征向量上去,接下来只需查找到预设计好的回答岂不就很有 AI 的味道了!

4. 附注

文中代码已上传至:https://github.com/atlantistin/Blogs/tree/master/20191001-keras-AAMsoftmax-mnist

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

20191001-使用Keras演示基于AAM-Softmax的图像识别原理 的相关文章

  • CentOS 7 禁止 root 直接登陆 及 修改默认端口

    一 禁止 Root 直接登陆 1 新建登陆用户 useradd tom 添加用户 passwd tom 设置密码 2 修改 sshd 配置文件 vi etc ssh sshd config 修改内容 PermitRootLogin no 改
  • 子网划分总结和技巧

    VLSM可变子网掩码对应CIDR值 下面是C类地址的划分技巧 1 确定划分子网数 子网数 61 2 n xff0c n代表子网掩码往右移动的位数 例如 xff1a 要划分2个子网 xff0c 子网掩码需要往右移动1位 xff0c 2 1 6
  • ubuntu虚拟机可以ping通主机但ssh连不上

    其它配置都正确后 xff0c 命令行执行此命令 xff1a service sshd start xff1b
  • android 安卓手机如何投屏到显示器

    这几天有个需求 xff0c 可以手机投屏到显示器上 xff0c 经过一番研究 xff0c 手机连接电脑再投屏是可以的 xff0c but xff0c 要想直接手机插一根线连接显示器 xff0c 只有支持USB3 0及以上的才可以 xff0c
  • sed之两个文件共有特征行的合并输出

    cat a txt 01 12510101 4001 02 12310001 4002 03 12550101 4003 04 12610001 4004 05 12810001 4005 06 12310001 4006 07 12710
  • Mac中有g++/clang,但引用报错:xcrun: error: invalid active developer path (/Library/Developer/Command...

    问题 xff1a 在Mac上安装fasttext的时候 xff0c 发现g 43 43 或者clang都不能直接引用 于是在终端用which检查了一下 xff0c 发现都是存在于 usr bin 目录中的 which g span clas
  • 利用XRDP远程登陆linux系统

    http linux chinaunix net bbs viewthread php tid 61 1149869 一般情况下我们用ssh客户端远程登陆inux系统 xff0c 至于图形界面下的linux 远程登陆工具 xff0c 我们一
  • BOND 动态链路聚合 lacp配置及相关问题

    内容基本都是参考的 xff0c 哪里有雷同或者错的地方 xff0c 请批评指正 针对802 3ad模式的业务分析 xff1a 场景 xff1a 两个千兆网口 xff0c 聚合成bond0 动态链路聚合抓取数据包格式分析 xff1a 后台bo
  • VNC使用介绍

    VNC在内部网络中经常被大家用到 xff0c 该工具同时具备远程操作和传输文件的双重功能 xff0c 而且速度也是很快的 xff0c xff08 低版本不具备文件传输功能 xff09 深受大众喜爱 xff0c 今天就简单写下在使用VNC的过
  • 清除chrome浏览器缓存

    之前有写过设置缓存 本文解决清除html缓存 如何才能清除缓存呢 xff1f 一下是几个清除浏览器缓存的方法 xff1a 方法1 chrome浏览器地址 xff1a chrome settings clearBrowserData xff1
  • Iterator 接口

    具有原生的Iterator 接口的数据结构有 Array Map Set String TypedArray arguments对象 NodeList对象 面我们来实现将class 和 object 也变成迭代的对象 实现的关键就是 Sym
  • 容器和LXC简单命令

    容器和LXC简单命令 文章目录 容器和LXC简单命令一 CGroup xff08 控制组 xff09 的功能1 cgroup xff08 容器控制组 xff09 1 1 功能 xff1a 1 2 具体功能 xff1a 1 3 控制组可以限制
  • Podman设置容器开机自启

    Podman设置容器开机自启 1 podman管理员容器开机自启动 span class token number 1 span span class token operator span span class token operato
  • Linux中tty、pty、pts的概念区别

    http blog sina com cn s blog 638ac15c01012e0v html 基本概念 xff1a 1 gt tty 终端设备的统称 tty一词源于Teletypes xff0c 或teletypewriters x
  • Linux下vnc的安装、使用以及设置开机启动

    安装和使用VNC resbian系统自带realvnc vnc server 启动vnc服务 vncserver 1 xff08 1类似与端口号 xff0c 也可以理解为桌面序号 xff09 关闭vnc服务 vncserver kill 1
  • 单例模式与双重锁

    设计模式中 xff0c 最为基础与常见的就是单例模式 这也是经常在面试过程中被要求手写的设计模式 下面就先写一个简单的单例 xff1a public class Singleton private static Singleton sing
  • tensorflow安装时成功,但引用时提示:Could not load dynamic library ‘cudart64_101.dll‘…… if you do not have a GPU

    问题 xff1a 前几天tensorflow已经安装成功 xff0c 并顺利引用 但是这几天安装了与之冲突的包 xff1b 在重新调整各个包的版本后 xff0c 引用tensorflow提示出错 xff1a gt gt gt import
  • 【Linux】线程实例 | 简单线程池

    今天来写一个简单版本的线程池 1 啥是线程池 池塘 xff0c 顾名思义 xff0c 线程池就是一个有很多线程的容器 我们只需要把任务交到这个线程的池子里面 xff0c 其就能帮我们多线程执行任务 xff0c 计算出结果 与阻塞队列不同的是
  • pandas数据读取与清洗视频05-批量读取excel文件并合并

    本系列课程适用人群 xff1a python零基础数据分析的朋友 xff1b 在校学生 xff1b 职场中经常要处理各种数据表格 xff0c 或大量数据 xff08 十万级以上 xff09 的朋友 xff1b 喜欢图表可视化的朋友 xff1
  • 解决Xp提示未激活状态

    今天不知是什么原因电脑突然桌面背景变为黑色 xff0c 右下角提示 You may be a victim of software counterfeiting xff0c 如下图 所示 解决方法 xff1a xff08 亲测可以解决 xf

随机推荐

  • 微软软件运行库下载 (DirectX,.NET Framework,VC++库..)

    运行库是程序在运行时所需要的库文件 xff0c 运行库中一般包括编程时常用的函数 xff0c 如字符串操作 文件操作 界面等内容 不同的语言所支持的函数通常是不同的 xff0c 所以使用的库也是完全不同的 xff0c 这就是为什么有VB运行
  • 解决笔记本win7系统玩游戏不能全屏办法

    我们在使用笔记本win7系统玩游戏时 xff0c 经常会发现屏幕居中两边有黑条 而有一些台式机的宽屏显示器也经常出现下玩游戏不能全屏的问题 下面系统之家给大家介绍游戏不能全屏问题通用解决方法 1 修改注册表中的显示器的参数设置 Win键 4
  • MouseWithoutBorders无界鼠标安装配置教程

    第一步 xff1a 怎样修改系统计算机全名 xff08 链接教程 xff09 win7如何修改计算机的名字 百度经验 所有虚拟机必须改成不一样的名字 xff08 至关重要 xff09 第二步 xff1a 必须防火墙为开启的状态 xff08
  • 更换 PVE7 软件仓库源和 CT模板(LXC)源为国内源

    PVE7 安装后默认配置的 apt 软件源和 CT LXC 容器模板源均是官方默认的 xff0c 国内使用性能不佳 xff0c 建议替换为 清华 Tuna 提供的国内镜像源 xff0c 速度将有一个较大的提升 如果 pve 官网 iso 镜
  • Proxmox 7.3 换国内源安装

    Proxmox 7 2 默认来自官方的源 xff0c 国内慢的一逼高峰期只有个几KB的速度 xff0c 所以换源 Debian系统源 阿里云源 和中科大proxmox源 一 更换阿里云的源 vi etc apt sources list 替
  • 在x86平台制作龙芯版debian 10系统(mips64el)

    OS ubuntu 18 04 使用debootstrap制作根文件系统会分成两个阶段 第一阶段是 xff0c 使用debootstrap命令来下载软件包 第二阶段是安装软件包 安装debootstap 等相关工具 sudo apt ins
  • Mac安装homebrew报错curl: (7) Failed to connect to raw.githubusercontent.com port 443: Operation的解决办法

    在mac上安装homebrew的时候一般都是在终端输入以下的命令安装的 xff1a bin bash c 34 curl fsSL https raw githubusercontent com Homebrew install maste
  • 深度强化学习-DQN算法

    论文地址 xff1a https arxiv org abs 1312 5602 先讲下在线 xff0c 离线 xff0c 同策略和异策略 同策略 xff08 on policy xff09 和异策略 xff08 off policy xf
  • 再忙也要及时输出

    最近项目比较忙 xff0c 加班到很晚 xff0c 所以没有太多时间来更新博客 在做事情的过程中有过许多想法因为没有及时记录下来 xff0c 已经回想不起来了 xff0c 这是一种损失 不论再忙碌 xff0c 也要去反思和输出自己思考的东西
  • 通过Navicat 连接的数据库 查看数据库密码

    有时候数据库密码弄丢了 但是navicat能正常连接 想着到navicat连接处 复制一个 发现复制不了 可以用下面的方法查看 一 xff1a 导出连接 选择要导出数据库 勾选导出密码 导出的结果 里面打开就有加密后的密码 二 xff1a
  • 波长与频率的关系

    波长的定义 沿着波的传播方向 xff0c 在波的图形中相对平衡位置的位移时刻相同的两个质点之间的距离 横波与纵波的波长 在横波中波长通常是指相邻两个波峰或波谷之间的距离 在纵波中波长是指相邻两个密部或疏部之间的距离 波长在物理中表示为 xf
  • Python中的yield详细解释

    Python中的yield详细解释 yield是一个六级词汇 xff0c 常见意思有 产量 xff0c 屈服 动词 这里的yield大概率解释为一个僻义 缴出 咱们在什么情况下 xff0c 会用到yield呢 xff1f 答 xff1a 处
  • scrapy下载文件遇到的问题

    在写DEMO时遇到两个问题 1 FilesPipeline 不执行 原因 xff1a 从网上抄的脚本 xff0c FILES STORE 写成 FILE STORE了 xff0c 改成FILES STORE xff0c 可以触发FilesP
  • odoo 界面风格(theme)设置

    odoo 官方提供了不少收费或免费的界面风格 xff0c 可以自行选择安装 xff0c 下面介绍一下安装过程 xff11 下载安装包 安装包从官方下载 xff0c 地址 xff1a https apps odoo com apps them
  • windows环境 odoo16源码安装

    windows环境 odoo16源码安装 odoo16 源码安装 postgresql 安装 anaconda安装 odoo安装 odoo配置 odoo16 源码安装 安装环境为windows7 postgresql 13 odoo16的源
  • 1602B液晶使用范例

    2007 12 24 16 27 46 液晶显示模块具有体积小 功耗低 显示内容丰富等特点 xff0c 现在字符型液晶显示模块已经是单片机应用设计中最常用的信息显示器件了 本实验以常见的1602B字符型LCD模块为例 xff0c 介绍该模块
  • 访问共享文件夹总是提示“指定的网络名不再可用”,重启就好了

    共享文件夹可以ping通 xff0c 但是访问的时候总是提示 指定的网络名不再可用 xff0c 重启电脑之后就好了 网上的解决方案基本上都是检查Computer Browser Server Workstation这几个服务有没有启动 这几
  • MySQL加密方式之更改(error 1251)

    caching sha2 password To mysql native password MySQL V8后 xff0c 采用更强的SHA加密方式 xff0c caching sha2 password 如果客户端 JDBC或者App通
  • LVGL 8.2 菜单

    定义及声明 span class token keyword enum span span class token punctuation span LV MENU ITEM BUILDER VARIANT 1 span class tok
  • 20191001-使用Keras演示基于AAM-Softmax的图像识别原理

    1 前言 视觉领域内基本的深度学习方法无非包括 xff1a 分类识别 目标检测 语义分割等 其中尤以分类识别最容易被人混淆 xff0c 大多数人都以为两者是统一的概念 xff0c 但实际上却天差地别 可以这么讲 xff0c 识别 出于 分类