提前锁票 InfoQ 最具价值感的视频栏目 | InfoQ 大咖说 了解详情
写点什么

技术详解:人脸识别算法的训练之路(上)

2020 年 6 月 12 日

技术详解:人脸识别算法的训练之路(上)

摘要:我们从人脸识别技术的技术细节讲起,带你初步了解人脸识别技术的发展过程。通过平台实例的操作,带你看看如何利用公有云的计算资源,快速训练一个可用的人脸识别模型。


前言

大家应该都看过布拉德.伯德执导、汤姆.克鲁斯主演的《碟中谍 4 吧》?茫茫人海的火车站,只要一眨眼的功夫已经被计算机识别出来,随即被特工盯梢;迎面相逢的美女是致命杀手,手机发出嘀嘀的报警声,上面已经显示美女的姓名和信息。这就是本文想要介绍的人脸识别算法,以及如果使用公有云 AI 平台训练模型。


作为目前人工智能领域中成熟较早、落地较广的技术之一,人脸识别的目的是要判断图片和视频中人脸的身份。从平常手机的刷脸解锁、刷脸支付,再到安防领域内的人脸识别布控,等等,人脸识别技术都有着广泛的应用。人脸是每个人与生俱来的特征,该特征具有唯一性并且不易被复制,从而为身份鉴别提供了必要的前提。


人脸识别的研究始于 20 世纪 60 年代,随着计算机技术和光学成像技术的发展不断提高,以及近几年神经网络技术的再次兴起,尤其是卷积神经网络在图像识别和检测中取得的巨大成功,使得人脸识别系统的效果得到了极大的提升。本文,我们从人脸识别技术的技术细节讲起,带你初步了解人脸识别技术的发展过程,文章的后半篇,我们将会使用 ModelArts 平台的自定义镜像,带你看看如何利用公有云的计算资源,快速训练一个可用的人脸识别模型。


正文

不管是基于传统图像处理和机器学习技术,还是利用深度学习技术,其中的流程都是一样的。如图 1 所示,人脸识别系统都包括人脸检测、对齐、编码以及匹配四个基本环节组成。所以该部分首先通过对基于传统图像处理和机器学习算法的人脸识别系统进行概述,就可以看出整个深度学习算法在人脸识别领域内发展的脉络。



图 1 人脸检测流程


传统机器学习算法

前面已经说过,人脸识别的目的就是要判断图像中的人脸身份是什么,所以就首先需要先把图像中的人脸检测出来,其实这一步归根结底就是一个目标检测的问题。传统的图像目标检测算法主要有三部分组成,建议框生成、特征工程以及分类,包括著名的 RCNN 系列算法的优化思路也是基于这三部分进行的。


首先是建议框生成,该步骤最简单的想法就是在图片中 crop 出来一堆待检测框,然后检测该框内是否存在目标,如果存在,则该框在原图中的位置即为目标检测出的位置,因此在该步骤中对目标的覆盖率越大,则建议框生成策略越好。常见的建议框生成策略有 sliding window、Selective Search、Randomized Prim 等等,生成大量的候选框,如下图所示。



图 2 人脸建议框生成


得到大量的候选框后,传统的人脸检测算法接下来最主要的部分就是特征工程。特征工程其实就是利用算法工程师的专家经验对不同场景的人脸提取各种特征,例如边缘特征、形状形态学特征、纹理特征等等,具体的算法是技术有 LBP、Gabor、Haar、SIFT 等等特征提取算法,将一张以二维矩阵表示的人脸图片转换成各种特征向量的表示。


得到特征向量之后,就可以通过传统的机器学习分类器对特征进行分类,得到是否是人脸的判断,例如通过 adaboost、cascade、SVM、随机森林等等。通过传统分类器分类之后就可以得到人脸的区域、特征向量以及分类置信度等等。通过这些信息,我们就可以完成人脸对齐、特征表示以及人脸匹配识别的工作。


以传统方法中,经典的 HAAR+AdaBoost 的方法为例,在特征提取阶段,首先会利用 haar 特征在图片中提取出很多简单的特征。Haar 特征如下图 3 所示。为了满足不同大小人脸的检测,通常会利用高斯金字塔对不同分辨率的图像进行 Haar 特征的提取。



图 3 Haar 特征示意图


Haar 特征的计算方法是将白色区域内的像素和减去黑色区域,因此在人脸和非人脸的区域内,得到的值是不一样的。一般在具体实现过程中,可以通过积分图的方法快速实现。一般在归一化到 20*20 的训练图片中,可供使用的 Haar 特征数在一万个左右,因此在这种特征规模的情况下,可以利用机器学习的算法进行分类和识别。


得到 Haar 特征后,可以利用 Adaboost 进行分类,Adaboost 算法是一种将多个比较弱的分类方法合在一起,组合出新的强分类方法。根据该级联分类器,和训练好的各个特征选择阈值,就可以完成对人脸的检测。


从上述方法可以看出,传统的机器学习算法是基于特征的算法,因此需要大量的算法工程师的专家经验进行特征工程和调参等工作,算法效果也不是很好。而且人工设计在无约束环境中对不同变化情况都鲁棒很困难的。过去的图像算法是工程师更多的是通过传统的图像处理方法,根据现实场景和专家经验提取大量的特征,然后对提取的特征再进行统计学习的处理,这样整体算法的性能就非常依赖于现实场景和专家经验,对于人脸这种类别巨大,每类样本不均衡情况严重的无约束场景效果并不是很好。因此,近几年随着深度学习在图像处理中取得的巨大成功,人脸识别技术也都以深度学习为主,并且已经达到了非常好的效果。


深度学习在人脸识别领域的应用

在深度学习的人脸识别系统中,该问题被分成了一个目标检测问题和一个分类问题,而目标检测问题在深度学习中本质还是一个分类问题和回归问题,因此随着卷积神经网络在图片分类上的成功应用,人脸识别系统的效果得到了快速且巨大的提升,并以此诞生了大量的视觉算法公司,并将人脸识别应用在了社会生活的各个方面。


其实利用神经网络来做人脸识别并不是什么新思想,1997 年就有研究者为人脸检测、眼部定位和人脸识别提出了一种名为基于概率决策的神经网络的方法。这种人脸识别 PDBNN 被分成了每一个训练主体一个全连接子网络,以降低隐藏单元的数量和避免过拟合。研究者使用密度和边特征分别训练了两个 PBDNN,然后将它们的输出组合起来得到最终分类决定。但是受限于当时算力和数据的严重不足,算法相对简单,因此该算法并没有得到很好的效果。随着仅今年反向传播理论和算力框架等的日趋成熟,人脸识别算法的效果才开始得到巨大的提升。


在深度学习中,一个完整的人脸识别系统也包括图 1 所示的四个步骤,其中第一步骤叫做人脸检测算法,本质也是一个目标检测算法。第二个步骤叫做人脸对齐,目前又基于关键点的几何对齐和基于深度学习的人脸对齐。第三个步骤特征表示,在深度学习中是通过分类网络的思想,提取分类网络中的一些 feature 层作为人脸的特征表示,然后用相同的方式对标准人脸像进行处理,最后通过比对查询的方式完成整体的人脸识别系统。下面主要对人脸检测和人脸识别算法的发展进行简单综述。


人脸检测

深度学习在图像分类中的巨大成功后很快被用于人脸检测的问题,起初解决该问题的思路大多是基于 CNN 网络的尺度不变性,对图片进行不同尺度的缩放,然后进行推理并直接对类别和位置信息进行预测。另外,由于对 feature map 中的每一个点直接进行位置回归,得到的人脸框精度比较低,因此有人提出了基于多阶段分类器由粗到细的检测策略检测人脸,例如主要方法有 Cascade CNN、 DenseBox 和 MTCNN 等等。


MTCNN 是一个多任务的方法,第一次将人脸区域检测和人脸关键点检测放在了一起,与 Cascade CNN 一样也是基于 cascade 的框架,但是整体思路更加的巧妙合理,MTCNN 总体来说分为三个部分:PNet、RNet 和 ONet,网络结构如下图 4 所示。



图 4 MTCNN 网络结构示意图


首先 PNet 网络对输入图片 resize 到不同尺寸,作为输入,直接经过两层卷积后,回归人脸分类和人脸检测框,这部分称之为粗检测。将粗检测得到的人脸从原图中 crop 出来后,在输入的 R-Net,再进行一次人脸检测。最后将得到的人脸最终输入 O-Net,得到的 O-Net 输出结果为最终的人脸检测结果。MTCNN 整体流程相对比较简单,能够快速的进行部署和实现,但是 MTCNN 的缺点也很多。包括多阶段任务训练费时,大量中间结果的保存需要占用大量的存储空间。另外,由于改网络直接对 feature 点进行 bounding box 的回归,对于小目标人脸检测的效果也不是很好。还有,该网络在推理的过程中为了满足不同大小人脸检测需要,要将人脸图片 resize 到不同尺寸内,严重影响了推理的速度。


随着目标检测领域的发展,越来越多的实验证据证明目标检测中更多的瓶颈在于底层网络语义低但定位精度相对较高和高层网络语义高但定位精度低的矛盾,目标检测网络也开始流行 anchor-based 的策略和跨层融合的策略,例如著名的 Faster-rcnn、SSD 和 yolo 系列等。因此,人脸检测算法也越来越多的利用 anchor 和多路输出来满足不同大小人脸检出的效果,其中最著名的算法就是 SSH 网络结构。



图 5 SSH 网络结构示意图


从上图中可以看出,SSH 网络已经有对不同网络层输出进行处理的方法,只需要一遍推理就能完成不同大小人脸的检测过程,因此称之为 Single Stage。SSH 的网络也比较简单,就是对 VGG 不同卷积层惊醒了分支计算并输出。另外还对高层 feature 进行了上采样,与底层 feature 做 Eltwise Sum 来完成底层与高层的特征融合。另外 SSH 网络还设计了 detection module 和 context module,其中 context module 作为 detection module 的一部分,采用了 inception 的结构,获取更多上下文信息以及更大的感受野。



图 6 SSH 中的 detection module 模块



图 7 SSH 中 detection module 里的 context module 模块


SSH 利用 1×1 卷积对输出最终的回归和分类的分支结果,并没有利用全连接层,因此可以保证不同尺寸图片的输入都能得到输出的结果,也是响应了当时全卷积设计方式的潮流。遗憾的是该网络并没有输出 landmark 点,另外其实上下文结构也没有用到比较流行的特征金字塔结构,VGG16 的 backbone 也相对较浅,随着人脸优化技术的不断进行,各种各样的 trick 也都日趋成熟。因此,最后向大家介绍一下目前人脸检测算法中应用比较广的 Retinaface 网络。


Retinaface 由 google 提出,本质是基于 RetinaNet 的网络结构,采用特征金字塔技术,实现了多尺度信息的融合,对检测小物体有重要的作用。网络结构如下所示。



图 8 RetinaFace 网络结构示意图


从上图可以看出,Retinaface 的 backbone 网络为常见的卷积神经网络,然后加入特征金子塔结构和 Context Module 模块,进一步融合上下文的信息,并完成包括分类、检测、landmark 点回归以及图像自增强的多种任务。


因为人脸检测的本质是目标检测任务,目标检测未来的方向也适用于人脸的优化方向。目前在目标检测中小目标、遮挡目标的检测依旧很困难,另外大部份检测网络更多的开始部署在端侧,因此基于端侧的网络模型压缩和重构加速等等更加考验算法工程师对与深度学习检测算法的理解和应用。


人脸识别

人脸识别问题本质是一个分类问题,即每一个人作为一类进行分类检测,但实际应用过程中会出现很多问题。第一,人脸类别很多,如果要识别一个城镇的所有人,那么分类类别就将近十万以上的类别,另外每一个人之间可获得的标注样本很少,会出现很多长尾数据。根据上述问题,要对传统的 CNN 分类网络进行修改。


我们知道深度卷积网络虽然作为一种黑盒模型,但是能够通过数据训练的方式去表征图片或者物体的特征。因此人脸识别算法可以通过卷积网络提取出大量的人脸特征向量,然后根据相似度判断与底库比较完成人脸的识别过程,因此算法网络能不能对不同的人脸生成不同的特征,对同一人脸生成相似的特征,将是这类 embedding 任务的重点,也就是怎么样能够最大化类间距离以及最小化类内距离。


在人脸识别中,主干网络可以利用各种卷积神经网络完成特征提取的工作,例如 resnet,inception 等等经典的卷积神经网络作为 backbone,关键在于最后一层 loss function 的设计和实现。现在从两个思路分析一下基于深度学习的人脸识别算法中各种损失函数。


思路 1:metric learning,包括 contrastive loss, triplet loss 以及 sampling method


思路 2:margin based classification,包括 softmax with center loss, sphereface, normface, AM-sofrmax(cosface) 和 arcface。


Metric Larning

  1. Contrastive loss

  2. 深度学习中最先应用 metric learning 思想之一的便是 DeepID2 了。其中 DeepID2 最主要的改进是同一个网络同时训练 verification 和 classification(有两个监督信号)。其中在 verification loss 的特征层中引入了 contrastive loss。

  3. Contrastive loss 不仅考虑了相同类别的距离最小化,也同时考虑了不同类别的距离最大化,通过充分运用训练样本的 label 信息提升人脸识别的准确性。因此,该 loss 函数本质上使得同一个人的照片在特征空间距离足够近,不同人在特征空间里相距足够远直到超过某个阈值。(听起来和 triplet loss 有点像)。


  4. Contrastive loss 引入了两个信号,并通过两个信号对网络进行训练。其中识别信号的表达式如下:


  5. 验证信号的表达式如下:


  6. 基于这样的信号,DeepID2 在训练的时候就不是以一张图片为单位了,而是以 Image Pair 为单位,每次输入两张图片,为同一人则 为 1,如果不是同一人则 为-1.

  7. Triplet loss from FaceNet

  8. 这篇 15 年来自 Google 的 FaceNet 同样是人脸识别领域分水岭性质的工作。它提出了一个绝大部分人脸问题的统一解决框架,即:识别、验证、搜索等问题都可以放到特征空间里做,需要专注解决的仅仅是如何将人脸更好的映射到特征空间。

  9. Google 在 DeepID2 的基础上,抛弃了分类层即 Classification Loss,将 Contrastive Loss 改进为 Triplet loss,只为了一个目的:学习到更好的 feature。

  10. 直接贴出 Triplet loss 的损失函数,其输入的不再是 Image Pair,而是三张图片(Triplet),分别为 Anchor Face, Negative Face 和 Positive Face。Anchor 与 Positive Face 为同一人,与 Negative Face 为不同的人。那么 Triplet loss 的损失函数即可表示为:


  11. 该式子的直观解释为:在特征空间里 Anchor 与 Positive 的距离要小于 Anchor 与 Negative 的距离并超过一个 Margin Alpha。他与 Contrastive loss 的直观区别由下图所示。


  12. Metric learning 的问题

  13. 上述的两个 loss function 效果很不错,而且也符合人的客观认知,在实际项目中也有大量的应用,但该方法仍有一些不足之处。

  14. 模型训练依赖大量的数据,拟合过程很慢。由于 contrastive loss 和 triplet loss 都是基于 pair 或者 triplet 的,需要准备大量的正负样本,,训练很长时间都不可能完全遍历所有可能的样本间组合。网上有博客说 10000 人、500000 张左右的亚洲数据集上花一个月才能完成拟合。

  15. Sample 方式影响模型的训练。比如对于 triplet loss 来说,在训练过程中要随机的采样 anchor face, negative face 以及 positive face,好的样本采样能够加快训练速度和模型收敛,但是在随机抽取的过程中很难做到非常好。

  16. 缺少对 hard triplets 的挖掘,这也是大多数模型训练的问题。比如说在人脸识别领域中,hard negatives 表示相似但不同的人,而 hard positive 表示同一个人但完全不同的姿态、表情等等。而对 hard example 进行学习和特殊处理对于提高识别模型的精度至关重要。

  17. 对于 Metric Learning 不足进行修正的各种 trick

  18. a. Finetune

  19. 参考论文:Deep Face Recognition

  20. 在论文《Deep Face Recognition》中,为了加快 triplet loss 的训练,坐着先用 softmax 训练人脸识别模型,然后移除顶层的 classification layer,然后用 triplet loss 对模型进行特征层 finetune,在加速训练的同时也取得了很不错的效果。该方法也是现在训练 triplet loss 时最常用的方法。

  21. b. 对 Triplet loss 的修改

  22. 参考论文:In Defense of the Triplet Loss for Person Re-Identification

  23. 该作者说出了 Triplet loss 的缺点。对于 Triplet loss 训练所需要的一个三元组,anchor(a)、positive§、negative(n)来说,需要从训练集中随机挑选。由于 loss function 的驱动,很有可能挑选出来的是很简单的样本组合,即很像的正样本以及很不像的负样本,而让网络一直在简单样本上进行学习,会限制网络的范化能力。因此坐着修改了 triplet loss 并添加了新的 trick,大量实验证明,这种改进版的方法效果非常好。

  24. 在 Google 提供的 facenet triplet loss 训练时,一旦选定 B triplets 集合,数据就会按照顺序排好的 3 个一组,那么总共的组合就有 3B 种,但是这些 3B 个图像实际上有多达 种有效的 triplets 组合,仅仅使用 3B 种就很浪费。

  25. 在该片论文中,作者提出了一个 TriHard loss,其核心思想是在 triplet loss 的基础上加入对 hard example 的处理:对于每一个训练的 batch, 随机挑选 P 个 ID 的行人,每个行人随机挑选 K 张不同的图片,即一个 batch 含有 P×K 张图片。之后对于 batch 中的每一张图片 a,我们可以挑选一个最难的正样本和一个最难的负样本和 a 组成一个三元组。首先我们定义和 a 为相同 ID 的图片集为 A,剩下不同 ID 的图片图片集为 B,则 TriHard 损失表示为:


  26. 其中α是人为设定的阈值参数。TriHard loss 会计算 a 和 batch 中的每一张图片在特征空间的欧氏距离,然后选出与 a 距离最远(最不像)的正样本 p 和距离最近(最像)的负样本 n 来计算三元组损失。其中 d 表示欧式距离。损失函数的另一种写法如下:


  27. 另外,作者在轮中也提出了几个实验得到的观点:

  28. 平方后的欧式距离不如开方后的真实欧氏距离(后续会简单提一下原因)

  29. 提出了 Soft-Margin 损失函数替代原始的 Triplet loss 表达式,soft-margin 能够使得损失函数更加平滑,避免函数收敛在 bad local 处,能够一定程度上加速算法收敛。

  30. 引进了 Batch Hard Sampling

  31. 该方法考虑了 hard example 后效果比传统的 triplet loss 好。

  32. c. 对 loss 以及 sample 方法的修改

  33. 参考论文:Deep Metric Learning via Lifted Structured Feature Embedding

  34. 该论文首先提出了现有的三元组方法 无法充分利用 minibatch SGD training 的 training batches 的优势,创造性的将 the vector of pairwise distances 转换成 the matrix of pairwise distance,然后设计了一个新的结构化损失函数,取得了非常好的效果。如下图所示,是 contrastice embedding,triplet embedding 以及 lifted structured embedding 三种方式的采样示意图。


  35. 直观上看,lifted structured embedding 涉及的分类模式更多,作者为了避免大量数据造成的训练困难,作者在此基础上给出了一个结构化的损失函数。如下图所示。


  36. 其中 P 是正样本集合,N 是负样本集合。可以看到对比上述的损失函数,该损失函数开始考虑一个样本集合的问题。但是,并不是所有样本对之间的 negative edges 都携带了有用的信息,也就是说随机采样的样本对之间的 negative edges 携带了非常有限的信息,因此我们需要设计一种非随机的采样方法

  37. 通过上述的结构化损失函数我们可以看到,在最终计算损失函数时,考虑了最像和最不像的 hard pairs(也就是损失函数中 max 的用处),也就相当于在训练过程中添加了 difficult neighbors 的信息了训练 mini-batch,通过这种方式训练数据能够大概率的搜寻到 hard negatives 和 hard positives 的样本,而随着训练的不断进行,对 hard 样本的训练也将实现最大化类间距离和最小化类内距离的目的。


  38. 如上图所示,该文章在进行 metric learning 的时候并没有随机的选择 sample pairs,而是综合了多类样本之间较难区分者进行训练。此外,文中还提到了以为的寻求 max 的过程或者寻求 single hardest negative 的过程会导致网络收敛到一个 bad local optimum,我猜想可能是因为 max 的截断效应,使得梯度比较陡峭或者梯度间断点过多。作者进一步改进了 loss function,采用了 smooth upper bound,即下式所示。


  39. d. 对 sample 方式和对 triplet loss 的进一步修改

  40. 参考论文:Sampling Matters in Deep Embedding Learning

  41. 对采样方式的修改

  42. 文章指出 hard negative 样本由于 anchor 的距离较小,这是如果有噪声,那么这种采样方式就很容易受到噪声的影响,从而造成训练时的模型坍塌。FaceNet 曾经提出一种 semi-hard negative mining 的方法,它提出的方法是让采样的样本不是太 hard。但是根据作者的分析认为,sample 应该在样本中进行均匀的采样,因此最佳的采样状态应该是在分散均匀的负样本中,既有 hard,又有 semi-hard,又有 easy 的样本,因此作者提出了一种新的采样方法 Distance weighted sampling。

  43. 在现实状态下,我们队所有的样本进行两两采样,计算其距离,最终得到点对距离的分布有着如下的关系:


  44. 那么根据给定的距离,通过上述函数的反函数就可以得到其采样概率,根据该概率决定每个距离需要采样的比例。给定一个 anchor,采样负例的概率为下式:


  45. 由于训练样本与训练梯度强相关,因此作者也绘制出了采样距离、采样方法与数据梯度方差的关系,如下图所示。从图中可以看出,hard negative mining 方法采样的样本都处于高方差的区域,如果数据集中有噪声的话,采样很容易受到噪声的影响,从而导致模型坍塌。随机采样的样本容易集中在低方差的区域,从而使得 loss 很小,但此时模型实际上并没有训练好。Semi-hard negative mining 采样的范围很小,这很可能导致模型在很早的时候就收敛,loss 下降很慢,但实际上此时模型也还没训练好;而本文提出的方法,能够实现在整个数据集上均匀采样。


  46. 对 loss function 的修改

  47. 作者在观察 constractive loss 和 triplet loss 的时候发现一个问题,就是负样本在非常 hard 的时候 loss 函数非常的平滑,那么也就意味着梯度会很小,梯度小对于训练来说就意味着非常 hard 的样本不能充分训练,网络得不到 hard 样本的有效信息,因此 hard 样本的效果就会变差。所以如果在 hard 样本周围 loss 不是那么平滑,也就是深度学习中经常用的导数为 1(像 relu 一样),那么 hard 模式会不会就解决了梯度消失的问题。另外 loss function 还要实现 triplet loss 对正负样本的兼顾,以及具备 margin 设计的功能,也就是自适应不同的数据分布。损失函数如下:


  48. 我们称 anchor 样本与正例样本之间的距离为正例对距离;称 anchor 样本与负例样本之间的距离为负例对距离。公式中的参数 beta 定义了正例对距离与负例对距离之间的界限,如果正例对距离 Dij 大于 beta,则损失加大;或者负例对距离 Dij 小于 beta,损失加大。A 控制样本的分离间隔;当样本为正例对时,yij 为 1,样本为负例对时,yij 为-1。下图为损失函数曲线。


  49. 从上图可以看出为什么在非常 hard 的时候会出现梯度消失的情况,因为离 0 点近的时候蓝色的线越来越平滑,梯度也就越来越小了。另外作者对β的设置也进行了调优,加入了样本偏置、类别偏置以及超参,对损失函数进一步优化,能够根据训练过程自动修改β的值。


作者介绍


Leon Li:浙江大学光电系毕业生,在大华工作 2 年后,18 年 4 月入职华为,一直从事图像算法的优化以及相关产品落地的工作。


Hannah:英国 UCL(伦敦大学学院)数据科学毕业生, 2018 年 6 月应届毕业生身份入职华为,机器学习平台算法落地负责人。


麦克周:在华为,每一位员工都会有自己的导师,我是 Leon 和 Hannah 的领路人。


2020 年 6 月 12 日 20:281195

评论

发布
暂无评论
发现更多内容

图扑打造智慧化物流仓储管理系统组态仿真

一只数据鲸鱼

物联网 工业4.0 组态软件 智慧物流

比特币矿机工作原理

v16629866266

volatile,还可以有这么硬的理解

云流

Java 程序员 线程

区块链数字货币交易所系统软件APP开发

开發I852946OIIO

系统开发

Android经典面试:46道面试题带你了解中高级Android面试,附面试题答案

欢喜学安卓

android 程序员 面试 移动开发

android开发培训!深度解析跳槽从开始到结束完整流程,系列篇

欢喜学安卓

android 程序员 面试 移动开发

【Java虚拟机】- Java虚拟机之逃逸分析

双木之林

即构✖叮咚课堂:行业第一套AI课堂解决方案是怎么被实现的?

ZEGO即构

图解分布式之:最终一致性,一致只会迟到,但绝不缺席

四猿外

架构 分布式 分布式系统 一致性 数据一致性

DDD分层架构最佳实践

程序员小毕

Java 编程 架构 面试 DDD

Apache Flink 在实时金融数据湖的应用

Apache Flink

flink

15个国内外最受欢迎的YouTube视频下载器

科技猫

youtube视频下载 油管视频下载 下载youtube视频 下载油管视频 视频下载器

【CSS】画三角形(8个角度及其原理)

学习委员

CSS html5 前端 CSS小技巧 28天写作

简单五步:利用Gitstats给代码仓库做一次体检

后台技术汇

28天写作

2021最新版阿里巴巴Java性能调优速成手册强烈推荐

比伯

Java 编程 架构 面试 架构师

新“庖丁解牛”,华为云技术全牛图解

陈泽涛

AQS之ReentrantReadWriteLock精讲分析上篇

伯阳

AQS 读写锁 ReentrantReadWriteLock 多线程与高并发 lock

吉他谱怎么看?看谱大攻略送上!

懒得勤快

音乐 吉他学习 吉他谱 看谱

合约交易APP系统开发|合约交易软件开发

开發I852946OIIO

系统开发

Hadoop编程实战:HDFS用户Shell详解

罗小龙

hadoop 最佳实践 28天写作 hdfs shell

架构师训练营第三周作业 -命题作业

阿德儿

Kafka 中的消息存储在磁盘上的目录布局是怎样的?

码农架构

Java kafka 架构 设计模式

【Redis】- Redis Cluser之数据分布

双木之林

Spring全家桶笔记:Spring+Spring Boot+Spring Cloud+Spring MVC

Crud的程序员

Java spring 程序员 架构

基于 KubeEdge 和 Kuiper 的边缘流式数据处理实践

华为云原生团队

数据库 云原生 边缘计算 华为云 边缘技术

快了何止100%?阿里巴巴Java性能调优实战(2021华山版)PDF版开源

Java架构追梦

Java 阿里巴巴 架构 性能优化 华山版

大数据知识专栏 -MapReduce 自定义计数器技术

小马哥

大数据 mapreduce 日更挑战

备忘录1

Vei

初步解析 Elasticsearch Document 核心元数据

escray

elastic 日更挑战 28天写作 死磕Elasticsearch 60天通过Elastic认证考试

Web UI自动化测试之元素定位

行者AI

软件测试 测试 自动化测试

认识产品经理(第一节)

让我思考一会儿

打造 VUCA 时代的 10 倍速 IT 团队

打造 VUCA 时代的 10 倍速 IT 团队

技术详解:人脸识别算法的训练之路(上)-InfoQ