Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 11856 articles
Browse latest View live

那些说easy girl的人是什么心态?

$
0
0

其实最讨厌easy girl的人不在知乎,甚至不在中国,而是在西方的混血儿。

你去看看那些混血儿的论坛:

http://www.reddit.com/r/hapas​www.reddit.com

毕竟easy girl和中国男人关系不大,中国男人不是受害者,可是easy girl的孩子就不一样了,那些亚欧混血儿,是直接的受害者,他们完全有资格评价自己母亲,还有easy girl,那么我们看看他们怎么评价的吧。

我挑出几个精彩的:

比如这个:

Asian woman embarrased me yesterday • r/hapas​www.reddit.com 图标

翻译:

我昨天去了一个派对。

我无聊就决定去了,可是我去晚了,只剩下两个座位,大家已经开始玩了,所以我很容易的就找了个座位坐下和人一起玩。

结果一个亚裔女也来玩了,她走到最后一个空椅子,就在我旁边。她看着椅子,也看着我一段时间,我有点不明白,然后我告诉她这个椅子是空的。她终于坐下了,我决定和她聊一下,但是她马上转过去了,和一个纯白人聊起来了。

然后我也不管了,看来她不想和我讲话。好吧没事,我和别人聊了一会,后来桌子上的人都散开了,我决定和那个亚裔女在说句话吧,结果她把自己挤到两个白人之间,却失败了,因为那两个白人不想给她留空,然后对我喊道:“我不和亚裔男交往,你们不要那么猥琐行吗?”。对我来说这并没出我意料。我能感觉到屋里的人看着和听着我们。

我不高兴了,准备爆炸。我反击她,指着我自己说“F**k you,我是个亚欧混血,这就是你未来的孩子的摸样"。然后我继续说"不管你怎么跪舔白人,你始终会是一个gook"。结果大家都看着我们,笑着,我暴怒的走出这个派对。

好消息是,我的朋友在派对里,给我发短信,告诉我那个亚裔女被大家欺负的走了。

哈哈哈,她想融入白人群里失败了。

再来:

翻译:

混血儿:我们有两个种族的优点和优势?

回答:OMG,关于混血儿拥有两个民族的优点和优势,请给我STFU,事实上我们有两个民族的缺点和劣势。我和我的兄弟姐妹都有很深的精神上的痛苦,而且我认识的,见过的,所有的母亲是亚洲人的混血儿都是这样。我们非常恨你们(指亚洲媚外女)。我们听过你们所说的所有的歧视白人女和亚洲男人的言论,经历过你们贬低甚至侮辱白人女和亚洲男人的行为。

我总是听到你们说“亚欧混血儿多漂亮”。不要说谎。我和我的兄弟姐妹长得像矮个子墨西哥人。有个SB亚洲碧池有一次跟我说她的亚欧混血孩子长大后会变得多漂亮,但她不知道我就是亚欧混血儿,她以为我是拉丁人。当我告诉她的时候,她走了,就像个SB一样。我的哥哥(或弟弟)遇到过白人男告诉他关于亚洲女人有多好上,多easy,以及关于白人女和亚洲男人有多差劲。同样,那些白人SB不知道我哥哥(弟弟)也是半个亚洲男人。你们就承认吧,你们根本不关心你们的孩子。

那些SB纯亚洲媚外女对跨种族交往有兴趣完全是出于对自己种族的自卑,还有希望自己的孩子变得像白人。她们对生物愚蠢又无知;不知道那些最成功,最高最漂亮,长得最像白人的混血儿都有白人母亲!不要用有白人母亲的亚欧混血儿的照片来为你的媚外洗白!黄男白女的混血是你们最痛恨的,不要说谎,我见过我父母对那些黄男白女组合都给予臭脸色,并仇视他们。我应该相信我的父母不是种族歧视吗??还best of both worlds??

我真希望我自己是纯种白人,纯种亚洲人,或者是有白人母亲的亚欧混血。Fuck 虎妈,我希望你们死得很惨。


同一个混血儿:

翻译:

(贴了一个混血儿的亚裔母亲的链接,希望自己的混血孩子得到帮助)

这就是为什么我f***king恨那种像我母亲这样的亚裔女人。她们当中有的只有在发现自己的孩子正在受苦时才开始关心。最令人愤怒的是,当她们绝望的去寻求帮助的时候,她们居然找亚裔区求帮忙?!你在开玩笑吗?你在寻求一个你憎恨,你背叛的群体来帮你?你憎恨亚裔群体里的一半人,居然还无耻的寻求他们的帮助?

我记得看过一篇文章,是一个愚蠢的亚洲碧池,当她看到林书豪出名了,她很高兴,因为她一半亚洲血统的儿子终于可以有学习的对象了!!!巴拉巴拉巴拉的。你是认真的吗?这些SB希望让一个纯亚裔男人做孩子的偶像,让纯亚裔血统的男性的成功来激励自己的孩子?这些女人明明非常鄙视亚裔男人,这是多么讽刺的事情啊?这是什么样的病态喜剧?

为什么不用Travis ishikawa,Marcel nguyen,Julian Kang,Kelly Hu,Lyoto Machida,Alexa Chung,Chloe Bennet,MylèneJampanoï,Brandon Lee或其他非常正常,健全,而且很成功的半个亚洲人来给孩子做偶像呢?为什么不???是不是……是不是……因为他们成功是因为他们都是有白人母亲的混血儿?



看看这些混血儿的说法,就知道那些真正痛恨easy girl的人是什么心态了。


将数据与应用分离

$
0
0
匿名读者 写道 " Solid(Social Linked Data 社交关联数据)是 Web 之父 Tim Berners-Lee 爵士所领导 MIT 团队的 Web 重新去中心化项目,基于 Linked Data(关联数据)原则以构建去中心化 Web 应用。将数据与应用分离,数据存储在 POD(Personal Online Data 个人在线数据) 上,应用访问数据需被授权。

Tim Berners-Lee 爵士和 John Bruce 创办 Inrupt公司推进 Solid 项目,支持 Solid 社区

Node Solid ServerSolid 规范的实现。除了自建服务器也可选择信任的 PODS(Personal Online Data Stores 个人在线数据商) 获取 Solid POD,注册并生成 WebID。(原型, 一堆 bug

配置文件查看器演示了应用如何登录登出并从 Solid POD 读取数据,需运行于 Web 服务器(例如 npm install -g local-web-server或参照 MDN 文档 建立简单的本地测试服务器),文档提供了用于测试的 Profile

在 yourpod 的照片与在 mypod 上对照片的评论需要关联数据。关联数据以 RDF(Resource Description Framework 资源描述框架)表示, 使用 RDF 语法中的 Turtle(Terse RDF Triple Language 简洁 RDF 三元语言),重用了 Web Annotation Ontology(Web 注释本体)。提供了 rdflib.js 关连数据常规工具箱以存储,解析,序列化为各格式并跟踪应用或服务器的数据变更。

Ruben Verborgh 是 Solid 项目的开发者,于去年的 博客文章谈论了数据和应用的分离。以社交网络为例,Twitter 和 Facebook 已经是以百万或数十亿用户单一数据中心的极端,而像电子邮件系统一样联邦式的去中心化微博网络 Mastodon(乳齿象)则大约 150 万用户分布在 2400 邦。Solid 则更进一步设想每人一个甚至多个 POD,一个 POD 用于办公,一个 POD 用于家用,一个 POD 用于学习等。数据和应用的分离将提高竞争力,以更快的速度激发创新,提供服务的能力不再取决于数据的所有。应用将以服务质量取胜,因为人们随时可以更换更合适的应用。"

墙裂推荐:中国改革四君子朱嘉明最新区块链演讲

$
0
0

导读:朱嘉明老师是中国改革四君子(王岐山、朱嘉明、黄江南、翁永曦)之一,著名经济学家。朱嘉明老师是区块链思想的积极支持者和布道者,对全球金融和经济趋势颇有远见,正是这位老师在5年前给一本叫《比特币》的书作序时,其所作的预言,都在2017年、2018年得到了印证,真得感謝这位预言者所作的探索与研究。本文是朱嘉明老师2018年9月15日在一次金融研讨会上的最新演讲,总结了2008年世界金融危机以来全球货币金融生态的演变,可以帮助大家从宏观上把握区块链和加密经济,墙裂推荐阅读。

今天是9月15日,为什么会选择今天?正如像主持人张宇先生开始向大家讲的,今天是雷曼兄弟倒闭十周年,我们把它称之为“全球金融危机的标志性事件”。这件事到今天整整过去了十年。2008年的9月15日是星期一,十年后的9月15日是星期六。

在这里,我想提这样一个观点,不管在座的有多么年轻,都应该认识到新世纪以来的两件大事改变了历史的走向,而且影响还会继续。一个是“9.11”事件,另一个就是2008年金融危机。前者改变了整个世界的政治秩序、地缘政治;后者改变了全世界的经济形态,特别是我们这次会议所关注的货币金融形态演变。两者交相呼应和持续影响,构成了我们今天宏观环境中的最重要的大背景。我今天主要想讲三个问题。

第一个问题:在2008年全球金融危机之后的十年间,世界究竟有哪些反应和对策?这里主要谈四个反应和四个对策,或者说世界性的反应方式表现在以下四件事,并来探讨它们之间的逻辑关系。

第一件事,2008年来自密码朋克的中本聪创造了比特币。为什么这件事情是意义重大的? 2008年的11月,一篇很短的论文登在一个由密码朋克主持的一个网站上,谁也没有想到这样一篇小小的论文居然影响了世界金融形态的新走向。中本聪研究的不过是一种P2P的支付方式,可以摆脱第三方的影响和控制,从而避免因为第三方所造成的风险。

第二件事,2009年以中国代表的新兴市场国家就国际金融秩序改革提出了方案。这个方案我们把它叫做“超主权货币方案”。2009年的3月27日,也就是在G20峰会前几天,当时的央行行长周小川,还有当时的副总理王岐山发表了文章,提出一个基本方案,就是为了解决世界金融秩序的非稳定状态,必须建立一个不由任何主权国家所控制的超主权货币,建立一个稳定世界金融秩序的货币基础。这个方案大家看到的结果就是我们刚才片子里所放出来的,并没有完全实现,最重要的结果是提高了新兴市场国家,主要是中国在IMF里的特别提款权的比重,比重提高了三个百分点左右。中国成为继美国、日本之后的IMF第三大股东国。

第三件事,茶党运动。这个时间节点也是2009年,发生的日子是美国的交税时间——4月15日。这件事影响很大,持续时间很长。这是一个没有领袖、没有中心组织、没有明确纲领的一次运动。它的直接后果就是影响了奥巴马时代的政治,以及导致特朗普成为美国总统的选举。

第四件事,2011年9月至11月的“占领华尔街”行动。因为种种原因,人们对它的关注不够。“占领华尔街”行动一度席卷美国,影响了世界的主要国家和城市,除了中国。他们的口号就是 “我们都是99%”,他们提出要选择一天不消费,采取不消费行为来抵制这个走向了严重的贫富差别和资本控制消费、权力控制民众的社会。当然,它和茶党运动不一样,最后是美国政府采取极为强制清场,使这个运动沉寂下来。

现在比较这四个运动,比较2008年金融危机之后世界性的精英的四种反应模式的差别,是有意义的。

第一件事,以中本聪代表的密码朋克的比特币创造事件。中本聪表面上是个技术派,但是他是相当成熟的技术派。他真正的基础是密码朋克运动。他背后有这样一个观点,就是在互联网时代我们必须保护自己的隐私。基于这个观点,他们最终产生出与中央政府,特别是中央政府所控制的法币体系的分离思潮和构想。在他们看来,现在各国的法币体系本质上就是中央银行控制货币发行数量,而数量多少很大程度上取决于政府相关部门的判断,特别是受制于政治家的判断。

当代的货币主权的基础都是政府和国家的信用。特别是,货币政策与财政政策的边界呈现模糊趋势。美国是典型国家。美联储并非完全独立,总统具有相当的影响力。这种分离思潮最后延伸出来的产品就是现在大家所讨论的以区块链作为基础的密码货币,或者说以比特币作为直接产品,而以区块链作为后台技术的一个体系。为了深入理解这件事,建议大家用一点时间来研究一下密码朋克运动,因为他们是开源的,和计算机开源运动,以及骇客群体有不可分割的联系。这个运动大概从70年代开始逐步发展,是伴随着互联网成长的一种运动。

第二件事,关于超主权货币这个思想,我们发现这是代表中国,但又不仅是中国,而更是代表新兴市场国家。他们要求改变原本的货币金融体系,但是提出的这个方案我们需要思考,为什么没有被接受?而方案提出方为什么没有把这个东西坚持下去?这是一个值得思考的问题。

第三件事,茶党运动。茶党运动的主体是美国中产阶级。他们要求减税,主张“大市场、小政府”,主张保卫和维护中产阶级利益。在当时的背景下,这一运动直接针对的是奥巴马刚刚上台的加税政策。

第四件事,华尔街运动。占领华尔街,它代表的是什么?代表的是草根,是民众,是社会的主体,是所有在全球化和金融危机之后受到最大伤害的主体。

在当时的历史条件下,这四个运动恰恰代表了四个反应,哪个胜出?谁也没有预见到的。到了今天,结果基本出来了,所有人不以为然的技术派,是所谓的密码朋克群体,是以中本聪和比特币为代表的比特币运动或者说区块链运动,不仅维持下来,而且产生日益扩大和深入的影响,从星星之火变成不可逆的态势。

第二个问题:在过去十年期间,全球货币金融生态发生了怎样的演变?演变分这样几个方面:

第一个方面,政策的演变。大家知道今天我们所谈的货币体系时间没有那么久远。我们今天的货币体系最早不过可以追溯到1944年的布雷顿森林会议。但是布雷顿森林会议的货币体系在尼克松时代发生了根本性的改变。尼克松关闭了美元和黄金的窗口,使货币制度彻底脱离了贵金属的束缚,货币的发行权完全由政府通过中央银行来实现。

那么从水门事件之前70年代算起,到今天为止,我们现在所说的货币体系不过40多年的历程。这样的货币体系体现出货币政策的重要性。在2008年金融危机之前,央行的货币政策,特别是美国央行的货币政策对世界金融体系的影响是显著的,甚至是至关紧要的。然而金融危机之后,货币政策的效益逐渐的下降,下降的基本特点和原因在于每一个国家都采取了所谓的货币宽松政策。在过去十年里,世界主要国家究竟释放了多少货币量?

至少是全世界GDP的一倍甚至两倍以上,中国是最多的。因此,这样的货币政策它有一系列的连锁效果,但是最根本的后果就是它导致了货币政策效益的下降。反过来它导致了所谓财政政策的上升。

第二个方面,结构性的演变。中国有很强的滞后,中国的家长和年轻人现在还认为投行都是有钱人的时候,投行的黄金时代早已悄然结束,商业银行的地位也是急剧下降。

第三个方面,运行机制的演变。我们这些年看到所有人张口讲基金闭口讲基金,就是讲银行、金融机构全面的崛起,就表示了传统的货币金融的组织架构也发生了变化,当然运行机制也发生了演变。中国盛传的所谓蒙代尔的“三角不可能”原本就不成立,现在就更不成立了。因为他的三个不可能中的一个是不存在的,就是固定汇率制度。所谓的后布雷顿森林会议和前布雷顿森林会议的差别就是世界的汇率制度从固定汇率进步到浮动汇率制度。

在过去十年,我们清楚地看到,原本稳定的和互动的利率、汇率和货币供给总量之间的关系,发生改变。最重要的指标就是创新名义负利率现象。在北欧国家,甚至出现银行给贷款方补贴利率的情况。倒退二三十年,这样的情况是不可想象的。为什么会这样?归根结底是因为2008年金融危机,实施货币宽松政策是全球性问题,流动性过剩是普遍情况。可以清楚地看到,源于政府的反危机,避免萧条的初衷,增加货币投放数量,最终传导到利率,而利率曾经是传统货币金融体系的核心所在。

当利率常态被改变,不可避免地导致影响和干扰资本市场和债券市场,以及外汇市场的预期,甚至经济增长预期。现在我们看不到利率像以前对投资产生那么强烈的影响,其结果就是现在不仅是物质生产产品过剩,而且是资本过剩。

第四个方面是技术性演变。我刚才跟山西一个朋友在聊天,讲到他现在有一个很大的新开发区,可遇到的问题是搞什么项目?资本从哪里来?这已经不再是80年代、90年代甚至2000年之后的金融生态和经济生态了。那么技术性的演变是很明显的,前几年大家谈的是金融科技,然后互联网金融的崛起,到现在区块链金融的诞生。

最后是主权性货币金融结构的调整。总的来讲,这影响的是一个世界的货币金融形态与结构,就是像我前面讲的新兴市场国家持有货币体系的影响力最大,发达国家特别是美国作出相应的反应,以及影响到我们今天所看到的贸易战,他们都有着深刻的历史联系。所谓的“三角不可能”,我说为什么不成立,就是在70年代以后,其中的一角,固定汇率制度已经不存在,所以它的前提有问题,当然就不对了。

这里一些结论,写在今天发给与会者的文章中,就是我写的《世界金融危机的十周年回顾与思考》。我把其中我认为更值得强调的四点写在这里:一是金融风险和金融危机的常态化;二是货币政策边际效益递减;三是传统和国际货币金融体系趋于解体。以区块链为基础的密码数字货币的崛起并形成了不可逆转的趋势;最后当然还有股票市场作为传统资本市场,过去的绝对主导地位正在被改变。

我着重说几句所谓的金融货币体系的“稳定性”内涵。如果把我们历史往后延伸,货币金融体系的基本特点就是它的非稳定性。只是在每个历史的时点中,我们都会误以为所存在的货币金融环境具有稳定性,其实是没有那么大的稳定性。以美国为例,仅仅从过去一百年间就完成了几次大的货币体系改革和演变。我们知道一百年前的美国已经有美联储,但是那时候谈不上货币政策,因为美联储本来是个私人金融机构。在美国已经有美联储的情况下,政府对货币金融有一定的政策影响的时候,美国的货币制度在一百年前是什么呢?是复本位制度,就是金和银同时支撑美国的货币体系。在一百年前的此时此刻,美国的每一个大区域都有一个央行的分行行长,就是所谓的美联储的分行行长,他们有权力签署美元。后来美国在1934年提高了美国白银的价格,这也影响了中国废弃了银元,建立了法币体系,这一切也不过80年左右的时间。

第三个问题,关于金融货币体系的未来前景。近二、三年的大量的事实说明,世界很快会进入到传统货币金融体系和数字货币金融体系并存的时代。这个时代已经开始了。数字金融体系可以分为政府主导的和政府不能主导的。我们现在看到前几天,9月10日,美国纽约金融服务部同时批准了两种基于以太坊发行的稳定币,分别为Gemini 公司发行的GUSD和Paxos公司发行的PSD,这是一个很大的举动,其实就是允许私人金融机构交易和美金对应的接受政府管制的数字货币。这件事对美国,乃至世界金融货币生态的长远影响,需要认真观察。可以这样思考:从现在开始,美国其实已经进入到21世纪的复本位时代,它的影响千万不能低估。

与此同时我们还会看到不受政府控制的加密数字货币的发展和基于区块链所谓的TOKEN的蔓延。针对这种情况、这种生态变化,我希望今天的与会者从更大的历史角度和更宏观的背景下去关注。一个绝对垄断的、以1971年8月15日作为时间节点的、纯粹由政府控制的、以政府信用作为基础的纸币时代,正在被一个新的21世纪的所谓的复本位时代所替代。在这个过程中,我们看到同时存在着两种信用,这是最深刻的变化。信用之一是建立在政府基础上,这是法币的特点;同样另外一个崛起的信用体系不是基于人与人之间的信任,而是基于区块链技术维系的一个信任体系。现在看来,谁也没有想到2008年金融危机十年之后的今天,会派生出这样一个结果。

最后,我想补充一点,为什么不早不晚,仅仅在世界金融危机之后的数个星期,即2008年11月中本聪就可以提出来P2P的那篇小论文?原因很简单,三个条件。条件一,技术完全成熟,包括数学、密码、互联网等技术的发展。条件二,深受2008年金融危机的刺激。金融危机刺激的核心后果是这个时代的精英发现谈了半天,他们有一个主权没有,就是财富主权。因为财富的尺度不在他们手里。条件三,大家认为所谓货币是可以非国家化的,而这个思想是可以被实践的。这样的一个历史环境就自然而然地在那样的时点中碰撞出这样的结果,我们至今不知道谁是中本聪,但是我们见过中本聪的主要成员,他们在当时做这样的事情和做了这样的创造,不是随机的,是自觉的,不是任意的,是认真的。

总之,在世界金融危机十周年之际,我们在这里讨论过去十年发生的相关重大事件,解读这些事件的背景和关系,我们会发现真实的历史过程远比我们以为的要精彩、要深刻。

来源:零壹财经

faceai: 一款入门级的人脸、视频、文字检测以及识别的项目.

$
0
0

English Doc

功能

  1. 人脸检测、识别(图片、视频)
  2. 轮廓标识
  3. 头像合成(给人戴帽子)
  4. 数字化妆(画口红、眉毛、眼睛等)
  5. 性别识别
  6. 表情识别(生气、厌恶、恐惧、开心、难过、惊喜、平静等七种情绪)
  7. 视频对象提取
  8. 图片修复(可用于水印去除)
  9. 图片自动上色
  10. 眼动追踪(待完善)
  11. 换脸(待完善)

查看功能预览↓↓↓

开发环境

  • Windows 10(x64)
  • Python 3.6.4
  • OpenCV 3.4.1
  • Dlib 19.8.1
  • face_recognition 1.2.2
  • keras 2.1.6
  • tensorflow 1.8.0
  • Tesseract OCR 4.0.0-beta.1

教程

OpenCV环境搭建

Tesseract OCR文字识别

图片人脸检测(OpenCV版)

图片人脸检测(Dlib版)

视频人脸检测(OpenCV版)

视频人脸检测(Dlib版)

脸部轮廓绘制

数字化妆

视频人脸识别

头像特效合成

性别识别

表情识别

视频对象提取

图片修复

其他教程

Ubuntu apt-get和pip源更换

pip/pip3更换国内源——Windows版

OpenCV添加中文

使用鼠标绘图——OpenCV

功能预览

绘制脸部轮廓

绘制脸部轮廓


人脸68个关键点标识

人脸68个关键点标识


头像特效合成

头像特效合成


性别识别

性别识别


表情识别

表情识别


数字化妆

视频人脸识别


视频人脸检测


视频人脸识别


视频人脸识别


图片修复


图片自动上色


技术方案

技术实现方案介绍

人脸识别:OpenCV / Dlib

人脸检测:face_recognition

性别识别:keras + tensorflow

文字识别:Tesseract OCR

TODO

换脸——待完善

眼睛移动方向检测——待完善

Dlib性能优化方案

Dlib模型训练方法

Tesseract模型训练方法

贡献者名单(特别感谢)

archersmind

rishab-sharma

深入浅出人脸识别原理 · Martin's Blog

$
0
0

深入浅出人脸识别

in

简介

前不久 Iphone X发布,革命性的取消了 TouchID(指纹识别),而添加了更酷的 FaceID(人脸识别)模块, FaceID不简单的运用在解锁上,还可以在支付,表情等场景中应用,给开发者带来更酷更丰富的应用, Iphone X在多个硬件传感器的加持下,可以采集3万个点来感知用户的面部特征。
animoji

我们知道人脸识别在这几年应用相当广泛,人脸考勤,人脸社交,人脸支付,哪里都有这黑科技的影响,特别这几年机器学习流行,使得人脸识别在应用和准确率更是达到了一个较高的水准。

下面将带着大家揭秘下这项黑科技的原理。

人脸识别流程:

人脸识别是由一系列的几个相关问题组成的:

  1. 首先找到一张图片中的所有人脸。
  2. 对于每一张脸来说,无论光线明暗或面朝别处,它依旧能够识别出是同一个人的脸。
  3. 能够在每一张脸上找出可用于他人区分的独特之处,比如眼睛多大,脸有多长等等。
  4. 最后将这张脸的特点与已知所有人脸进行比较,以确定这个人是谁。

第一步:找出所有的面孔

很显然在我们在人脸识别的流程中得首先找到图片中的人脸。我们在使用手机或相机拍照时都会有人像模式,它能轻松的检测出人脸的位置,帮助相机快速对焦。

auto_focus

相机对焦我们得感谢 保罗·比奥拉(Paul Viola)和迈克尔·琼斯(Michael Jones)在2000年 发明了一种能够快速在廉价相机上运行的 人脸检测方法,人脸检测在相机上的应用才成为主流。然而现在我们有更可靠的解决方案 HOG(Histogram of Oriented Gradients) 方向梯度直方图,一种能够检测物体轮廓的算法。

首先我们把图片灰度化,因为颜色信息对于人脸检测而言没什么用。
audrey_hepburn

我们分析每个像素以及其周围的像素,根据明暗度画一个箭头,箭头的指向代表了像素逐渐变暗的方向,如果我们重复操作每一个像素,最终像素会被箭头取代。这些箭头被称为 梯度(gradients),它们能显示出图像从明亮到黑暗流动的过程。

hog_directo

分析每个像素对我们来说有点不划算,因为它太过细节化了,我们可能会迷失在像素的海洋里,我们应该从更高的角度观察明暗的流动。

为此我们将图像分割成16x16像素的小方块。在每个小方块中,计算出每个主方向有多少个剃度(有多少指向上,指向右上,指向右等)。然后用指向性最强的那个方向箭头来代替原来那个小方块。
face_fhog_filters

最终结果,我们把原始图像转换成一个非常简单的HOG表达形式,它可以很轻松的捕获面部的基本结构。

为了在HOG图像中找到脸部,我们需要做的是,与已知的一些HOG图案中,看起来最相似的部分。这些HOG图案都是重其他面部训练数据中提取出来的。

第二步:脸部的不同姿势

我们已经找出了图片中的人脸,那么如何鉴别面朝不同方向的人脸呢?

对于电脑来说朝向不同的人脸是不同的东西,为此我们得适当的调整扭曲图片中的人脸,使得眼睛和嘴总是与被检测者重叠。

为了达到目的我们将使用一种 面部特征点估计(face landmark estimation)的算法。其实还有很多算法都可以做到,但我们这次使用的是由 瓦希德·卡奇米(Vahid Kazemi)和约瑟菲娜·沙利文(Josephine Sullivan)在 2014 年发明的方法

这一算法的基本思路是找到68个人脸上普遍存在的点(称为 特征点, landmark)

landmark_68_point

  • 下巴轮廓17个点 [0-16]
  • 左眉毛5个点 [17-21]
  • 右眉毛5个点 [22-26]
  • 鼻梁4个点 [27-30]
  • 鼻尖5个点 [31-35]
  • 左眼6个点 [36-41]
  • 右眼6个点 [42-47]
  • 外嘴唇12个点 [48-59]
  • 内嘴唇8个点 [60-67]

有了这68个点,我们就可以轻松的知道眼睛和嘴巴在哪儿了,后续我们将图片进行旋转,缩放和错切,使得眼睛和嘴巴尽可能的靠近中心。

现在人脸基本上对齐了,这使得下一步更加准确。

第三步:给脸部编码

我们还有个核心的问题没有解决, 那就是如何区分不同的人脸。

最简单的方法就是把我们第二步中发现的未知人脸与我们已知的人脸作对比。当我们发现未知的面孔与一个以前标注过的面孔看起来相似的时候,就可以认定他们是同一个人。

我们人类能通过眼睛大小,头发颜色等等信息轻松的分辨不同的两张人脸,可是电脑怎么分辨呢?没错,我们得量化它们,测量出他们的不同,那要怎么做呢?

测量人脸的最可靠的方法

如何测量人脸的数值呢?耳朵大小?鼻子长度?眼睛的颜色?

实际上,对于人脸这些信息很容易分辨,可是对于计算机,这些值没什么价值。实际上最准确的方法是让计算机自己找出他要收集的测量值。深度学习比人类更懂得哪些面部测量值比较重要。

所以,解决方案是训练一个深度卷积神经网络,训练让它为脸部生成128个测量值。

每次训练要观察三个不同的脸部图像:

  1. 加载一张已知的人的面部训练图像

  2. 加载同一个人的另一张照片

  3. 加载另外一个人的照片

然后,算法查看它自己为这三个图片生成的测量值。再然后,稍微调整神经网络,以确保第一张和第二张生成的测量值接近,而第二张和第三张生成的测量值略有不同。

我们要不断的调整样本,重复以上步骤百万次,这确实是个巨大的挑战,但是一旦训练完成,它能攻轻松的找出人脸。

庆幸的是 OpenFace上面的大神已经做完了这些,并且他们 发布了几个训练过可以直接使用的网络,我们可以不用部署复杂的机器学习,开箱即用,感谢开源精神。

d_value_128

这128个测量值是什么鬼?

其实我们不用关心,这对我们也不重要。我们关心的是,当看到同一个人的两张不同照片时,我们的网络需要能得到几乎相同的数值。

第四部:从编码中找出人的名字

最后一步实际上是最简单的一步,我们需要做的是找到数据库中与我们的测试图像的测量值最接近的那个人。

如何做呢,我们利用一些现成的数学公式,计算两个128D数值的 欧氏距离
euclidean_distance

哈,这样我们得到一个欧式距离值,系统将给它一个认为是同一个人欧氏距离的阀值,即超过这个阀值我们就认定他们是 同 (失) 一 (散) 个 (兄) 人 (弟)

人脸识别就这样达成啦,来来我们再回顾下流程:

  1. 使用HOG找出图片中所有人脸的位置。
  2. 计算出人脸的68个特征点并适当的调整人脸位置,对齐人脸。
  3. 把上一步得到的面部图像放入神经网络,得到128个特征测量值,并保存它们。
  4. 与我们以前保存过的测量值一并计算欧氏距离,得到欧氏距离值,比较数值大小,即可得到是否同一个人。

人脸识别应用场景

人脸识别分两大步骤,人脸检测和人脸识别,它们应用场景也各不相同。

Face-Tracking-Android-Application-Structure

人脸检测目的是找出人脸,得到人脸的位置,我们可以在美颜,换肤,抠图,换脸 的一些场景中使用到它。我们可以通过系统API调用相机完成对预览针的实时渲染,那些看上去的黑科技我们也可以玩啦。
change_face

而人脸识别则可以在会员,支付等场景中使用,带给用户更酷的使用场景,快来试试吧。








引用:

https://en.wikipedia.org/wiki/Viola%E2%80%93Jones_object_detection_framework
https://medium.com/@ageitgey/machine-learning-is-fun-part-4-modern-face-recognition-with-deep-learning-c3cffc121d7
https://en.wikipedia.org/wiki/Histogram_of_oriented_gradients
http://www.csc.kth.se/~vahidk/papers/KazemiCVPR14.pdf

App Store Top 1000 关键词分析

$
0
0

做这个分析的主要目的是分析用户的搜索习惯及用户的需求方向,寻找可能的机会。以下分析是7月初进行的,数据比较老,供参考。

在Top 1000的关键词中,82% 是品牌词,足见品牌(口碑)对应App的下载量还是非常的重要。而另外18%的非品牌词也展现了一些打造品牌的机会(用户在该品类下还没有产生思维定势,新的App还存在一定的机会),以下为另外18%的非品牌关键词:

VPN

有需求存在,涉及到相关的法律政策,很难有较好的发展。

排名关键字热度榜首应用热度搜索结果数
13vpn9849VPN-极速高效稳定的VPN网络加速神器1149
358vpn翻墙软件免费7557VPN – 无限流量,高速网络vpn42
596vpn免费版7050VPN-极速高效稳定的VPN网络加速神器1166
688极速vpn6926VPN-极速高效稳定的VPN网络加速神器1167

理财

理财的需求没有被很好的满足,这个行业鱼龙混杂,随着P2P的不断爆雷,其他方向的理财或许是一个方向,目前这个行业存在较大的机会。

排名关键字热度榜首应用热度搜索结果数
31理财9450京东金融-新人领888元大礼包1410
66期货8903期货直播吧-香港全球期货投资理财专家1741
181理财平台8088金碗理财-高收益短期理财投资平台1601
251理财软件7849有理树理财—短期投资理财的手机理财软件1530
270投资7785立信投资-银行存管对接中1555
314外汇7666外汇牌价-全球行情分析软件1693
642理财工具6991八条鱼理财-懒人投资理财神器1626
536短期理财7146正经事理财-短期投资理财银行存管1508
578金融7083聪明钱包-简单的金融投资理财软件1685
613理财产品7028有理树理财—短期投资理财的手机理财软件1507
626外汇交易7010金道外汇投资-全球期货现货交易平台1691
673投资理财6952铜掌柜理财-高收益合规p2p金融投资理财平台1533
676股票6948股市1631
762理财管家6843帝象理财1586
872贵金属6714原油交易宝-原油贵金属期货投资平台1705
932期货交易6639期货交易-全球期货资讯专业平台1573
940外汇投资6634金道外汇投资-全球期货现货交易平台1691
986活期理财6587花生米富-银行存管,理财更安全1360

贷款

与理财相对,有的人钱多,有的人钱少,小额贷款存在一定量的需求,此类风险较大,贷款的核心是尽可能多贷,所以会出现用户尽可能的下载更多的app,进行拆东墙补西墙的操作等,慎。

排名关键字热度榜首应用热度搜索结果数
41贷款9220贷款钱包-30分钟到账的贷款app1634
102借钱8543马上借钱-分期贷款现金借钱借款平台1597
232闪电借款7896闪电借款领先版-小额现金借款1709
345贷款平台7591贷款钱包-借钱快现金贷款平台1665
346现金贷7587现金贷-小额分期借钱贷款平台1713
398贷款app7448闪贷钱包—小额分期贷款的现金贷款APP1650
423借款7388借款王-身份证就能手机借款的app1700
515小额贷款7184小额贷款-分期贷款急借钱平台1717
805现金贷款6778现金借款-手机极速借款app1727
8136772任性贷-有信用即刻下3000-10万的贷款借钱、网贷分期APP1709
840借款平台6752秒借款-30000元2小时到账的闪电借款平台1706
897借贷6685借贷宝 – 手机打借条, 体面有保障1676
938小额借款6635小额借款-极速借钱小额借款贷款软件1723
973借钱软件6597轻松借-小额分期贷款借钱借贷软件1657

记账

钱不够花的引导出来的还有一个需求是:记账。记账属于工具类的产品,难点在获利。

排名关键字热度榜首应用热度搜索结果数
656账本6972鲨鱼记账-3秒钟快速记账手机助手765
756记账软件6851鲨鱼记账-3秒钟快速记账手机助手1694

彩票

没钱的状况下,还会考虑一夜暴富,买彩票也存在一定的需求,由于相关法律法规,想要开展互联网彩票非常的困难。

排名关键字热度榜首应用热度搜索结果数
46六合彩9113重庆时时彩-六合彩资料大全好运彩票出品780
152彩票8236网易彩票-双色球体彩竞彩预测投注开奖801
503六合宝典7203奖多多彩票-新人注册送108元765
793快三6795天天中彩票1028
827福利彩票6761福利彩票-高低频彩票开奖平台616
228抓娃娃7920抓娃娃机游戏-天天都想玩的在线抓娃娃913

游戏

游戏类的关键词会出现较多的品类词,可能原因是用户想要寻找新的游戏导致的,目前也可以看到益智类的游戏品类词较多。

排名关键字热度榜首应用热度搜索结果数
196游戏8016密室逃脱:逃出神秘宫殿1644
301游戏大全7694神庙酷跑 – 跑步游戏大全1575
460手游7302龙骑世界-国民级热血PK手游1536
987单机6587斗地主 单机斗地主单机版全民棋牌比赛1409
34吃鸡9359绝地求生:刺激战场1678
123吃鸡游戏8377绝地求生:刺激战场1422
167修仙8142焚仙-逍遥侠侣修仙手游1463
799仙侠6787全民寻仙-大型3D国民修仙热恋手游1485
937足球6635足球大师黄金一代1248
832赛车6756狂野飙车8:极速凌云1147
905炫舞6673QQ炫舞1845
147百家乐8247百家讲坛大全-手机电子书免费书城(得到一个有声小说阅读器管家)690
279牛牛7762金爵棋牌-真人棋牌游戏900
327麻将7638腾讯欢乐麻将全集1159
494欢乐炸金花7220炸金花 – 全民炸金花天天欢乐炸金花826
766棋牌6836金爵棋牌-真人棋牌游戏967
877五子棋6710腾讯欢乐五子棋1415
830象棋6758中国象棋—楚汉争霸,策略小游戏1664
974卡牌游戏6595三国·军师-全自由实时策略卡牌手游1442
944数独6630数独—经典数字趣味谜题桌游1686
149弹球8245物理弹球1685
239消消乐7878开心消消乐®1702
394钢琴7460完美钢琴 – 智能键盘, 海量曲谱, 远程连线1887
447塔防7325塔防三国志 – TD塔防类卡牌手游1645
764塔防游戏6837塔防三国志 – TD塔防类卡牌手游1676
454捕鱼游戏7313熊猫捕鱼-捕鱼高手的街机捕鱼游戏1031
618跳一跳7016欢乐跳跳跳487
509拼图7190简拼-文艺范视频拼图·美图P图神器1935
748拼图软件6858海报拼图软件-照片组合器1872
545儿童游戏71402-6岁宝宝爱数学-家庭育儿益智启蒙教育必备的免费儿童游戏1961
964连连看6602宠物连连看经典版-最新免费益智爱消除单机小游戏1843

系统工具

工具类的需求可能原因是原生类的工具不能很好的满足需求或功能不够强大。核心是产品要在功能点上有亮点。

排名关键字热度榜首应用热度搜索结果数
50浏览器9051QQ浏览器 – 用腾讯王卡全网免流1518
333浏览器IPHONE版7625浏览器iPhone版-安全版1537
201天气8001天气1749
362天气预报7552天气预报808
706天气预报苹果版6907天气 – 精准预报实时天气变化808
238万年历7879万年历-值得信赖的日历黄历查询工具914
886万年历黄历6694万年历-值得信赖的日历黄历查询工具908
292日历7732日历1857
258计算器7815计算器1855
865计算机6724计算器 – 默认计算器1870
269翻译7792有道翻译官-出国旅游英语日语翻译必备旅行APP1790
531翻译软件7153有道翻译官-出国旅游英语日语翻译必备旅行APP1216
548录音7134专业录音—移动录音专家。1292
912变声器6662Voice Changer – 变声神器 声音特效器172
737网络电话6876微微电话-通话最好的网络电话1440
639指南针6993指南针1297
498备忘录7215备忘录990
570输入法7096百度输入法-语音输入法1181
550音乐播放器7133酷狗音乐-《无限歌谣季》官方音乐APP1289
501K歌7206全民K歌 – K歌短视频互动平台1370
816视频播放器6768爱奇艺万能播放器 – 观看本地视频神器1363
845播放器6745播放器OPlayer Lite – 视频播放器1352
564空调遥控器苹果版7116遥控大师68
858空调万能遥控6728遥控精灵-手机万能遥控器147
412邮箱7413邮件938
115壁纸8422壁纸 : 高清手机主题桌面动态墙纸1529
448动态壁纸7323我的动态壁纸427
767壁纸大全6836壁纸大全 : 百万超高清壁纸库977
483铃声7238铃声多多-手机铃声大全1351
530手机铃声7153铃声多多-手机铃声大全582
854苹果手机铃声6735铃声多多-手机铃声大全531
213相机7961B612咔叽 – 全球自拍达人的新圣地1720
462p图7297P图神器-图片编辑拼图修图制作软件1765
637证件照6994智能证件照-自带美颜拍摄最美证件照272
796ps6789Adobe Photoshop Express1807
953美颜6615BeautyCam美颜相机1612
958拍照6611B612咔叽 – 全球自拍达人的新圣地1810
264视频编辑7801爱剪辑手机版 – 专业视频剪辑编辑制作1391
481视频剪辑7243视频剪辑 – 影片裁剪,视频编辑制作636
665同步助手6961QQ同步助手-手机通讯录安全备份管家1356
820电池医生6766电池医生-专业电池状态管理576
908wifi6668WiFi钥匙-安全上网管家1587

微信分身

工作微信和生活微信分开是很多人存在需求,特别是微商类的。

排名关键字热度榜首应用热度搜索结果数
119微信分身版苹果版免费8405商信 – 微商达人必备助手102
302微信分身版苹果版7691商信 – 微商达人必备助手110
875微信分身6711商信 – 微商达人必备助手927

小说、视频、新闻

用户闲的时间还是比较多,小说、资讯等还是存在一些发展空间。

排名关键字热度榜首应用热度搜索结果数
56免费小说8958小说大全 – 小说阅读器大全1587
70小说8861QQ阅读-拥有海量热门小说、漫画的电子书阅读器1460
351免费小说阅读器7571小说大全 – 小说阅读器大全1571
552看书神器7132小说阅读器 – 畅读小说阅读大全1520
978小说大全6592热门小说大全—小说阅读器之最新电子书小说1480
591阅读7063小说阅读器 – 畅读小说阅读大全1611
660听书6966喜马拉雅FM「听书社区」电台有声小说相声评书1612
339漫画7606快看漫画-高清正版漫画流畅看1497
800电子书6786QQ阅读-拥有海量热门小说、漫画的电子书阅读器1637
929收音机6642FM收音机-轻松收听全国广播电台594
399视频7445视频1620
603NBA7040NBA LIVE-EA出品 5v5真操控篮球手游1647
290直播7737腾讯视频-创造101全网独播1345
502电视直播7204天天看电视直播1091
728韩剧6888韩剧TV-追剧女生大本营1557
160音乐8195音乐1798
241影视大全7876今日影视大全-电影电视剧视频播放器741
120新闻8402一点资讯-热门新闻头条资讯和娱乐视频1797
245头条7859今日头条 – 你关心的,才是头条1693
353资讯7569一点资讯-热门新闻头条资讯和娱乐视频1722
522头条新闻7166今日头条 – 你关心的,才是头条1606

交友

原始需求还是非常的旺盛啊

排名关键字热度榜首应用热度搜索结果数
211约炮7970约单-有你想要的服务1312
906同城约炮6669成人之美 – 成人交友社区807
275交友7769他趣 – 成人交友情趣体验社1359
422闲聊7392闲聊-兴趣部落1365

购物返利

此部分有一定的发展机会,也容易赚钱,可以好好的发展。

排名关键字热度榜首应用热度搜索结果数
869购物6719大众点评-会生活的人在大众点评1609
926全球购6644全球购-全球正品海淘代购特卖商城1366
231返利app7897返利-专注网购省钱的APP1599
326淘宝优惠券7640省钱达人-领淘宝优惠券的省钱APP825
439返利7348返利-专注网购省钱的APP1587
341二手车7602瓜子二手车直卖网-二手车卖车买车交易平台1899

生活服务

主要是O2O型的服务,比较适合大型的公司进行发展。

排名关键字热度榜首应用热度搜索结果数
458租房7305蚂蚁租房 – 掌上租房必备1489
466酒店7283艺龙旅行-订酒店机票旅游攻略1879
584租车7072神州租车-万辆新车自由行1934
587共享汽车7065GoFun出行-首汽共享汽车320
823旅游6765驴妈妈旅游-订景点门票,选机票酒店1715
801外卖6785美团外卖-外卖订餐,送啥都快1739
975装修6595好好住 – 你的家居装修指南1739
495兼职7220兼客兼职-年轻人兼职找工作必备1653
526招聘7159Boss直聘-招聘求职找工作神器1889
790找工作6798Boss直聘-招聘求职找工作神器1793
475英语7264懒人英语—每日英语听力趣配音秀(基础音标学习雅思扇贝口语)1837
590运动软件7064Keep – 自由运动场1847
488违章查询7230车轮(车轮查违章)2018全国违章代缴助手1423
862公交车实时查询6726车来了-精准的实时掌上公交地铁103
615菜谱7023菜谱大全-小白学做菜做饭必备烹饪助手1774
863汽车6726汽车之家-2.2亿家人都在用的汽车App1726
427大姨妈7377大姨妈月经期助手-女性生理备孕健康工具社区1118
774发现精彩6830信用卡办卡 – 中国的银行手机银行信用卡快速申请攻略111

 

The post App Store Top 1000 关键词分析 appeared first on 标点符.

No related posts.

使用dlib中的深度残差网络(ResNet)实现实时人脸识别 - supersayajin - 博客园

$
0
0

opencv中提供的基于haar特征级联进行人脸检测的方法效果非常不好,本文使用dlib中提供的人脸检测方法(使用HOG特征或卷积神经网方法),并使用提供的深度残差网络(ResNet)实现实时人脸识别,不过本文的目的不是构建深度残差网络,而是利用已经训练好的模型进行实时人脸识别,实时性要求一秒钟达到10帧以上的速率,并且保证不错的精度。opencv和dlib都是非常好用的计算机视觉库,特别是dlib,前面文章提到了其内部封装了一些比较新的深度学习方法,使用这些算法可以实现很多应用,比如人脸检测、车辆检测、目标追踪、语义分割等等。由于这两个库相应的都包含了C++和Python的版本,而Python的配置和相对使用起来更加简单,因此这篇文章主要通过Python来实现。

先上测试的识别效果,第一张识别吴恩达和Bengio,后者我没有打标签所以识别的是“other”;另外一张gif 是识别梁朝伟、刘德华和一个女主持的过程,本地库中没有存储女主持的图片。

                                                      

              

 

因为博客园不方便上传本地视频,所以用的gif显示效果图,源视频要比gif清楚,640X480像素大小,总的来说效果识别的效果还不错。

一、准备

(1)需要安装opencv和dlib的Python库,之前的一篇文章提到了怎样安装: http://www.cnblogs.com/supersayajin/p/8446685.html;如果你有GPU并且开启了加速,那么实现的人脸识别程序速度非常快,可以满足实时性,以我运行的结果来看,检测+识别640X480像素的视频流一秒钟大约十几帧;如果你没有GPU那么速度就会很慢了,而且在检测阶段不能使用卷积神经网络的方法了,否则检测一帧数据可能需要几秒甚至几十秒:)

(2)需要一个和PC连接的摄像头;在本文中使用的是串口的摄像头,笔记本电脑集成的摄像头就是串口的,opencv中提供了直接获取串口摄像头的接口,非常方便使用;如果是网口的摄像头那么就要看摄像头提供方,比如大华、海康他们的摄像头可能会提供官方的SDK,如果有接口那是最好;或者,如果摄像头支持RTSP协议,opencv也可以通过RTSP协议获取摄像头的数据;否则可能就要写一套socket通信来实现数据传输,这个不在本文范围之内,默认使用的是串口的摄像头。

二、策略

人脸识别分为人脸检测和识别两个阶段,人脸检测会找到人脸区域的矩形窗口,识别则通过ResNet返回人脸特征向量,并进行匹配。

(1)人脸检测阶段。人脸检测算法需要用大小位置不同的窗口在图像中进行滑动,然后判断窗口中是否存在人脸。在深度学习之前的主流方法是特征提取+集成学习分类器,比如以前火热的haar特征+adaboost级联分类器,opencv中实现的人脸检测方法就采用了这种,不过实验结果来看,这种检测方法效果很不好,经常误检测人脸,或者检测不到真实的人脸;dlib中使用的是HOG(histogram of oriented gradient)+ 回归树的方法,使用dlib训练好的模型进行检测效果要好很多。dlib也使用了卷积神经网络来进行人脸检测,效果好于HOG的集成学习方法,不过需要使用GPU加速,不然程序会卡爆了,一张图片可能几秒甚至几十秒。

(2)识别阶段。识别也就是我们常说的“分类”,摄像头采集到这个人脸时,让机器判断是张三还是其他人。分类分为两个部分:

  • 特征向量抽取。本文用到的是dlib中已经训练好的ResNet模型的接口,此接口会返回一个128维的人脸特征向量。
  • 距离匹配。在获取特征向量之后可以使用欧式距离和本地的人脸特征向量进行匹配,使用最近邻分类器返回样本的标签。

根据以上,识别的大致过程如下:

图1 人脸识别分类过程

 

对于图1中的获取人脸特征向量,其过程如下:

图2 获取人脸特征向量过程

用简单的话总结,整个过程分为两个阶段,本地存储已标记人脸数据;识别阶段把从摄像头读取的人脸和本地进行匹配,得到分类结果。

三、程序实现

(1)构建本地人脸特征向量库,并且打标签。

首先加载需要的python库:

import dlib
import numpy as np
import cv2
import os
import json

 

然后加载模型参数:

detector = dlib.cnn_face_detection_model_v1('mmod_human_face_detector.dat')
sp = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
facerec = dlib.face_recognition_model_v1('dlib_face_recognition_resnet_model_v1.dat')

 

上面代码中的模型参数可以到这里下载: http://dlib.net/files/。detector是使用卷积神经网络(CNN)进行人脸检测的检测算子,当然如果你使用CNN的话需要使用GPU加速,否则速度会超级慢。也可以使用另一种方法,即HOG特征级联分类的检测方法,效果略差于CNN。变量sp,使用预测算子获取得到的人脸区域中的五官的几何点区域,这里加载的是68特征点的landmark模型;然后facerec会得到ResNet模型,He Kaiming(2009年和2015的CVPR best paper作者)提出的方法的一个实现,这里训练模型已经给出,因此不需要自己手动去训练了。

最后,对某个目录中的所有图片进行处理,处理的方式是一张一张地读取某个目录中的图片,每读取一张就检测人脸,如果存在人脸就使用ResNet的接口获取人脸特性向量,保存到事先准备好的矩阵中,并且按照文件名存取标签,完了之后把所有的人脸特征向量和标签都存到本地的文本文件中。注意这里给图片打标签的方式,我把每张图片命名为标签名+下划线+序号+点号+后缀名的形式,标签名是手动命名的标记名称,序号用以区分同一类中的第几张。以下是demo中存放的部分图片:

 

也有很多其他的方法打标签,这里不多举例。

复制代码
imagePath = 'LocalImage/'                                                                           #图像的目录
data = np.zeros((1,128))                                                                            #定义一个128维的空向量data
label = []                                                                                          #定义空的list存放人脸的标签

for file in os.listdir(imagePath):                                                                  #开始一张一张索引目录中的图像
    if '.jpg' in file or '.png' in file:
        fileName = file
        labelName = file.split('_')[0]                                                              #获取标签名
        print('current image: ', file)
        print('current label: ', labelName)
        img = cv2.imread(imagePath + file)                                                          #使用opencv读取图像数据
        if img.shape[0]*img.shape[1] > 500000:                                                      #如果图太大的话需要压缩,这里像素的阈值可以自己设置
            img = cv2.resize(img, (0,0), fx=0.5, fy=0.5)
        dets = detector(img, 1)                                                                     #使用检测算子检测人脸,返回的是所有的检测到的人脸区域
        for k, d in enumerate(dets):
            rec = dlib.rectangle(d.rect.left(),d.rect.top(),d.rect.right(),d.rect.bottom())
            shape = sp(img, rec)                                                                    #获取landmark
            face_descriptor = facerec.compute_face_descriptor(img, shape)                           #使用resNet获取128维的人脸特征向量
            faceArray = np.array(face_descriptor).reshape((1, 128))                                 #转换成numpy中的数据结构
            data = np.concatenate((data, faceArray))                                                #拼接到事先准备好的data当中去
            label.append(labelName)                                                                 #保存标签
            cv2.rectangle(img, (rec.left(), rec.top()), (rec.right(), rec.bottom()), (0, 255, 0), 2)       #显示人脸区域
        cv2.waitKey(2)
        cv2.imshow('image', img)

data = data[1:, :]                                                                                  #因为data的第一行是空的128维向量,所以实际存储的时候从第二行开始
np.savetxt('faceData.txt', data, fmt='%f')                                                          #保存人脸特征向量合成的矩阵到本地

labelFile=open('label.txt','w')                                      
json.dump(label, labelFile)                                                                         #使用json保存list到本地
labelFile.close()

cv2.destroyAllWindows()                                                                             #关闭所有的窗口
复制代码

 

上面的代码中,会索引imagePath这个存放图像的目录;然后定义一个128维的空向量data,在后续获取每一张人脸特征向量的时候可以往这个向量后面追加,即data的每一行是一个样本的特征向量;然后定义一个list来存储标签。之后开始索引某个目录下所有的图片文件。注意我这里用的是opencv的接口读取图像,也可以使用其他的图像读取接口,比如dlib自带的或者PIL接口中的,都可以使用,不过重要的是接口一定要统一,因为每个接口读取图片转成矩阵的数值可能会有差异。然后使用前面定义的测算子开始检测人脸,返回的是dlib中的一个数据结构,这个数据结构存储了所有检测到的人脸区域信息,对每个检测到的人脸区域获取landmark,并且调用深度残差模型的接口获取128维的人脸特征向量,之后我们把这个人脸向量存储到data中去,这里使用numpy中提供的concatenate方法进行拼接,同时把标签添加到label列表中去。最后,因为data事先定义的是一个128维的空向量,之后利用concatenate方法进行拼接得到,我们需要抛弃第一行;最后把得到的人脸特征和标签存储到本地文件。

这里使用的是CNN进行人脸检测,如果你没有GPU,或者你有GPU但没有进行GPU的配置,那么速度巨慢,此时你可以使用传统的HOG特征+级联分类的方法,不过效果没有CNN的好。这时代码的第6行中模型需要替换成:

detector = dlib.get_frontal_face_detector()

 

其余的基本保持不变。 

以上的代码可以直接运行,运行之后会检测所有的图像,类似于:

并且存取得到本地的人脸特征向量库和标签:

 

(2)实时读取摄像头进行人脸识别

在(1)中我们已经得到了本地的打过标签的人脸特征向量,这一部分是实现读取摄像头实时识别。首先加载需要的python库:

import dlib
import numpy as np
import cv2
import json

 

然后加载神经网络模型:

detector = dlib.cnn_face_detection_model_v1('mmod_human_face_detector.dat')
sp = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
facerec = dlib.face_recognition_model_v1('dlib_face_recognition_resnet_model_v1.dat')
threshold = 0.54

其中threshold是人脸识别的阈值,当测试图片和本地图片欧式距离最近的值大于这个值的时候,我们认为不属于本都图片的任何一个类别。然后定义最近邻分类器:

复制代码
def findNearestClassForImage(face_descriptor, faceLabel):
    temp =  face_descriptor - data
    e = np.linalg.norm(temp,axis=1,keepdims=True)
    min_distance = e.min() 
    print('distance: ', min_distance)
    if min_distance > threshold:
        return 'other'
    index = np.argmin(e)
    return faceLabel[index]
复制代码

 

 当距离值大于threshold的时候我们返回标签“other”,否则返回本地的标签,你可以根据实际情况来设置这个阈值。题外话,安全阈值很依赖于具体的场合,比如安检、银行里进行人脸验证、iPhone解锁,这些对安全要求很高的场合需要比较小的threshold来保证安全,在嫌犯追踪的时候,需要比较大的threshold以保证由嫌疑的人不会漏过。

然后是读取图像进行识别的函数:

复制代码
def recognition(img):
    dets = detector(img, 1)
    for k, d in enumerate(dets):
        print("Detection {}: Left: {} Top: {} Right: {} Bottom: {}".format(
            k, d.rect.left(), d.rect.top(), d.rect.right(), d.rect.bottom()))
        rec = dlib.rectangle(d.rect.left(),d.rect.top(),d.rect.right(),d.rect.bottom())
        print(rec.left(),rec.top(),rec.right(),rec.bottom())
        shape = sp(img, rec)
        face_descriptor = facerec.compute_face_descriptor(img, shape)        
        class_pre = findNearestClassForImage(face_descriptor, label)
        print(class_pre)
        cv2.rectangle(img, (rec.left(), rec.top()+10), (rec.right(), rec.bottom()), (0, 255, 0), 2)
        cv2.putText(img, class_pre , (rec.left(),rec.top()), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,255,0), 2, cv2.LINE_AA)

    cv2.imshow('image', img)
复制代码

 

 最后是实时读取摄像头图像,并且进行识别的过程:

复制代码
labelFile=open('label.txt','r')
label = json.load(labelFile)                                                   #载入本地人脸库的标签
labelFile.close()
    
data = np.loadtxt('faceData.txt',dtype=float)                                  #载入本地人脸特征向量

cap = cv2.VideoCapture(0)
fps = 10
size = (640,480)
fourcc = cv2.VideoWriter_fourcc(*'XVID')
videoWriter = cv2.VideoWriter('video.MP4', fourcc, fps, size)

while(1):
    ret, frame = cap.read()
    #frame = cv2.resize(frame, (0,0), fx=0.5, fy=0.5)
    recognition(frame)
    videoWriter.write(frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()
videoWriter.release()
cv2.destroyAllWindows()
复制代码

 在上面的代码中为了展示检测的效果,我用opencv的接口把图像保存到了视频当中。识别效果截图:

 

四、总结

利用已有的计算机视觉库可以实现很多好玩和有用的应用,本文只是粗略地展示了一个进行实时人脸识别的demo,还有很多可以改善的点来提高精度和效率,比如人脸受角度、表情影响很大,或者需要处理速度要求更高的场景;同时图像类别规模很大的情况下如何保证效果,如何优化这些都是难点。另外dlib中的提供的这些模型都是已经训练好的,我们可以到官方demo下载,demo给出了在一些benchmark中的效果,也可以自己训练得到这些模型,当然前提是你需要有GPU,并且要求很大量的数据以及丰富的调参经验,这些也都是深度学习中的点~

使用 Spark, LSH 和 TensorFlow 检测图片相似性 | 雷锋网

$
0
0

雷锋网(公众号:雷锋网)按:本文为 AI 研习社编译的技术博客,原标题 Detecting image similarity using Spark, LSH and TensorFlow,作者为 Andrey Gusev(Pinterest 工程师,内容质量分析师)。

翻译 | 沈波  张天航    校对 |  余杭    整理 |  凡江

作为一个视觉数据处理平台,拥有从海量图片中学习并理解其内容的能力是非常重要的。为了检测几近重复的相似图片,我们使用了一套基于 Spark 和 TensorFlow 的数据流处理系统——NearDup。这套系统的核心由一个使用 Spark 实现的批量化 LSH(locality-sensitive hashing,局部敏感哈希)搜索器和一个基于 TensorFlow 的分类器构成。这个数据流处理系统每天能够比较上亿个分析对象,并渐进式地完成各个图像类别的信息更新。在本文中,我们将讲解如何使用这项技术更好地理解海量图片内容,从而使得我们产品前端界面的推荐内容和搜索结果具有更高的信息准确性、更大的数据密度。  

简介 

我们将我们图片库中的所有图片划分为不同的图片类,每一类都由几近相同(以人类观察员的判断为标准)的图片组成。这种分类标准有一定的主观性,为了给你一个感性认识,下图展示了一些按照 NearDup 阈值进行图片分类的例子。注意,这些相似图片不一定来自同一图像源(参见右下图),也不一定有相同的背景(参见左下图)。同时,图像中可能包含一定的几何扭曲(参见左上图),或者旋转、剪切和翻转变化(见中心图和右上图)。

使用 Spark, LSH 和 TensorFlow 检测图片相似性

为图片库中的所有图片进行分类与划分的过程在数学上无法进行严格定义与求解,这是因为在 NearDup 系统中,图片之间的关系不具有传递性和相等性。为了说明这一点,我们可以想象将一张「猫」的图片通过 1000 步慢慢地形变为一张「狗」的图片的过程。容易推断,每一步微小形变前后的两张图片的相似度都将落入 NearDup 的阈值,从而将它们判断为「相似」图片。然而,究竟该将这一串前后相似的图片序列划分为哪几类?猫类,狗类,还是猫-狗类?为了解决这一问题,我们将问题模型转化为图:图的节点代表图片,边代表相应图片间的相似度。随后我们结合传递闭包法( transitive closure )和贪婪 k-cut 算法来最小化图的 k-cut 划分,从而近似求解整个图片库的最优划分。

使用批量化 LSH 进行数据预处理

嵌入和 LSH 对象

为了理解图片内容,我们将图片转换到一个嵌入向量空间(embedded vector space)中。这些图嵌入向量是图片的一种高维向量表示,能够抓取图片的视觉和语义相似性。它们一般通过神经网络架构如 VGG16 或 Inception 等处理生成。为了在 NearDup 系统中处理图片关系并对图片库进行分类,我们每天要比较几千万张新图片,并将它们分类到上亿个图片类别中。如果没有优化措施,如此大规模的近邻搜索(nearest neighbor search)问题的时间复杂度为平方(quadratic)复杂度,相应的计算时间将正比于甚至超过 10 个 quadrillion 秒(1 后面 16 个 0!)。为此,我们通过将图嵌入向量进一步缩减为 LSH 对象的方法,显著缩小了问题规模,降低了处理难度。

LSH 是一种先进的数据降维技术,降维前后数据点之间的距离关系保持不变。原向量空间首先通过随机投影法(random projection)和位抽样 LSH(bit sampling LSH)法进行一定的降维。随后,我们继续将所得到的向量位分组为多个 LSH 对象,分组过程有效地权衡了检测准确率和计算时间这一矛盾体。分组越精细,进行最近邻搜索的计算复杂度将越高,但检测准确率也将越高。这里,我们使用 LSH 对象之间的 Jaccard 重合度来近似表示原向量空间中相应向量间的余弦相似度。

批量 LSH 搜索

当所有图片都用一组 LSH 对象表示之后,我们继续为它们建立反向索引,并实现对所有图片的批量查询与搜索。从顶层看,我们通过函数式转换法(functional transformation)、压缩式反向索引与连接法(compressed inverted indexes and joins)等方法的结合,来实现对所有图片的一次性批量查询与比较。这个数据流处理过程是用 Spark 实现的,并需要借助一系列的优化措施来进一步保证这些海量数据能够转化到尽量简单有效地的LSH 对象空间中进行处理。我们所使用到的主要优化措施包括:

  • 字典编码( Dictionary encoding ) 使得所有数据都用具有最短宽度的数值进行表示

  • 可变字节编码( Variable byte encoding ) 被用于所有的反向索引

  • 索引切分( Index partitioning ) 提高了反向索引的平衡性

  • 基于代价的优化器( Cost-based optimizer ) 能够检测嵌入向量空间的密度,并计算最优的运行时参数

  • 原始数据堆排( Primitive data packing ) 进一步提高了内存使用率

  • Jaccard 重合度计数( Jaccard overlap counting ) 基于低层次、高性能的数据收集实现

  • 去堆化( Off heaping ) 减少了垃圾回收(GC)过载


使用迁移学习的候选选择

批量化LSH是产生高查全率(召回率)同时又能最小化计算成本的一个很效果的方法。但是,它通常不会对候选项产生最优的查准率(准确率)和排序。我们通过使用一个有监督的分类器去挑选那些NearDups 认为足够相似的候选项。这个分类器是一个迁移学习在视觉嵌入上的例子。它使用了Tensorflow 前馈网络和一个 Adam 优化器 。我们已经在超过包含10亿不同对图像的样本集中训练了分类器。训练集由决策树分类器在SURF 视觉特征上的输出得到,并进行了几何验证,然后用于NearDup 系统的先前迭代。为了提高学习和每一对图像的收敛性,将 hamming 码字节进行异或运算后输入到输入层。该分类器被调整到很高的准确率并且在人类标记的样本上达到了 99% 以上准确率。

SparkContext 也可以对训练过的网络进行推断。使用 mapPartitions 和分组范式,我们可以使用预定义好尺寸的大批数据去有效地向量化和减少开销。在一个拥有1000万个参数的网络中,我们在一个r3.8xlarge 的机器集群上实现了平均2ms进行一个预测的速率。


结论

NearDup 检测需要进行计算代价很高的两两比较。通过在Spark 中使用批处理LSH实现,我们通过跳过不太相似的图像对大大降低了计算的复杂度。Spark-based 的实现结合了高效的工作负载分配和低层次的优化,以最小化内存和CPU占用。随后的调优步骤使用了一个有监督的前馈网络来选择和排序高于NearDup 相似性阈值的图相对。Spark 和Tensorflow 的推断结合使用了分布式计算和每个内核矢量化的最佳特性,实现了高吞吐量和低延迟的预测。然后,将这两个步骤的结果用于集群图像,每天帮助提供数百亿的搜索结果和在Pinterest 上的推荐。

欲了解更多关于这个问题的信息,请看我在 Spark+AI Summit 2018 的演讲。 https://vimeo.com/274629687

致谢:感谢以下团队成员对本项目的所有贡献:Jiajing Xu, Vitaliy Kulikov, Jooseong Kim, Peter John Daoud, Andrew Zhai, Matthew Fang, Kevin Lau, Jacob Hanger, Zhuoyuan Li and Chao Wang

原文链接: https://medium.com/@Pinterest_Engineering/detecting-image-similarity-using-spark-lsh-and-tensorflow-618636afc939


Python 3 利用 Dlib 实现摄像头实时人脸识别 - coneypo - 博客园

$
0
0

0.引言

  利用 Python 开发,借助 Dlib 库捕获摄像头中的人脸,提取人脸特征,通过计算特征值之间的欧氏距离,来和预存的人脸特征进行对比,判断是否匹配,达到人脸识别的目的;

  可以从摄像头中抠取人脸图片存储到本地,然后提取构建预设人脸特征;

  根据抠取的 / 已有的同一个人多张人脸图片提取 128D 特征值,然后计算该人的 128D 特征均值;

  然后和摄像头中实时获取到的人脸提取出的特征值,计算欧氏距离,判定是否为同一张人脸;  

    

   Features :

  • 支持人脸数据采集,自行建立人脸数据库
  • 调用摄像头实时人脸检测和识别
  • 支持多张人脸 ( updated on 08.23 )    

 

   人脸识别 / Face Recognition 的说明:

  Wikipedia 上关于 人脸识别系统 / Face Recognition System 的描述: they work by comparing selected facial features from given image with faces within a database.

  本项目中就是比较  预设的人脸的特征 和  摄像头实时获取到的人脸的特征 

  核心就是  提取 128D 人脸特征,然后计算 摄像头人脸特征 和 预设的特征脸的 欧式距离,进行比对;

 

  效果如下:  

 

 

图 1 摄像头多个人脸时识别效果 

 

1. 总体流程

  先说下  人脸检测 ( Face detection ) 和  人脸识别 ( Face Recognition ) ,前者是达到检测出场景中人脸的目的就可以了,而后者不仅需要检测出人脸,还要和 已有人脸数据进行比对,识别出是否在数据库中,或者进行身份标注之类处理,人脸检测和人脸识别两者有时候可能会被理解混淆;

  我的之前一些项目都是用 Dlib 做人脸检测这块,这个项目想要实现的功能是人脸识别功能,借助的是 Dlib 官网中 face_recognition.py 这个例程 ( Link: http://dlib.net/face_recognition.py.html );

  核心在于 利用“dlib_face_recognition_resnet_model_v1.dat” 这个 model,提取 人脸图像的 128D 特征,然后比对不同人脸图片的 128D 特征,设定阈值  计算欧氏距离 来判断是否为同一张脸;

1 # face recognition model, the object maps human faces into 128D vectors
2 facerec = dlib.face_recognition_model_v1("dlib_face_recognition_resnet_model_v1.dat")
3 
4 shape = predictor(img, dets[0])
5 face_descriptor = facerec.compute_face_descriptor(img, shape)

 

   

 

图 2 总体设计流程

 

2.源码介绍

  主要有

    get_face_from_camera.py , 

     get_features_into_CSV.py ,

    face_reco_from_camera.py

  这三个 Python 文件,接下来会分别介绍实现功能;

 

2.1 get_face_from_camera.py / 人脸注册录入

  人脸识别需要将  提取到的图像数据 和  已有图像数据 进行比对分析,所以这部分代码实现的功能就是  人脸录入

  程序会生成一个窗口,显示调用的摄像头实时获取的图像;

   (关于摄像头的调用方式可以参考这里:  Python 3 利用 Dlib 19.7 实现摄像头人脸检测特征点标定);

  

  然后根据键盘输入进行人脸捕获:

  • “N” 新录入人脸,新建文件夹 person_X/  用来存储某人的人脸图像
  •   "S" 开始捕获人脸,将捕获到的人脸放到 person_X/ 路径下
  • “Q” 退出窗口

  

  摄像头的调用是利用 opencv 库的  cv2.VideoCapture(0), 此处参数为 0 代表调用的是笔记本的默认摄像头,你也可以让它调用传入已有视频文件;

 

图 3  get_face_from_camera.py 的界面

   

  捕获到的一组人脸示例;

图 4 捕获到的一组人脸

 

  get_face_from_camera.py 源码

复制代码
  1 # created at 2018-05-11
  2 # updated at 2018-09-07
  3 
  4 # Author:   coneypo
  5 # Blog:     http://www.cnblogs.com/AdaminXie
  6 # GitHub:   https://github.com/coneypo/Dlib_face_recognition_from_camera
  7 
  8 # 进行人脸录入
  9 # 录入多张人脸
 10 import dlib         # 人脸识别的库 Dlib
 11 import numpy as np  # 数据处理的库 Numpy
 12 import cv2          # 图像处理的库 OpenCv
 13 import os
 14 import shutil
 15 
 16 # Dlib 预测器
 17 detector = dlib.get_frontal_face_detector()
 18 predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
 19 
 20 # 创建 cv2 摄像头对象
 21 cap = cv2.VideoCapture(0)
 22 
 23 # cap.set(propId, value)
 24 # 设置视频参数,propId 设置的视频参数,value 设置的参数值
 25 cap.set(3, 480)
 26 
 27 # 截图 screenshoot 的计数器
 28 cnt_ss = 0
 29 
 30 # 人脸截图的计数器
 31 cnt_p = 0
 32 
 33 # 存储人脸的文件夹
 34 current_face_dir = 0
 35 
 36 # 保存
 37 path_make_dir = "data/faces_from_camera/"
 38 
 39 path_csv = "data/csvs_from_camera/"
 40 
 41 
 42 # clear the old folders at first
 43 def pre_clear():
 44     folders_rd = os.listdir(path_make_dir)
 45     for i in range(len(folders_rd)):
 46         shutil.rmtree(path_make_dir+folders_rd[i])
 47 
 48     csv_rd = os.listdir(path_csv)
 49     for i in range(len(csv_rd)):
 50         os.remove(path_csv+csv_rd[i])
 51 
 52 
 53 # clear the exist folders of faces and csv
 54 pre_clear()
 55 
 56 
 57 # 人脸种类数目的计数器
 58 person_cnt = 0
 59 
 60 # cap.isOpened() 返回 true/false 检查初始化是否成功
 61 while cap.isOpened():
 62 
 63     # cap.read()
 64     # 返回两个值:
 65     #    一个布尔值 true/false,用来判断读取视频是否成功/是否到视频末尾
 66     #    图像对象,图像的三维矩阵q
 67     flag, im_rd = cap.read()
 68 
 69     # 每帧数据延时 1ms,延时为 0 读取的是静态帧
 70     kk = cv2.waitKey(1)
 71 
 72     # 取灰度
 73     img_gray = cv2.cvtColor(im_rd, cv2.COLOR_RGB2GRAY)
 74 
 75     # 人脸数 rects
 76     rects = detector(img_gray, 0)
 77 
 78     # print(len(rects))q
 79 
 80     # 待会要写的字体
 81     font = cv2.FONT_HERSHEY_COMPLEX
 82 
 83     # 按下 'n' 新建存储人脸的文件夹
 84     if kk == ord('n'):
 85         person_cnt += 1
 86         # current_face_dir = path_make_dir + time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())
 87         current_face_dir = path_make_dir + "person_" + str(person_cnt)
 88         print('\n')
 89         for dirs in (os.listdir(path_make_dir)):
 90             if current_face_dir == path_make_dir + dirs:
 91                 shutil.rmtree(current_face_dir)
 92                 print("删除旧的文件夹:", current_face_dir)
 93         os.makedirs(current_face_dir)
 94         print("新建的人脸文件夹: ", current_face_dir)
 95 
 96         # 将人脸计数器清零
 97         cnt_p = 0
 98 
 99     if len(rects) != 0:
100         # 检测到人脸
101 
102         # 矩形框
103         for k, d in enumerate(rects):
104 
105             # 计算矩形大小
106             # (x,y), (宽度width, 高度height)
107             pos_start = tuple([d.left(), d.top()])
108             pos_end = tuple([d.right(), d.bottom()])
109 
110             # 计算矩形框大小
111             height = d.bottom() - d.top()
112             width = d.right() - d.left()
113 
114             # 根据人脸大小生成空的图像
115             cv2.rectangle(im_rd, tuple([d.left(), d.top()]), tuple([d.right(), d.bottom()]), (0, 255, 255), 2)
116             im_blank = np.zeros((height, width, 3), np.uint8)
117 
118             # 按下 's' 保存摄像头中的人脸到本地
119             if kk == ord('s'):
120                 cnt_p += 1
121                 for ii in range(height):
122                     for jj in range(width):
123                         im_blank[ii][jj] = im_rd[d.top() + ii][d.left() + jj]
124                 cv2.imwrite(current_face_dir + "/img_face_" + str(cnt_p) + ".jpg", im_blank)
125                 print("写入本地:", str(current_face_dir) + "/img_face_" + str(cnt_p) + ".jpg")
126 
127         # 显示人脸数
128     cv2.putText(im_rd, "Faces: " + str(len(rects)), (20, 100), font, 0.8, (0, 255, 0), 1, cv2.LINE_AA)
129 
130     # 添加说明
131     cv2.putText(im_rd, "Face Register", (20, 40), font, 1, (255, 255, 255), 1, cv2.LINE_AA)
132     cv2.putText(im_rd, "N: New face folder", (20, 350), font, 0.8, (255, 255, 255), 1, cv2.LINE_AA)
133     cv2.putText(im_rd, "S: Save face", (20, 400), font, 0.8, (255, 255, 255), 1, cv2.LINE_AA)
134     cv2.putText(im_rd, "Q: Quit", (20, 450), font, 0.8, (255, 255, 255), 1, cv2.LINE_AA)
135 
136     # 按下 'q' 键退出
137     if kk == ord('q'):
138         break
139 
140     # 窗口显示
141     # cv2.namedWindow("camera", 0) # 如果需要摄像头窗口大小可调
142     cv2.imshow("camera", im_rd)
143 
144 # 释放摄像头
145 cap.release()
146 
147 # 删除建立的窗口
148 cv2.destroyAllWindows()
复制代码

 

  get_face_from_camera.py 的输出 log

复制代码
删除旧的文件夹: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_1
新建的人脸文件夹:  F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_1
写入本地: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_1/img_face_1.jpg
写入本地: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_1/img_face_2.jpg
写入本地: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_1/img_face_3.jpg
写入本地: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_1/img_face_4.jpg
写入本地: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_1/img_face_5.jpg
写入本地: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_1/img_face_6.jpg


删除旧的文件夹: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_2
新建的人脸文件夹:  F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_2
写入本地: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_2/img_face_1.jpg
写入本地: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_2/img_face_2.jpg
写入本地: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_2/img_face_3.jpg
写入本地: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_2/img_face_4.jpg


删除旧的文件夹: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_3
新建的人脸文件夹:  F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_3
写入本地: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_3/img_face_1.jpg
写入本地: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_3/img_face_2.jpg
写入本地: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_3/img_face_3.jpg
写入本地: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_3/img_face_4.jpg
写入本地: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_3/img_face_5.jpg
写入本地: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_3/img_face_6.jpg
写入本地: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_3/img_face_7.jpg   
复制代码

 

2.2  get_features_into_CSV.py / 将图像文件中人脸数据提取出来存入 CSV

  这部分代码实现的功能是将之前捕获到的人脸图像文件,提取出 128D 特征,然后计算出某人人脸数据的特征均值存入 CSV 中,方便之后识别时候进行比对;

  利用 numpy.mean() 计算特征均值;

   get_features_into_CSV.py 源码:

复制代码
  1 # created at 2018-05-11
  2 
  3 # updated at 2018-09-06
  4 # 增加录入多张人脸到CSV的功能
  5 
  6 # By        coneypo
  7 # Blog:     http://www.cnblogs.com/AdaminXie
  8 # GitHub:   https://github.com/coneypo/Dlib_face_recognition_from_camera
  9 
 10 #   return_128d_features()          获取某张图像的128d特征
 11 #   write_into_csv()                将某个文件夹中的图像读取特征并写入csv
 12 #   compute_the_mean()              从csv中读取128d特征,并计算特征均值
 13 
 14 
 15 import cv2
 16 import os
 17 import dlib
 18 from skimage import io
 19 import csv
 20 import numpy as np
 21 import pandas as pd
 22 
 23 path_faces_rd = "data/faces_from_camera/"
 24 path_csv = "data/csvs_from_camera/"
 25 
 26 # detector to find the faces
 27 detector = dlib.get_frontal_face_detector()
 28 
 29 # shape predictor to find the face landmarks
 30 predictor = dlib.shape_predictor("shape_predictor_5_face_landmarks.dat")
 31 
 32 # face recognition model, the object maps human faces into 128D vectors
 33 facerec = dlib.face_recognition_model_v1("dlib_face_recognition_resnet_model_v1.dat")
 34 
 35 
 36 # 返回单张图像的128D特征
 37 def return_128d_features(path_img):
 38     img = io.imread(path_img)
 39     img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
 40     dets = detector(img_gray, 1)
 41 
 42     print("检测的人脸图像:", path_img, "\n")
 43 
 44     # 因为有可能截下来的人脸再去检测,检测不出来人脸了
 45     # 所以要确保是 检测到人脸的人脸图像 拿去算特征
 46     if len(dets) != 0:
 47         shape = predictor(img_gray, dets[0])
 48         face_descriptor = facerec.compute_face_descriptor(img_gray, shape)
 49     else:
 50         face_descriptor = 0
 51         print("no face")
 52 
 53     # print(face_descriptor)
 54     return face_descriptor
 55 
 56 
 57 # 将文件夹中照片特征提取出来,写入csv
 58 # 输入input:
 59 #   path_faces_personX:     图像文件夹的路径
 60 #   path_csv:               要生成的csv路径
 61 
 62 def write_into_csv(path_faces_personX, path_csv):
 63     dir_pics = os.listdir(path_faces_personX)
 64     with open(path_csv, "w", newline="") as csvfile:
 65         writer = csv.writer(csvfile)
 66         for i in range(len(dir_pics)):
 67             # 调用return_128d_features()得到128d特征
 68             print("正在读的人脸图像:", path_faces_personX + "/" + dir_pics[i])
 69             features_128d = return_128d_features(path_faces_personX + "/" + dir_pics[i])
 70             #  print(features_128d)
 71             # 遇到没有检测出人脸的图片跳过
 72             if features_128d == 0:
 73                 i += 1
 74             else:
 75                 writer.writerow(features_128d)
 76 
 77 
 78 # 读取 某人 所有的人脸图像的数据,写入 person_X.csv
 79 faces = os.listdir(path_faces_rd)
 80 for person in faces:
 81     print(path_csv + person + ".csv")
 82     write_into_csv(path_faces_rd + person, path_csv + person + ".csv")
 83 
 84 
 85 # 从csv中读取数据,计算128d特征的均值
 86 def compute_the_mean(path_csv_rd):
 87     column_names = []
 88 
 89     # 128列特征
 90     for i in range(128):
 91         column_names.append("features_" + str(i + 1))
 92 
 93     # 利用pandas读取csv
 94     rd = pd.read_csv(path_csv_rd, names=column_names)
 95 
 96     # 存放128维特征的均值
 97     feature_mean = []
 98 
 99     for i in range(128):
100         tmp_arr = rd["features_" + str(i + 1)]
101         tmp_arr = np.array(tmp_arr)
102 
103         # 计算某一个特征的均值
104         tmp_mean = np.mean(tmp_arr)
105         feature_mean.append(tmp_mean)
106     return feature_mean
107 
108 
109 # 存放所有特征均值的 CSV 的路径
110 path_csv_feature_all = "data/features_all.csv"
111 # 存放人脸特征的csv的路径
112 path_csv_rd = "data/csvs_from_camera/"
113 
114 with open(path_csv_feature_all, "w", newline="") as csvfile:
115     writer = csv.writer(csvfile)
116     csv_rd = os.listdir(path_csv_rd)
117     print("特征均值: ")
118     for i in range(len(csv_rd)):
119         feature_mean = compute_the_mean(path_csv_rd + csv_rd[i])
120         # print(feature_mean)
121         print(path_csv_rd + csv_rd[i])
122         writer.writerow(feature_mean)
复制代码

 

  我们可以看下对于某张图片,face_descriptor 的输出结果:

  绿色框内是我们的返回 128D 特征的函数;

  在红色框内调用该函数来计算 img_face_13.jpg;

  可以看到黄色框中的输出为 128D 的向量;

图 5 返回单张图像的 128D 特征的计算结果

 

  所以就把人脸图像进行批量化操作,提取出 128D 的特征,然后计算特征均值,存入 features_all.csv;

  features_all.csv 是一个 n 行 128 列的 CSV, n 是录入的人脸数,128 列是某人的 128D 特征;

  这存储的就是  录入的人脸数据,之后  摄像头捕获的人脸 将要拿过来和  这些特征值 进行比对,如果欧式距离比较近的话,就可以认为是同一张人脸

 

    get_features_into_CSV.py 的输出 log:

复制代码
F:/code/python/P_dlib_face_reco/data/csvs_from_camera/person_1.csv
正在读的人脸图像: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_1/img_face_1.jpg
检测的人脸图像: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_1/img_face_1.jpg 
   
...
正在读的人脸图像: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_5/img_face_3.jpg 检测的人脸图像: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_5/img_face_3.jpg 正在读的人脸图像: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_5/img_face_4.jpg 检测的人脸图像: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_5/img_face_4.jpg 正在读的人脸图像: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_5/img_face_5.jpg 检测的人脸图像: F:/code/python/P_dlib_face_reco/data/faces_from_camera/person_5/img_face_5.jpg 特征均值: F:/code/python/P_dlib_face_reco/data/csvs_from_camera/person_1.csv F:/code/python/P_dlib_face_reco/data/csvs_from_camera/person_2.csv F:/code/python/P_dlib_face_reco/data/csvs_from_camera/person_3.csv F:/code/python/P_dlib_face_reco/data/csvs_from_camera/person_4.csv F:/code/python/P_dlib_face_reco/data/csvs_from_camera/person_5.csv
复制代码

 

2.3 face_reco_from_camera.py / 实时人脸识别对比分析

  这部分源码实现的功能:调用摄像头,捕获摄像头中的人脸,然后如果检测到人脸,将  摄像头中的人脸提取出 128D 的特征,然后和  之前录入人脸的 128D 特征 进行计算欧式距离,如果比较小,可以判定为一个人,否则不是一个人;

  欧氏距离对比的阈值设定,是在  return_euclidean_distance 函数的  dist 变量;

  我这里程序里面指定的  欧氏距离判断阈值是 0.4,具体阈值可以根据实际情况或者测得结果进行修改;

  

  这边做了一个,让人名跟随显示在头像下方,如果想要在人脸矩形框下方显示人名,首先需要知道 Dlib 生成的矩形框的尺寸怎么读取;

  Dlib 返回的 dets 变量是一系列人脸的数据,此处对单张人脸处理,所以取 dets[0] 的参数;

  可以通过  dets[0].top()dets[0].bottom()dets[0].left() 和  dets[0].right() 来确定要显示的人名的坐标;

图 6 dets[0].top() 等参数说明 

  

  得到矩形框的坐标,就可以获取人名的相对位置;

  这是我这边取的坐标:

1 pos_text_1 = tuple([dets[0].left(), int(dets[0].bottom()+(dets[0].bottom()-dets[0].top())/4)])

 

  

 

图 7 face_reco_from_camera.py 生成的人脸识别窗口界面

 

  face_reco_from_camera.py 源码:

复制代码
  1 # created at 2018-05-11
  2 # updated at 2018-09-08
  3 # support multi-faces now
  4 
  5 # Author:   coneypo
  6 # Blog:     http://www.cnblogs.com/AdaminXie
  7 # GitHub:   https://github.com/coneypo/Dlib_face_recogqnition_from_camera
  8 
  9 import dlib         # 人脸识别的库dlib
 10 import numpy as np  # 数据处理的库numpy
 11 import cv2          # 图像处理的库OpenCv
 12 import pandas as pd # 数据处理的库Pandas
 13 
 14 # face recognition model, the object maps human faces into 128D vectors
 15 facerec = dlib.face_recognition_model_v1("dlib_face_recognition_resnet_model_v1.dat")
 16 
 17 
 18 # 计算两个向量间的欧式距离
 19 def return_euclidean_distance(feature_1, feature_2):
 20     feature_1 = np.array(feature_1)
 21     feature_2 = np.array(feature_2)
 22     dist = np.sqrt(np.sum(np.square(feature_1 - feature_2)))
 23     print("e_distance: ", dist)
 24 
 25     if dist > 0.4:
 26         return "diff"
 27     else:
 28         return "same"
 29 
 30 
 31 # 处理存放所有人脸特征的 CSV
 32 path_features_known_csv = "data/features_all.csv"
 33 csv_rd = pd.read_csv(path_features_known_csv, header=None)
 34 
 35 # 存储的特征人脸个数
 36 # print(csv_rd.shape[0])
 37 
 38 # 用来存放所有录入人脸特征的数组
 39 features_known_arr = []
 40 
 41 # known faces
 42 for i in range(csv_rd.shape[0]):
 43     features_someone_arr = []
 44     for j in range(0, len(csv_rd.ix[i, :])):
 45         features_someone_arr.append(csv_rd.ix[i, :][j])
 46     #    print(features_someone_arr)
 47     features_known_arr.append(features_someone_arr)
 48 print("Faces in Database:", len(features_known_arr))
 49 
 50 # Dlib 预测器
 51 detector = dlib.get_frontal_face_detector()
 52 predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
 53 
 54 # 创建 cv2 摄像头对象
 55 cap = cv2.VideoCapture(0)
 56 
 57 # cap.set(propId, value)
 58 # 设置视频参数,propId 设置的视频参数,value 设置的参数值
 59 cap.set(3, 480)
 60 
 61 
 62 # 返回一张图像多张人脸的 128D 特征
 63 def get_128d_features(img_gray):
 64     dets = detector(img_gray, 1)
 65     if len(dets) != 0:
 66         face_des = []
 67         for i in range(len(dets)):
 68             shape = predictor(img_gray, dets[i])
 69             face_des.append(facerec.compute_face_descriptor(img_gray, shape))
 70     else:
 71         face_des = []
 72     return face_des
 73 
 74 
 75 # cap.isOpened() 返回true/false 检查初始化是否成功
 76 while cap.isOpened():
 77 
 78     flag, img_rd = cap.read()
 79     kk = cv2.waitKey(1)
 80 
 81     # 取灰度
 82     img_gray = cv2.cvtColor(img_rd, cv2.COLOR_RGB2GRAY)
 83 
 84     # 人脸数 dets
 85     faces = detector(img_gray, 0)
 86 
 87     # 待会要写的字体
 88     font = cv2.FONT_HERSHEY_COMPLEX
 89 
 90     cv2.putText(img_rd, "Press 'q': Quit", (20, 400), font, 0.8, (84, 255, 159), 1, cv2.LINE_AA)
 91 
 92     # 存储人脸名字和位置的两个 list
 93     # list 1 (faces): store the name of faces               Jack    unknown unknown Mary
 94     # list 2 (pos_namelist): store the positions of faces   12,1    1,21    1,13    31,1
 95 
 96     # 存储所有人脸的名字
 97     pos_namelist = []
 98     name_namelist = []
 99 
100     # 检测到人脸
101     if len(faces) != 0:
102         # 获取当前捕获到的图像的所有人脸的特征,存储到 features_cap_arr
103         features_cap_arr = []
104         for i in range(len(faces)):
105             shape = predictor(img_rd, faces[i])
106             features_cap_arr.append(facerec.compute_face_descriptor(img_rd, shape))
107 
108         # 遍历捕获到的图像中所有的人脸
109         for k in range(len(faces)):
110             # 让人名跟随在矩形框的下方
111             # 确定人名的位置坐标
112             # 先默认所有人不认识,是 unknown
113             name_namelist.append("unknown")
114 
115             # 每个捕获人脸的名字坐标
116             pos_namelist.append(tuple([faces[k].left(), int(faces[k].bottom() + (faces[k].bottom() - faces[k].top()) / 4)]))
117 
118             # 对于某张人脸,遍历所有存储的人脸特征
119             for i in range(len(features_known_arr)):
120                 print("with person_", str(i+1), "the ", end='')
121                 # 将某张人脸与存储的所有人脸数据进行比对
122                 compare = return_euclidean_distance(features_cap_arr[k], features_known_arr[i])
123                 if compare == "same":  # 找到了相似脸
124                     name_namelist[k] = "person_" + str(i+1)
125 
126             # 矩形框
127             for kk, d in enumerate(faces):
128                 # 绘制矩形框
129                 cv2.rectangle(img_rd, tuple([d.left(), d.top()]), tuple([d.right(), d.bottom()]), (0, 255, 255), 2)
130 
131         # 写人脸名字
132         for i in range(len(faces)):
133             cv2.putText(img_rd, name_namelist[i], pos_namelist[i], font, 0.8, (0, 255, 255), 1, cv2.LINE_AA)
134 
135     print("Name list now:", name_namelist, "\n")
136 
137     cv2.putText(img_rd, "Face Register", (20, 40), font, 1, (255, 255, 255), 1, cv2.LINE_AA)
138     cv2.putText(img_rd, "Faces: " + str(len(faces)), (20, 100), font, 1, (0, 0, 255), 1, cv2.LINE_AA)
139 
140     # 按下 q 键退出
141     if kk == ord('q'):
142         break
143 
144     # 窗口显示
145     cv2.imshow("camera", img_rd)
146 
147 # 释放摄像头
148 cap.release()
149 
150 # 删除建立的窗口
151 cv2.destroyAllWindows()
复制代码

 

  face_reco_from_camera.py 输出 log:

复制代码
Faces in Database: 5
Name list now: [] 

Name list now: [] 

Name list now: [] 

Name list now: [] 

Name list now: [] 

with person_ 1 the e_distance:  0.40770022710364756
with person_ 2 the e_distance:  0.41082186674421134
with person_ 3 the e_distance:  0.3961545573801463
with person_ 4 the e_distance:  0.3881850644563972
with person_ 5 the e_distance:  0.3495735780870818
Name list now: ['person_4'] 

with person_ 1 the e_distance:  0.4314467101915446
with person_ 2 the e_distance:  0.4299990464683071
with person_ 3 the e_distance:  0.4182695008637471
with person_ 4 the e_distance:  0.4173694262729763
with person_ 5 the e_distance:  0.38357217732017734
Name list now: ['person_4'] 

with person_ 1 the e_distance:  0.4262991040992263
with person_ 2 the e_distance:  0.43254966504500664
with person_ 3 the e_distance:  0.41576433114841965
with person_ 4 the e_distance:  0.4122140311433292
with person_ 5 the e_distance:  0.38073570942005236
Name list now: ['person_4'] 

with person_ 1 the e_distance:  0.42088261541728456
with person_ 2 the e_distance:  0.42064499551908163
with person_ 3 the e_distance:  0.404443147870785
with person_ 4 the e_distance:  0.4043774203639022
with person_ 5 the e_distance:  0.37271089160417986
Name list now: ['person_4'] 
复制代码

 

  实时输出结果:

图 9 实时输出的欧氏距离结果

 

  通过实时的输出结果,看的比较明显;

  输出绿色部分:当是我自己(即之前分析提取特征的 default_person)时,计算出来的欧式距离基本都在  0.2 左右

  输出红色部分:而换一张图片上去比如特朗普,明显看到欧式距离计算结果  达到了 0.8,此时就可以判定,后来这张人脸不是我们预设的人脸;

  所以之前提到的欧式距离计算对比的阈值可以由此设定,本项目中取的是  dist=0.4

 

3.总结

  核心就是  提取人脸特征,然后计算欧式距离和预设的特征脸进行比对;

  不过这个实时获取摄像头人脸进行比对,要实时的进行计算摄像头脸的特征值,然后还要计算欧氏距离,所以计算量比较大,可能摄像头视频流会出现卡顿;

   

  update on 09.06:

   满足多张人脸识别需要

 

# 请尊重他人劳动成果,转载或者使用源码请注明出处:http://www.cnblogs.com/AdaminXie

# 代码已上传到了我的 GitHub,如果对您有帮助欢迎 Star 下: https://github.com/coneypo/Dlib_face_recognition_from_camera

人脸识别模型和源代码 Welcome to Yu Qiao's homepage!

$
0
0
This page list the codes, models, and softwares we released in the last years. Most of them are developed and maintained by our students and collaborators. The topics includes the following areas:
  • Action Recognition and Detection in Videos
  • Scene Understanding and Classification
  • Scene Text Detection and Recognition
  • Face Detection and Recognition
  • Action Recognition and Detection in Videos

  • Temporal Segment Networks for action video classification( ECCV 16 paper, 1st in ActivityNet Challenge 2016) [Codes]
  • Enhanced Motion Vector CNNs for fast Action Recognition ( CVPR 16 paper, realtime action recognition, 390 frames per second) [Project][Codes]
  • Trajectory-Pooled Deep-Convolutional Descriptors for Action Recognition( CVPR 15 paper, high Performance on HMDB51 (65.9%) and UCF101(91.5%).) [Project][Codes]
  • Multi-View Super Vector for Action Recognition( CVPR 14 Oral paper, a new method for jointly encoding multiple types of local descriptors.) [Code]

    Scene Understanding and Classification

  • MR-CNNS for large scale scene classification (2nd in scene classification task ImageNet 2016, 1st in LSUN 2016) [Code]
  • Weakly Supervised PatchNets for Scene Recognition ( MIT Indoor67 (86.2%) and SUN397 (73.0%) ) [Paper][Code]
  • Pairwise Rotation Invariant Co-occurrence Local Binary Pattern ( A very fast local descriptors for scene and texture classification ) [PAMI 14 Paper][Project][Code]
  • Scene Text Detection and Recognition

  • Connectionist Text Proposal Network for Scene Text Detection [ECCV 16 Paper][Online demo]
  • Facial Detection and Recognition

  • A Discriminative Deep Feature Learning Approach for Face Recognition(Single model trained on CAISA-WebFace achieves ~99% verification rate on LFW) [ECCV 16 Paper][Code][Model]
  • Multi-task Cascaded Convolutional Neural Networks for Joint Face Detection and Alignment [SPL 16 Paper][Code]
  • Back to Top

    Copyright © Yu Qiao 2013.

    Template designed by OSWD Ad_267

    基于TensorFlow Serving的深度学习在线预估

    $
    0
    0

    一、前言

    随着深度学习在图像、语言、广告点击率预估等各个领域不断发展,很多团队开始探索深度学习技术在业务层面的实践与应用。而在广告CTR预估方面,新模型也是层出不穷: Wide and Deep[1]、DeepCross Network[2]、DeepFM[3]、xDeepFM[4],美团很多篇深度学习博客也做了详细的介绍。但是,当离线模型需要上线时,就会遇见各种新的问题: 离线模型性能能否满足线上要求、模型预估如何镶入到原有工程系统等等。只有准确的理解深度学习框架,才能更好地将深度学习部署到线上,从而兼容原工程系统、满足线上性能要求。

    本文首先介绍下美团平台用户增长组业务场景及离线训练流程,然后主要介绍我们使用TensorFlow Serving部署WDL模型到线上的全过程,以及如何优化线上服务性能,希望能对大家有所启发。

    二、业务场景及离线流程

    2.1 业务场景

    在广告精排的场景下,针对每个用户,最多会有几百个广告召回,模型根据用户特征与每一个广告相关特征,分别预估该用户对每条广告的点击率,从而进行排序。由于广告交易平台(AdExchange)对于DSP的超时时间限制,我们的排序模块平均响应时间必须控制在10ms以内,同时美团DSP需要根据预估点击率参与实时竞价,因此对模型预估性能要求比较高。

    2.2 离线训练

    离线数据方面,我们使用Spark生成TensorFlow[5]原生态的数据格式tfrecord,加快数据读取。

    模型方面,使用经典的Wide and Deep模型,特征包括用户维度特征、场景维度特征、商品维度特征。Wide 部分有 80多特征输入,Deep部分有60多特征输入,经过Embedding输入层大约有600维度,之后是3层256等宽全连接,模型参数一共有35万参数,对应导出模型文件大小大约11M。

    离线训练方面,使用TensorFlow同步 + Backup Workers[6]的分布式框架,解决异步更新延迟和同步更新性能慢的问题。

    在分布式ps参数分配方面,使用GreedyLoadBalancing方式,根据预估参数大小分配参数,取代Round Robin取模分配的方法,可以使各个PS负载均衡。

    计算设备方面,我们发现只使用CPU而不使用GPU,训练速度会更快,这主要是因为尽管GPU计算上性能可能会提升,但是却增加了CPU与GPU之间数据传输的开销,当模型计算并不太复杂时,使用CPU效果会更好些。

    同时我们使用了Estimator高级API,将数据读取、分布式训练、模型验证、TensorFlow Serving模型导出进行封装。
    使用Estimator的主要好处在于:

    1. 单机训练与分布式训练可以很简单的切换,而且在使用不同设备:CPU、GPU、TPU时,无需修改过多的代码。
    2. Estimator的框架十分清晰,便于开发者之间的交流。
    3. 初学者还可以直接使用一些已经构建好的Estimator模型:DNN模型、XGBoost模型、线性模型等。

    三、TensorFlow Serving及性能优化

    3.1 TensorFlow Serving介绍

    TensorFlow Serving是一个用于机器学习模型Serving的高性能开源库,它可以将训练好的机器学习模型部署到线上,使用gRPC作为接口接受外部调用。TensorFlow Serving支持模型热更新与自动模型版本管理,具有非常灵活的特点。

    下图为TensorFlow Serving整个框架图。Client端会不断给Manager发送请求,Manager会根据版本管理策略管理模型更新,并将最新的模型计算结果返回给Client端。


    TensorFlow Serving架构,图片来源于TensorFlow Serving官方文档
    TensorFlow Serving架构,图片来源于TensorFlow Serving官方文档

    美团内部由数据平台提供专门TensorFlow Serving通过YARN分布式地跑在集群上,其周期性地扫描HDFS路径来检查模型版本,并自动进行更新。当然,每一台本地机器都可以安装TensorFlow Serving进行试验。

    在我们站外广告精排的场景下,每来一位用户时,线上请求端会把该用户和召回所得100个广告的所有信息,转化成模型输入格式,然后作为一个Batch发送给TensorFlow Serving,TensorFlow Serving接受请求后,经过计算得到CTR预估值,再返回给请求端。

    部署TensorFlow Serving的第一版时,QPS大约200时,打包请求需要5ms,网络开销需要固定3ms左右,仅模型预估计算需要10ms,整个过程的TP50线大约18ms,性能完全达不到线上的要求。接下来详细介绍下我们性能优化的过程。

    3.2 性能优化

    3.2.1 请求端优化

    线上请求端优化主要是对一百个广告进行并行处理,我们使用OpenMP多线程并行处理数据,将请求时间性能从5ms降低到2ms左右。

    #pragma omp parallel for 
    for (int i = 0; i < request->ad_feat_size(); ++i) {
        tensorflow::Example example;
        data_processing();
    }

    3.2.2 构建模型OPS优化

    在没有进行优化之前,模型的输入是未进行处理的原格式数据,例如,渠道特征取值可能为:'渠道1'、'渠道2' 这样的string格式,然后在模型里面做One Hot处理。

    最初模型使用了大量的高阶tf.feature_column对数据进行处理, 转为One Hot和embedding格式。 使用tf.feature_column的好处是,输入时不需要对原数据做任何处理,可以通过feature_column API在模型内部对特征做很多常用的处理,例如:tf.feature_column.bucketized_column可以做分桶,tf.feature_column.crossed_column可以对类别特征做特征交叉。但特征处理的压力就放在了模型里。

    为了进一步分析使用feature_column的耗时,我们使用tf.profiler工具,对整个离线训练流程耗时做了分析。在Estimator框架下使用tf.profiler是非常方便的,只需加一行代码即可。

    with tf.contrib.tfprof.ProfileContext(job_dir + ‘/tmp/train_dir’) as pctx:
       estimator = tf.estimator.Estimator(model_fn=get_model_fn(job_dir),
                                          config=run_config,
                                          params=hparams)
    

    下图为使用tf.profiler,网络在向前传播的耗时分布图,可以看出使用feature_column API的特征处理耗费了很大时间。


    优化前profiler记录
    优化前profiler记录, 前向传播的耗时占总训练时间55.78%,主要耗费在feature_column OPS对原始数据的预处理

    为了解决特征在模型内做处理耗时大的问题,我们在处理离线数据时,把所有string格式的原生数据,提前做好One Hot的映射,并且把映射关系落到本地feature_index文件,进而供线上线下使用。这样就相当于把原本需要在模型端计算One Hot的过程省略掉,替代为使用词典做O(1)的查找。同时在构建模型时候,使用更多性能有保证的低阶API替代feature_column这样的高阶API。下图为性能优化后,前向传播耗时在整个训练流程的占比。可以看出,前向传播的耗时占比降低了很多。


    优化后profiler记录
    优化后profiler记录,前向传播耗时占总训练时间39.53%

    3.2.3 XLA,JIT编译优化

    TensorFlow采用有向数据流图来表达整个计算过程,其中Node代表着操作(OPS),数据通过Tensor的方式来表达,不同Node间有向的边表示数据流动方向,整个图就是有向的数据流图。

    XLA(Accelerated Linear Algebra)是一种专门对TensorFlow中线性代数运算进行优化的编译器,当打开JIT(Just In Time)编译模式时,便会使用XLA编译器。整个编译流程如下图所示:


    tensorflow计算流程
    TensorFlow计算流程

    首先TensorFlow整个计算图会经过优化,图中冗余的计算会被剪掉。HLO(High Level Optimizer)会将优化后的计算图 生成HLO的原始操作,XLA编译器会对HLO的原始操作进行一些优化,最后交给LLVM IR根据不同的后端设备,生成不同的机器代码。

    JIT的使用,有助于LLVM IR根据 HLO原始操作生成 更高效的机器码;同时,对于多个可融合的HLO原始操作,会融合成一个更加高效的计算操作。但是JIT的编译是在代码运行时进行编译,这也意味着运行代码时会有一部分额外的编译开销。


    jit性能影响
    网络结构、Batch Size对JIT性能影响[7]

    上图显示为不同网络结构,不同Batch Size下使用JIT编译后与不使用JIT编译的耗时之比。可以看出,较大的Batch Size性能优化比较明显,层数与神经元个数变化对JIT编译优化影响不大。

    在实际的应用中,具体效果会因网络结构、模型参数、硬件设备等原因而异。

    3.2.4 最终性能

    经过上述一系列的性能优化,模型预估时间从开始的10ms降低到1.1ms,请求时间从5ms降到2ms。整个流程从打包发送请求到收到结果,耗时大约6ms。


    模型计算时间相关参数:QPS: 1308, 50line: 1.1ms,999line: 3.0ms。下面四个图分别为: 耗时分布图显示大部分耗时控制在1ms内; 请求次数显示每分钟请求大约8万次,折合QPS为1308; 平均耗时时间为1.1ms; 成功率为100%
    模型计算时间相关参数:QPS:1308,50line:1.1ms,999line:3.0ms。下面四个图分别为:耗时分布图显示大部分耗时控制在1ms内;请求次数显示每分钟请求大约8万次,折合QPS为1308;平均耗时时间为1.1ms;成功率为100%

    3.3 模型切换毛刺问题

    通过监控发现,当模型进行更新时,会有大量的请求超时。如下图所示,每次更新都会导致有大量请求超时,对系统的影响较大。通过TensorFlow Serving日志和代码分析发现,超时问题主要源于两个方面,一方面,更新、加载模型和处理TensorFlow Serving请求的线程共用一个线程池,导致切换模型时候无法处理请求;另一方面,模型加载后,计算图采用Lazy Initialization方式,导致第一次请求需要等待计算图初始化。


    模型切换导致请求超时
    模型切换导致请求超时

    问题一主要是因为加载和卸载模型线程池配置问题,在源代码中:

    uint32 num_load_threads = 0; uint32 num_unload_threads = 0;

    这两个参数默认为 0,表示不使用独立线程池,和Serving Manager在同一个线程中运行。修改成1便可以有效解决此问题。

    模型加载的核心操作为RestoreOp,包括从存储读取模型文件、分配内存、查找对应的Variable等操作,其通过调用Session的run方法来执行。而默认情况下,一个进程内的所有Session的运算均使用同一个线程池。所以导致模型加载过程中加载操作和处理Serving请求的运算使用同一线程池,导致Serving请求延迟。解决方法是通过配置文件设置,可构造多个线程池,模型加载时指定使用独立的线程池执行加载操作。

    对于问题二,模型首次运行耗时较长的问题,采用在模型加载完成后提前进行一次Warm Up运算的方法,可以避免在请求时运算影响请求性能。这里使用Warm Up的方法是,根据导出模型时设置的Signature,拿出输入数据的类型,然后构造出假的输入数据来初始化模型。

    通过上述两方面的优化,模型切换后请求延迟问题得到很好的解决。如下图所示,切换模型时毛刺由原来的84ms降低为4ms左右。


    优化后模型切换超时问题得到很大提升
    优化后模型切换后,毛刺降低

    四、总结与展望

    本文主要介绍了用户增长组基于Tensorflow Serving在深度学习线上预估的探索,对性能问题的定位、分析、解决;最终实现了高性能、稳定性强、支持各种深度学习模型的在线服务。

    在具备完整的离线训练与在线预估框架基础之后,我们将会加快策略的快速迭代。在模型方面,我们可以快速尝试新的模型,尝试将强化学习与竞价结合;在性能方面,结合工程要求,我们会对TensorFlow的图优化、底层操作算子、操作融合等方面做进一步的探索;除此之外,TensorFlow Serving的预估功能可以用于模型分析,谷歌也基于此推出What-If-Tools来帮助模型开发者对模型深入分析。最后,我们也会结合模型分析,对数据、特征再做重新的审视。

    参考文献

    [1] Cheng, H. T., Koc, L., Harmsen, J., Shaked, T., Chandra, T., Aradhye, H., ... & Anil, R. (2016, September). Wide & deep learning for recommender systems. In Proceedings of the 1st Workshop on Deep Learning for Recommender Systems (pp. 7-10). ACM.
    [2] Wang, R., Fu, B., Fu, G., & Wang, M. (2017, August). Deep & cross network for ad click predictions. In Proceedings of the ADKDD'17 (p. 12). ACM.
    [3] Guo, H., Tang, R., Ye, Y., Li, Z., & He, X. (2017). Deepfm: a factorization-machine based neural network for ctr prediction. arXiv preprint arXiv:1703.04247.
    [4] Lian, J., Zhou, X., Zhang, F., Chen, Z., Xie, X., & Sun, G. (2018). xDeepFM: Combining Explicit and Implicit Feature Interactions for Recommender Systems. arXiv preprint arXiv:1803.05170.
    [5] Abadi, M., Barham, P., Chen, J., Chen, Z., Davis, A., Dean, J., ... & Kudlur, M. (2016, November). TensorFlow: a system for large-scale machine learning. In OSDI (Vol. 16, pp. 265-283).
    [6] Goyal, P., Dollár, P., Girshick, R., Noordhuis, P., Wesolowski, L., Kyrola, A., ... & He, K. (2017). Accurate, large minibatch SGD: training imagenet in 1 hour. arXiv preprint arXiv:1706.02677.
    [7] Neill, R., Drebes, A., Pop, A. (2018). Performance Analysis of Just-in-Time Compilation for Training TensorFlow Multi-Layer Perceptrons.

    作者简介

    仲达,2017年毕业于美国罗彻斯特大学数据科学专业,后在加州湾区Stentor Technology Company工作,2018年加入美团,主要负责用户增长组深度学习、强化学习落地业务场景工作。

    鸿杰,2015年加入美团点评。美团平台与酒旅事业群用户增长组算法负责人,曾就职于阿里,主要致力于通过机器学习提升美团点评平台的活跃用户数,作为技术负责人,主导了美团DSP广告投放、站内拉新等项目的算法工作,有效提升营销效率,降低营销成本。

    廷稳,2015年加入美团点评。在美团点评离线计算方向先后从事YARN资源调度及GPU计算平台建设工作。

    招聘

    美团DSP是美团在线数字营销的核心业务方向,加入我们,你可以亲身参与打造和优化一个可触达亿级用户的营销平台,并引导他们的生活娱乐决策。同时,你也会直面如何精准,高效,低成本营销的挑战,也有机会接触到计算广告领域前沿的AI算法体系和大数据解决方案。你会和美团营销技术团队一起推动建立流量运营生态,支持酒旅、外卖、到店、打车、金融等业务继续快速的发展。我们诚邀有激情、有想法、有经验、有能力的你,和我们一起并肩奋斗!参与美团点评站外广告投放体系的实现,基于大规模用户行为数据,优化在线广告算法,提升DAU,ROI, 提高在线广告的相关度、投放效果。欢迎邮件wuhongjie#meituan.com咨询。

    好未来励步英语发布3.0完整学习产品,推出AI智能学习机器人

    $
    0
    0

    雷锋网消息,10月10日,好未来旗下英语品牌励步英语在北京举行“用ai驱动完整学习——励步英语新十年•新产品发布会”。会上,励步英语正式推出3.0完整学习产品,提出“用ai驱动完整学习”的理念。

    发布3.0完整学习产品,形成线上+线下学习闭环

    雷锋网注:励步英语总经理曹伟

    励步英语总经理曹伟在题为“励步新十年,用ai驱动”的演讲中介绍了励步3.0教育公式:(有爱的老师+有爱的内容)*科技智能=孩子完整的学习体验。

    据介绍,围绕线上线下、爱与科技、语言素养三点,对幼儿和小学课程体系与教材全面升级,该3.0产品采取1+6的完整学习路径,即线下专注语言体系化学习+线上课前导学视频预习、课后智能互动练习、AI外教互动课辅助理解、RAZ&ORT在线阅读应用、丰富学习资源浸泡、小步机器人提供AI外教语伴,形成线上+线下的完整学习闭环。

    励步英语联合创始人白云表示,在3.0产品中,励步将实现四大突破:

    第一,课堂的个体化学习数据在线化;第二,实现课堂活动的游戏化和个性化体验的优化;第三,持续强化线下内容和线上内容的内在融合;最后,引领家庭学习智能化。

    据介绍,励步英语于2016年发布2.0产品体系,用科技手段推动线上、线下学习相结合。2.0产品发布一年多来,学员通过励步云学习APP人均每周在线学习时长82分钟,人均每年阅读120本英文图书,达到欧美小学生平均阅读量水平。

    励步云学习平台累计在线语音练习1亿2千多万条,音频总时长7200万分钟,在线阅读超过1200多万本次,阅读理解练习2700万个,在线观看外教互动课时长近9000万分钟。

    布局AI,推出智能学习机器人

    据介绍,励步英语将在AI方面加大投入,具体有:

    • 对学生英语学习中大部分知识点进行精准切片,实现大数据支持下的个性化智能学习;

    • 推出智能穿戴装备、智能课件平台助力老师效率提升;

    • 收集记录学生在智能教室与智能家庭场景下的学习数据。

    曹伟表示,“首先我们通过大数据和人工智能能够把知识有效地切片,能知道知识的架构是什么、知识之间的关系是什么,那么我们对知识的机理就可以更好地理解。在这个基础上可以用大数据、用人工智能,智能化地评估孩子的学习,哪些是他的弱点、哪些是他的强势,一个知识点他到底掌握得怎么样、是不是掌握了。在这些精准评估的基础上,我们可以通过个性化学习资源的推送,通过互联网、AR、VR这种增强型的学习资源,帮助孩子提升个性化的学习效果。”

    随着人工智能技术的迅猛发展,当下的教育公司纷纷在AI技术领域大力布局,以AI智适应学习为代表的AI+教育投资事件不断升温,11月15日,雷锋网将联合乂学教育·松鼠AI和IEEE LTSC,共同举办汇聚国内外顶尖阵容的『全球AI+智适应教育峰会』,免费门票、VIP门票开放申请中,访问大会官网即刻申请:https://gair.leiphone.com/gair/aiedu2018

    另外,此次发布会上,励步英语正式推出全新家庭智能终端——“小步智能机器人”,据介绍,该机器人为好未来励步英语和ROOBO联合打造,集成了英语智能对话、图书馆、音乐厅、动画影院等功能,在使用智能机器人过程中,学习数据通过大数据能力图谱分析与智能算法推送,帮助孩子进行个性化语言学习。

    雷锋网注:小步智能机器人

    虽然科技发展日新月异,对各行各业产生巨大影响,但曹伟认为,在教育行业,科技(AI)再发达,但“爱(ai)”仍然是根本,科技应用可以把知识、技能切片化,更高效地传递和掌握,但教育的本质是影响思想和灵魂。

    最后,曹伟总结了励步英语“用AI驱动完整学习”的理念:第一层,即完整的核心理念——爱与科技;第二层,完整的学习内容,即语言和素养并重;第三层,完整的学习路径或者学习方法,即线上线下并重。

    基于电影知识图谱的智能问答系统(八) -- 终极完结篇 - Appleyk的专栏 - CSDN博客

    $
    0
    0


    基于电影知识图谱的智能问答系统系列章节传送门:

     

    基于电影知识图谱的智能问答系统(一) -- Mysql数据准备

    基于电影知识图谱的智能问答系统(二) -- Neo4j导入CSV文件

    基于电影知识图谱的智能问答系统(三) -- Spark环境搭建

     

    基于电影知识图谱的智能问答系统(四) -- HanLP分词器

    基于电影知识图谱的智能问答系统(五) -- Spark朴素贝叶斯分类器

    基于电影知识图谱的智能问答系统(六) -- 问题训练样本集敲定

    基于电影知识图谱的智能问答系统(七) -- Neo4j语句那点事

     

     

    博主注:本篇不再过多的讲解demo了,集成也很简单,前面几章也已经给本篇做足了铺垫,项目demo中的注释也是非常的详细,最后会附上整个项目的下载地址,如有问题,另留言吧。

     

     

    一、效果预览

     

     

    (1)电影简介

     

    前端展示:

     

     

    后台效果:

     

     

     

     

     

    (2)电影评分

     

    前端展示:

     

     

     

     

    后台效果:

     

     

     

     

     

     

    (3)电影演员列表

     

    前端展示:

     

     

     

     

    后台效果:

     

     

     

     

     

     

    (4)演员A和演员B合作过哪些电影

     

    前端展示:

     

     

     

     

     

     

    后端效果:

     

     

     

    由于章子怡本来是一个完整的人名,但是HanLP分词的时候,却意外的“失手”了,因此导致最后查询无果

     

    我们再换个问题试验一把

     

     

     

    后台效果:

     

     

     

     

     

    (5)某演员出演过那种类型的电影或演过某种类型的电影有哪些

     

     

     

     

    ......etc,其余不在做演示,下面直接来看如何利用Spring-Boot搭建我们的智能问答系统

     

     

     

    二、项目目录结构图

     

     

     

     

     

     

    三、Movie节点类

     

     

    这里只拿电影信息的节点类来进行演示,比如,movie对应的节点在Java中定义类如下:

     

     

    package com.appleyk.node;
    
    import java.util.List;
    
    import org.neo4j.ogm.annotation.NodeEntity;
    import org.neo4j.ogm.annotation.Relationship;
    
    import com.fasterxml.jackson.annotation.JsonProperty;
    
    @NodeEntity
    public class Movie extends BaseEntity{
    
    	private Long mid;
    	private Double rating;
    	private String releasedate;
    	private String title;
    	private String introduction;
    
    	@Relationship(type = "is")
    	@JsonProperty("电影类型")
    	private List<Genre> genres;
    
    	public Movie() {
    
    	}
    
    	public Long getMid() {
    		return mid;
    	}
    
    	public void setMid(Long mid) {
    		this.mid = mid;
    	}
    
    	public Double getRating() {
    		return rating;
    	}
    
    	public void setRating(Double rating) {
    		this.rating = rating;
    	}
    
    	public String getReleasedate() {
    		return releasedate;
    	}
    
    	public void setReleasedate(String releasedate) {
    		this.releasedate = releasedate;
    	}
    
    	public String getTitle() {
    		return title;
    	}
    
    	public void setTitle(String title) {
    		this.title = title;
    	}
    
    	public String getIntroduction() {
    		return introduction;
    	}
    
    	public void setIntroduction(String introduction) {
    		this.introduction = introduction;
    	}
    
    	public List<Genre> getGenres() {
    		return genres;
    	}
    
    	public void setGenres(List<Genre> genres) {
    		this.genres = genres;
    	}
    
    }

     

    其中属性和neo4j中的movie节点的属性一一对应

     

     

     

     

     

    电影信息节点里面带有关系is,对应neo4j中该电影的类型

     

     

     

    通过Controller对外提供的查询接口如下:

     

     

     

    外部调用效果如下:

     

     

     

     

     

    四、加载自定义带词性的字典数据

     

     

     

    注:不要使用HanLP提供的自定义词典路径,因为这个除了不能随心所欲的定义分词的词性以外,还极容易出现分词紊乱,词性对不上的bug,为了满足我们对专有电影名、电影分数及电影类型词性的定义,我们使用额外加载的方式设置HanLP的自定义分词,application.properties中设置自定义词典的路径如下:

     

     

     

     

     

     

     

     

     

     

    这三个文件的下载地址: https://pan.baidu.com/s/13PYsF2X3v7BzkPFG37kKuw

     

    注:也可以根据自己的需求进行设置

     

     

    五、项目完整地址

     

     

    GitHub下载地址: Spring-Boot集成Neo4j并利用Spark的朴素贝叶斯分类器实现基于电影知识图谱的智能问答系统

     

     

     

    完结!!!

     

     

     

    番外篇:如何将项目导入到IDEA并运行测试效果?

     

    (1)IDEA项目结构图(导入pom文件,配置好Maven后如下)

     

     

     

     

    (2)项目配置文件

     

     

     

     

    (3)内嵌html测试前端访问页面

     

     

     

     

     

    (4)启动项目

     

     

     

     

     

     

     

    (5)浏览器访问主页Index.html

     

    [译] 谷歌团队的容器运维最佳实践

    $
    0
    0

    谷歌大神们带你进行容器运维最佳实践

    本文介绍了一组使容器更易于运维的最佳实践。这些实践涉及安全性、监控和日志记录等广泛的主题,旨在使应用程序更容易在 Kubernetes Engine 和一般的容器中运行。这里讨论的许多实践都受到 12 因素方法的启发 ,12 因素方法是一个构建云原生应用程序的优质资源。

    使用容器的原生日志记录机制

    重要性:高

    作为应用程序管理的一部分,日志中包含宝贵的信息,可让人了解应用程序中发生的事件。Docker 和 Kubernetes 致力于简化日志管理。

    在传统服务器上,你可能需要将日志写入特定文件并处理日志轮换以避免填满磁盘。如果有高级日志系统,则可以将这些日志转发到远程服务器来集中它们。

    通过容器可以将日志写入 stdout 和 stderr,因而容器提供了一种简单且标准化的方式来处理日志。Docker 捕获这些日志行,并允许你使用 docker logs 命令访问它们。作为应用程序开发人员,你不需要实现高级日志记录机制,试试用原生的日志记录机制吧。

    平台运营商必须提供一个系统来集中日志并进行搜索,你可以使用 Kubernetes Engine 提供的 fluentd 和 Stackdriver Logging。其他常见方法包括使用 EFK (Elasticsearch,Fluentd,Kibana)栈。

    图 1. Kubernetes 中典型的日志管理系统图

    JSON 日志

    大多数日志管理系统实际上是时序数据库,用于存储时间索引文档。这些文档通常以 JSON 格式提供。在 Stackdriver Logging 和 EFK 中,单个日志行和一些元数据(容器组、容器、节点等相关信息)一起被存储为一个文档。

    你可以直接通过将不同字段以 JSON 格式进行日志记录。然后,可以根据这些字段更有效地搜索日志。

    例如,考虑将以下日志转换为 JSON 格式:

    [2018-01-01 01:01:01] foo - WARNING - foo.bar - There is something wrong.

    这是转换后的日志:

    {"date": "2018-01-01 01:01:01","component": "foo","subcomponent": "foo.bar","level": "WARNING","message": "There is something wrong."
    }

    通过这种转换,你可以在日志中轻松搜索所有 WARNING 级别日志或 foo.bar 组件中的所有日志。

    如果你决定记录 JSON 格式的日志,请注意必须在每一行上加入事件才能正确解析。在实际中,它看起来是下面这样:

    {"date":"2018-01-01 01:01:01","component":"foo","subcomponent":"foo.bar","level": "WARNING","message": "There is something wrong."}

    如你所见,结果远不如正常的日志可读。如果决定使用此方法,请确保你的团队不会严重依赖手动日志检查。

    边车模式的记录聚合器

    某些应用程序(如 Tomcat)无法通过简单配置来生成日志。这些应用程序在磁盘上写入不同的日志文件,所以在 Kubernetes 中处理它们的最佳方法是使用边车模式进行日志记录。边车是一个小容器,与应用程序在同一个 pod 中运行。有关边车的更详细信息,请参阅 Kubernetes 官方文档 (https://kubernetes.io/docs/concepts/cluster-administration/logging/#sidecar-container-with-a-logging-agent)。

    在这种解决方案中,你为应用程序的边车容器添加一个日志代理(在同一 pod 中,)并在两个容器之间共享 emptyDir 卷,可参考 GitHub 上的这个 YAML 示例:https://github.com/kubernetes/contrib/blob/0.7.0/logging/fluentd-sidecar-gcp/logging-sidecar-example.yaml。然后,配置应用程序将日志写入共享卷,接着配置日志代理进行读取,并转发到需要的地方。

    在此模式中,因为没有使用 Docker 和 Kubernetes 原生的日志记录机制,所以必须处理日志轮换。如果你的日志代理程序不处理日志轮换,则同一 pod 中的另一个边车容器会处理。

    图 2. 日志管理的边车模式

    确保容器是无状态且不可变的

    重要性:高

    如果你是第一次尝试容器,请不要将它们视为传统服务器。比如,你可能想要在正在运行的容器内更新应用程序,或者在出现漏洞时给正在运行的容器打补丁。从根本上说,容器不是以这种方式工作的。它们被设计成了无状态且不可改变。

    无状态

    无状态意味着任何状态(任何类型的持久数据)都存储在容器之外。这个外部存储可以采取多种形式,具体取决于你的需求:

    • 要存储文件,我们建议使用 Cloud Storage 等对象存储。

    • 要存储用户会话等信息,我们建议使用外部的低延迟键值存储,例如 Redis 或 Memcached。

    • 如果需要块级存储(例如数据库),则可以使用连接到容器的外部磁盘。对于 Kubernetes Engine,我们建议使用 持久化磁盘。

    通过以上方法将数据从容器本身中移出,这意味着可以随时干净地关闭和销毁容器,而不必担心数据丢失。如果创建了一个新容器来替换旧容器,则只需将新容器连接到同一数据存储区或将其绑定到同一磁盘即可。

    不变性

    不可变意味着容器在其生命周期内不会被修改:没有更新,没有补丁,没有配置更改。如果必须更新应用程序代码或打补丁,则需要构建新镜像并重新部署。不变性使部署更安全,更可重复。如果需要回滚,只需重新部署旧镜像即可。此方法允许你在每个环境中部署相同的容器镜像,使它们尽可能一致。

    为了在不同环境中使用相同的容器镜像,我们建议你外部化容器配置(侦听端口,运行时选项等)。容器通常配置有环境变量或挂载到特定路径上的配置文件。在 Kubernetes 中,你可以使用 Secrets 和 ConfigMaps 作为环境变量或文件将配置注入到容器中。

    如果需要更新配置,请使用更新的配置部署新容器(基于相同的镜像)。

    图 3. 如何使用挂载到 pod 配置文件中的 ConfigMaps 更新部署中的配置

    无状态和不变性的结合是基于容器的基础设施的卖点之一。这种组合允许你自动化部署并提高其频率和可靠性。

    避免使用特权容器

    重要性:高

    在虚拟机或裸机服务器中,你会避免使用 root 用户运行应用程序,原因很简单:如果应用程序受到攻击,攻击者就可以完全访问服务器。出于同样的原因,请避免使用特权容器。特权容器是一个容器,可以访问主机的所有设备,绕过容器的几乎所有安全功能。

    如果你认为需要使用特权容器,请考虑以下备选方案:

    • 通过 Kubernetes 的 securityContext 选项或 Docker 的 --cap-add 标志为容器提供特定功能 。该 Docker 文档(https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities) 同时列出了默认启用和必须明确启用的功能。

    • 如果你的应用程序必须修改主机设置才能运行,请在边车容器或初始化容器中修改这些设置 。与你的应用程序不同,这些容器不需要暴露于内部或外部流量,更加独立。

    • 如果需要在 Kubernetes 中修改 sysctls,请使用 专用注解。

    在 Kubernetes 中,特权容器可以被特定的 Pod 安全策略禁止 。Pod 安全策略是集群管理员配置和管理的 Kubernetes 对象,它强制执行对 pod 的特定要求。在 Kubernetes 集群中,你无法创建违反这些要求的 pod。

    使应用程序易于监控

    重要性:高

    与日志一样,监控是应用程序管理的一个组成部分。在许多方面,监控容器化应用的原则与非容器化应用的监控相同。但是,由于容器化的基础架构往往是高度动态的,伴随着频繁创建或删除的容器,你无法每次都去重新配置监控系统。

    你可以区分两种主要的监控类型:黑盒监控和白盒监控。黑盒监控是指从外部检查应用程序,你就是最终用户。如果你想要最终提供的服务可用且有效,则黑盒监控非常有用。由于它位于基础设施外部,因此黑盒监控在传统基础设施和容器化基础设施之间没有区别。

    白盒监控是指使用某种特权访问检查应用程序,并收集最终用户无法查看的度量指标。由于白盒监控必须检查基础架构的最深层,因此传统基础架构和容器化基础架构的差异很大。

    Prometheus 是 Kubernetes 社区中用于白盒监控的一个流行选择,可以自动发现必须监控的容器。Prometheus 以期望的特定格式获取容器组的指标。Stackdriver 能够监控 Kubernetes 集群,应用也可以运行自己的的 Prometheus。

    以下是一个 Stackdriver Kubernetes Monitoring 的演示实例:

    图 4. Stackdriver Kubernetes Monitoring 中的仪表板

    要从 Prometheus 或 Stackdriver Kubernetes Monitoring 中受益,应用程序需要按照 Prometheus 的格式公开指标。你可以按照以下两种方法来做。

    HTTP 端点度量

    HTTP 端点度量的工作方式与下文提到的公开应用程序的运行状况的端点类似 。它通常在 /metrics URI 上公开应用程序的内部指标。响应如下:

    http_requests_total{method="post",code="200"} 1027
    http_requests_total{method="post",code="400"}    3
    http_requests_total{method="get",code="200"} 10892
    http_requests_total{method="get",code="400"}    97

    在这个例子中,http_requests_total 是度量,method 和 code 是标签,最右边的数字是该指标对于这些标签的值。上图中所示,自启动以来,该应用程序已使用 400 错误码响应了 97 次 HTTP 的 GET 请求。

    通过已有的多种语言的 Prometheus 客户端库,可以轻松生成此 HTTP 端点 。 OpenCensus 还可以使用此格式(以及许多其他功能)导出指标。不要将此端点暴露给公共网络。

    Prometheus 官方文档 (https://prometheus.io/docs/introduction/overview/)详细介绍了该主题。你还可以阅读站点可靠性工程的第 6 章 ,以了解有关白盒(和黑盒)监控的更多信息。

    监控中使用边车模式

    并非所有应用程序都可以使用 /metrics HTTP 端点进行检测。为了保持标准化监控,我们建议使用边车模式以正确的格式导出指标。

    日志聚合边车模式 部分介绍如何使用边车容器来管理应用程序日志。你可以使用相同的模式进行监控:边车容器托管监控代理程序,该代理程序将应用程序公开的度量标准转换为全局监控系统可以理解的格式和协议。

    考虑一个具体示例:Java 应用程序和 Java Management Extensions(JMX)。许多 Java 应用程序使用 JMX 公开指标。利用 jmx_exporter,你可以不必重写应用程序就公开 Prometheus 格式的指标。jmx_exporter 通过 JMX 从应用程序收集指标,并通过 Prometheus 可以读取的 /metrics 端点公开它们。这种方法还具有限制 JMX 端点暴露的优点,因为它可以用来修改应用程序设置。

    图 5. 用于监控的边车模式

    暴露应用程序的健康状况

    重要性:中等

    为了便于在生产中进行管理,应用程序必须将其状态传达给整个系统:应用程序是否正在运行?它健康吗?它准备好接收流量吗?它是如何表现的?

    Kubernetes 有两种类型的健康检查:活性探针(liveness probes )和就绪探针(readiness probes)。如下文所述,每个都有特定的用途。你可以通过多种方式实现这两种方式(包括在容器内运行命令或检查 TCP 端口),但首选方法是使用此最佳实践中描述的 HTTP 端点。

    注意:本节中给出的路径只是一种约定。HTTP 端点的实际路径可能因应用程序而异。

    活性探针

    实现活性探针的推荐方法是让应用程序公开 /health HTTP 端点。在此端点上收到请求后,如果认为健康,应用程序应发送“200 OK”响应。在 Kubernetes 中,健康意味着容器不需要被杀死或重新启动。影响健康的因素因应用程序而异,但通常意味着以下内容:

    • 应用程序正在运行。

    • 它的主要依赖性得到满足(例如,它可以访问其数据库)。

    就绪探针

    实现就绪探针的推荐方法是让应用程序公开 /ready HTTP 端点。在此端点上收到请求后,如果应用程序已准备好接收流量,则应发送“200 OK”响应。准备接收流量意味着以下内容:

    • 该应用程序是健康的。

    • 完成任何潜在的初始化步骤。

    • 发送到应用程序的任何有效请求都不会导致错误。

    Kubernetes 使用就绪探针来编排应用程序的部署。如果更新部署,Kubernetes 将对属于该部署的 pod 进行滚动更新。默认更新策略是一次更新一个 pod:Kubernetes 在更新下一个 pod 之前等待新 pod 准备就绪(如就绪探针所示)。

    注意:在许多应用程序中,/health 和 /ready 端点合并为一个 /health 端点,因为它们的健康状态和就绪状态之间没有真正的区别。

    避免以 root 身份运行

    重要性:中等

    容器提供隔离:使用默认设置,Docker 容器内的进程无法访问来自主机或其他并置容器的信息。但是,由于容器共享主机的内核,因此隔离不像虚拟机那样完整。攻击者可以找到未知的漏洞(在 Docker 或 Linux 内核本身中),这些漏洞将允许攻击者从容器中逃脱。如果攻击者确实发现了漏洞并且你的进程在容器内以 root 身份运行,则他们将获得对主机的 root 访问权限。

    图 6. 左侧,虚拟机使用虚拟化硬件。右侧,容器中的应用程序使用主机内核。

    为避免这种可能性,最佳做法是不在容器内以 root 身份运行进程。你可以使用 PodSecurityPolicy 在 Kubernetes 中强制执行此行为 。在 Kubernetes 中创建 pod 时,使用 runAsUser 选项 指定正在运行该进程的 Linux 用户。这种方法会覆盖 Dockerfile 中的 USER 指令。

    实际上,存在挑战。许多软件包都以 root 身份运行其主进程。如果要避免以 root 用户身份运行,设计你的容器使用未知的非特权用户运行。这种做法通常意味着你必须调整各种文件夹的权限。在容器中,如果按照一个容器一个应用的最佳实践,并且一个应用一个用户(最好不是 root 用户),则授予所有用户对文件夹和文件的读写权限不是问题 。

    检查容器是否符合此最佳实践的一种简单方法是在本地使用随机用户运行容器并测试是否正常工作。替换 [YOUR_CONTAINER] 为你的容器名称。

    docker run --user $((RANDOM + 1))[YOUR_CONTAINER]

    如果容器需要外部卷,则可以配置 fsGroup Kubernetes 选项 以将此卷的所有权授予给特定的 Linux 组。此配置解决了外部文件所有权的问题。

    如果你的进程由非特权用户运行,则它将无法绑定到 1024 以下的端口。这不是什么大问题,因为你可以配置 Kubernetes 服务将流量从一个端口路由到另一个端口。例如,你可以配置 HTTP 服务器绑定到 8080 端口,并通过 Kubernetes 服务从 80 端口将流量重定向回来。

    仔细选择镜像版本

    重要性:中等

    当你使用 Docker 镜像时,无论是作为 Dockerfile 中的基础镜像,还是作为 Kubernetes 中部署的镜像,你都必须选择正在使用的镜像的标签。

    大多数公共和私有镜像都遵循构建容器最佳实践(https://cloud.google.com/solutions/best-practices-for-building-containers#properly_tag_your_images)中所述的标签系统 。如果镜像使用语义版本控制的系统 ,则必须考虑一些标签细节。

    最重要的是,“latest”标签可以在镜像之间频繁移动。结果是你无法依赖此标签进行可预测或可重现的构建。例如,采用以下 Dockerfile:

    FROM debian:latest
    
    RUN apt-get -y update && \
        apt-get -y install nginx

    如果你在不同的时间使用这个 Dockerfile 构建两次镜像,你最终会得到两个不同版本的 Debian 和 NGINX。相反,考虑这个修订版:

    FROM debian:9.4
    
    RUN apt-get -y update && \
        apt-get -y install nginx

    通过使用更精确的标签,你可以确保生成的镜像始终基于 Debian 的特定子版本。因为特定的 Debian 版本还附带了特定的 NGINX 版本,所以你可以更好地控制正在构建的镜像。

    这个结果不仅适用于构建时,也适用于运行时。如果你在 Kubernetes 清单中引用“latest”标签,则无法保证 Kubernetes 将使用的版本。集群的不同节点可能会在不同时刻拉取相同的“latest”标签。如果标签已经在拉动之间的某个点更新,则最终可能会在不同的节点运行不同的镜像(这是因为同时打上了“latest”标签)。

    理想情况下,你应始终在 FROM 行中使用不可变标签。此标签允许你重现构建。但是,存在一些安全性权衡:你固定使用的版本越多,安全补丁在镜像中的自动化程度就越低。如果你使用的镜像使用正确的语义版本控制,则补丁版本(即“X.Y.Z”中的“Z”)不应具有向后不兼容的更改:你可以使用“X.Y”标签并自动修复错误。

    注意:标签在 Docker 中不是真正不变的。只要镜像的所有者决定更改标签。但是,“X.Y.Z”标签实际上几乎总是不变的。

    设想一个名为“SuperSoft”的软件。假设 SuperSoft 的安全过程是通过新的补丁版本来修复漏洞。你想自定义 SuperSoft,并编写了以下 Dockerfile:

    FROM supersoft:1.2.3
    
    RUN a-command

    一段时间后,供应商发现了一个漏洞,并发布了 SuperSoft 的 1.2.4 版本来解决这个问题。在这种情况下,你可以随时了解 SuperSoft 的补丁并相应地更新 Dockerfile。如果你在 Dockerfile 中使用 FROM supersoft:1.2 进行替换,则会自动拉取新版本。

    最后,你必须仔细检查正在使用的每个外部镜像的标签系统,判定你对构建这些镜像的人员的信任程度,并确定要使用的标签。

     

    来自:https://mp.weixin.qq.com/s/FEqVWBQ1LkQAf6sdX7MPng

     

    facebook-faiss库 - YiLiang - CSDN博客

    $
    0
    0

    三月初,Facebook AI Research(FAIR)开源了一个名为 Faiss 的库,Faiss 主要用于有效的相似性搜索(Similarity Search)和稠密矢量聚类(Clustering of dense vectors),包含了在任何大小的矢量集合里进行搜索的算法。Faiss 上矢量集合的大小甚至可以大到装不进 RAM。这个库基本上是用 C++ 实现的,带有可选的通过 CUDA 提供的 GPU 支持,以及一个可选的 Python 接口。

    通过 Faiss 进行相似性搜索时,10 亿图像数据库上的一次查询仅耗时 17.7 微秒,速度较之前提升了 8.5 倍,且准确度也有所提升。

    耗时 17.7 微秒、提速 8.5 倍,Facebook AI 相似性搜索库 Faiss 的核心奥义究竟在哪?

    除图片检索外,相似性搜索还有更广阔的运用场景。例如,通过搜索数据库来判断某一罪行是否属于较严重的犯罪形式,或有重罪趋势;通过搜索和成功店铺所在地相似的人口特征和环境特征,来寻找零售商新店的最佳位置;通过相似城市的搜索,来衡量所在城市薪资水平是否合理等。

    日前,Facebook 发布了一份关于 Faiss 原理的介绍,36氪对此进行了编译和整理,具体内容如下:

    关于相似性搜索

    传统的数据库由包含符号信息的结构化表格组成。举例来说,一个图片集合的呈现方式是一个列表,这个列表的每一行都有一张索引照片,同时包含图像标识、描述语句等信息。每一行的信息也可以连接其他表格,如一张包含人物的照片可以连接到姓名表上。

    大部分 AI 工具都会生成高维矢量,如以 word2vec 为代表的文字嵌入工具,和用于深度学习训练的 CNN 描述符(Convolutional Neural Net)等。在这篇文章中,我们将阐述为什么高维矢量数据比固定符号数据更强大且灵活。不过,使用 SQL 查询的传统数据库并不适用这些新型表述方式:首先,海量的多媒体信息流创造了数十亿矢量;其次,更重要的一点是,找到类似的条目意味着要找到类似的高维矢量,这对标准的查询语言来说是极其低效甚至是不可能的。

    如何应用矢量表述?

    让我们假设你现在有一张建筑的照片,这个建筑是某个中型城市的市政府,但你已经记不清名字了,然后你想在整个图片集合中找到这个建筑的其他所有照片。这种情况下使用传统的 SQL 语句来完成关键字查询是不可能的,因为你已经忘记了这个城市的名字。

    相似性搜索此刻却能派上用场,图片的矢量描述是为了针对相似图片制造出相似的矢量,这些矢量被定义为临近欧几里得空间的向量。

    矢量描述的另一个应用是分类。假设你需要一个分类器来判定一个图像集合中哪些图片代表的是小雏菊。分类器的训练是一个比较知名的过程:该算法会输入雏菊图像和非雏菊图像(如汽车、绵羊、玫瑰、向日葵等)。如果分类器是线性的,则会输出一个分类矢量,

    所以,针对相似性搜索和分类,我们需要进行如下操作:

    • 给定一个查询矢量,回到欧几里德空间中最接近这个矢量的数据库对象列表。

    • 给定一个查询矢量,回到有最高向量点积德数据库对象列表。

    有一个挑战是,我们希望这些操作可以以数十亿矢量的规模来运行。

    软件包

    目前投入应用的软件工具还不足以支持上述数据研究的进行。传统的 SQL 数据库系统不切实际,因为它们是针对 hash-based searches 或 1D interval searches 而优化的。相似性搜索功能在 OpenCV 这类工具包中受到的扩展性限制较大,一些针对“小”数据集(比如仅 100 万个矢量)的相似性搜索算法库也是如此。

    耗时 17.7 微秒、提速 8.5 倍,Facebook AI 相似性搜索库 Faiss 的核心奥义究竟在哪?

    Faiss 是一个打破了以上提到的所有限制的算法库,其优点有:

    • Faiss 提供了多种相似性搜索方法,可以针对不同的使用方法,进行跨度较大的功能取舍。

    • Faiss 针对内存使用和速度进行了优化。

    • Faiss 为最相关的索引方法提供了先进的 GPU 实现方案。

    评估相似性搜索

    一旦矢量被学习机器提取(从图像、视频、文档等),就可以被输入到相似性搜索库中。

    我们拥有一个作为参考的暴力算法,能精确而详尽地计算出所有相似性,并且返回到最相似的元素列表中。这提供了一个“黄金标准”参考结果列表。但值得注意的是,高效实施暴力算法并不容易,并且暴力算法经常会影响到系统其他组件的效果。

    如果我们愿意牺牲一部分精确度,相似性搜索的速度可以提高几个数量级,但是会偏离参考结果。例如,将图像相似性搜索的第一和第二个结果交换,可能没有什么太大影响,因为它们都是给定查询的正确结果。加速搜索涉及一些数据集合的预处理,我们将这项操作称之为“索引”。

    这使我们确定了三个感兴趣的研究指标:

    • 速度

    在整个数据库中寻找 10 个(或其他数字)最相近的矢量需要花费多长时间?最好的情况是比暴力算法耗时短,否则索引就没有任何意义了。

    • 内存用量

    这个方法需要多少 RAM?比传统矢量多还是少?Faiss 仅支持在 RAM 搜索,因为磁盘数据库的数量级要慢一些,即使是 SSD。

    • 准确度

    返回的结果列表和暴力搜索结果的匹配度如何?准确度可以通过对结果列表中优先返回最邻近单位的检索数量进行评估,或者是通过衡量 10 个最先返回的最邻近单位的平均分数来评估(这种方法被称为“10-intersection”)。

    我们通常会评估固定内存使用速度和精度之间的关联。Faiss 采用的是压缩原始矢量的方法,因为这是扩展到十亿级矢量数据库的唯一方法:以每个矢量占用 32 字节计,当规模达到 10 亿矢量后,这些矢量会占据大量的存储空间。

    大部分索引库包含约 100 万个矢量,我们认为这个规模很小。比如说, nmslib 拥有非常有效的算法,速度比 Faiss 快,但同时需要更多的存储空间。

    评估十亿个矢量

    工程界中对于这种规模的数据集并没有一个完善的标准,我们比较了一些研究结果,并进行评估。

    Deep1B 是一个有 10 亿张照片的图像库,我们在这上面评估精度。每张照片都被卷积神经网络(CNN)处理过,且 CNN 中的一个激活图(activation map)会被当作图像描述符(descriptor)。这些矢量可以和欧氏距离进行比较,以量化图像之间的相似度。

    Deep1B 有一个小的图像检索库,且处理了这些图像的暴力算法提供了一个真实的相似性搜索结果。因此,如果我们运行搜索算法,我们可以评估结果中的 1-recall@1。

    选择索引

    为了评估,我们把内存空间大小限定为 30GB RAM。这个内存空间约束我们选择索引方法和参数。在 Faiss 中,索引方法具体表现为一个字符串,如:OPQ20_80,IMI2x14,PQ20。

    这个字符串指示了用于矢量预处理的具体步骤(OPQ20_80),一个指示数据库应该如何被分割的选择机制(IMI2x14),以及一个指示产品量化编码矢量的编码组件(PQ20),这个编码组件会产生成 20 字节的代码。因此,内存使用(包括间接使用)低于 30 GB RAM。

    我们知道这听起来有点“太技术”,因此 Faiss 的开发文件会提供相应的指导,即如何根据你的需求来提供最合适的索引类型。

    一旦确定了索引类型,检索就开始了。这个算法会处理 10 亿个矢量,并将这些矢量置于所一个索引中。索引可以存储在磁盘上,或者立即使用,同时索引中的搜索、添加/删除可以交叉输入。

    在索引中检索

    索引就绪后,可以设置一组搜索事件参数来调整检索方法。为了评估,我们使用单线程进行搜索。由于内存使用量已经被固定,我们需要优化精确度和搜索时间之间的权衡。这也意味着,我们要能在尽可能少的时间内,使 1-recall@1 达到 40%。

    幸好,Faiss 有一个自动调节机制,可以扫描参数空间,并收集提供最佳操作点的空间,也就是在给定精度的情况下最好的潜在搜索时间,反之亦然。在 Deep1B 中,操作点可以进行可视化,如下图所示:

    耗时 17.7 微秒、提速 8.5 倍,Facebook AI 相似性搜索库 Faiss 的核心奥义究竟在哪?

    在这个量化表中,我们可以看到,使 1-recall@1 达到 40% 的查询时间少于 2 微秒/矢量,或者将时间限定为 0.5 微秒,我们可以达到 30%。2 微秒的检索时间意味着在一个单核上每秒查询 500 次(500 QPS)。

    这个结果可以和这个领域中最先进的研究结果进行比较。Babenko 和 Lempitsky 于 2016 年撰写了一篇名为 “ Efficient Indexing of Billion-Scale Datasets of Deep Descriptors” 的论文,论文中提到,使用 Deep1B 时,他们需要花费 20 微秒来使 1-recall@1 达到 45%。

    用 GPU(图形处理器)处理十亿级数据集

    许多的研究都在致力于  GPU 的实施,在原生多 GPU 的支持下,产生了令人惊讶的单机性能。GPU 的实施可以看作是对应 CPU 的替代,使用 GPU 时你甚至不需要知道 CUDA API。Faiss 支持所有 2012 年后发布的英伟达 GPU(开普勒,计算能力 3.5+)。

    我们希望将  roofline model 作为指导,它指出,开发者应尽可能让内存带宽或浮点单位饱和。Faiss GPU 在单个 GPU 上的速度比相应的 Faiss CPU 快 5-10倍。如果是使用新的帕斯卡级硬件,如英伟达 P100,那么速度会快 20 倍以上。

    以下是一些性能方面的数字:

    • 通过相似索引,可以在 35 分钟内(包括索引构建时间),在四路 Maxwell Titan X  GPU 上构建一个简单的 k-nearest-neighbor 图(k=10),基于 YFCC100M 数据集合 9500 万图像的 128D CNN 描述符,以及 0.8 的10-intersection。

    • 十亿矢量的 k-nearest-neighbor 图也已实现。开发者可以在 Deep1B 数据集上创建强力的 k-nearest-neighbor 图(k=10),0.65 的 10-intersection 在四路 Maxwell Titan X  GPU 下需要 12 个小时。而 0.8 的 10-intersection 在八路帕斯卡 P100-PCle GPU 上也需要 12 个小时。画质较低的图可以在五小时内通过 Titan X 生成。

    • 其他方面的性能也非常惊人。例如,构建上述 Deep1B 索引需要用 k-means 聚类生成 262,144 个几何中心和 6710 万 120-dim 矢量。在 25 E-M 次迭代下,四路 Titan X GPU(12.6 tflp/s)需要花 139 分钟处理,八路帕斯卡 P100 GPU(40 tflop/s)则需花 43.8 分钟。要注意的是聚类的训练集并不需要和 GPU 显存匹配,因为数据会按需及时导入到 GPU 中,不会影响性能。

    其他技术

    基于诸多研究成果和大量工程技术,Facebook AI  Research 团队自 2015 年开始研发 Faiss。针对 Faiss, Facebook 选择优化几项基础技术,尤其是在 CPU 方面,Facebook 大量运用了如下技术:

    • 多线程充分利用多核性能,并在多路 GPU 上进行并行搜索;

    • 运用 matrix/matrix 乘法在 BLAS 算法库中进行高效、精确的距离计算。如果没有 BLAS,高效的暴力算法很难呈现最优效果。BLAS/LAPACK 是 Faiss 必备的前提软件;

    • 机器 SIMD 矢量化和 popcount 被用于加速孤立向量的距离计算。

    GPU 方面

    由于典型的 CPU 算法(如 heap selection)并不适用于 GPU,此前应用在相似性搜索上的 GPU 和 k-selection(寻找 k-minimum 或 k-maximum 因素)一直存在性能方面的问题。对 Faiss GPU 来说,我们设计了文献记载中已知的最快的小 k-selection 算法(k<=1024)。所有的中间状态都被保存在寄存器中,这样有助于提升其速度。它能将输入的数据以 single pass 的方式进行 k-select,运行潜在峰值性能的 55%,这取决于峰值 GPU 的显存带宽。由于其状态仅保留在寄存器文件中,并且能和其他内核一起使用,从而能进行快速准确的相似搜索算法。

    研究领域中,许多人把注意力放在高效的平铺策略和面向相似性搜索的内核执行上。Multi-GPU 支持由分片或复制数据来提供,开发者不会受到单 GPU 显存大小的限制。半精度浮点支持(float 16)也有提供,使得开发者可以在支持的 GPU 上进行完整的 float 16 运算。float 16 这样的编码矢量能在几乎不损失精度的情况下提高速度。

    总之,连续不断的超量因素在实施中非常重要,Faiss 做了许多关注工程细节的痛苦的工作。

    Faiss上手

    Faiss 以 C++ 实现,支持 Python。想要上手,需要从 GitHub 上获取 Faiss,编译后将 Faiss 模块导入到 Python 中。Faiss 与 numpy 完全集成,所有的函数都是用 numpy 数组来实现的(in float32)


    特征向量内存快速查找库 GitHub - spotify/annoy: Approximate Nearest Neighbors in C++/Python optimized for memory usage and loading/saving to disk

    $
    0
    0

    背景

    还有一些其他库可以进行最近邻搜索。Annoy几乎和最快的库一样快(见下文),但实际上还有另一个功能让Annoy与众不同:它能够将静态文件用作索引。特别是,这意味着您可以跨进程共享索引。Annoy还将创建索引与加载它们分离,因此您可以将索引作为文件传递并快速映射到内存中。Annoy的另一个好处是它试图最小化内存占用,因此索引非常小。

    为什么这有用?如果你想找到最近的邻居并且你有很多CPU,你只需要RAM来适应索引。您还可以传递和分发静态文件 在生产环境,Hadoop作业等使用。任何进程都可以将索引加载(mmap)到内存中,并且能够立即执行查找。

    我们在 Spotify上使用它来获取音乐推荐。在运行矩阵分解算法之后,每个用户/项目可以表示为f维空间中的向量。此库可帮助我们搜索类似的用户/项目。我们在高维空间中拥有数百万个轨道,因此内存使用是首要考虑因素。

    Annoy是由 Erik BernhardssonHack Week期间的几个下午建造的。

    功能摘要

    • 欧几里德距离曼哈顿距离余弦距离汉明距离点(内)产品距离
    • 余弦距离相当于归一化向量的欧几里德距离= sqrt(2-2 * cos(u,v))
    • 如果你没有太多的尺寸(如<100),效果会更好,但即使是最多1000个维度,它也表现得非常出色
    • 内存使用量小
    • 允许您在多个进程之间共享内存
    • 索引创建与查找分开(特别是在创建树后,您无法添加更多项目)
    • 本机Python支持,使用2.6,2.7,3.3,3.4,3.5进行测试

    Annoy

    Annoy example
    https://img.shields.io/travis/spotify/annoy/master.svg?style=flathttps://ci.appveyor.com/api/projects/status/github/spotify/annoy?svg=true&pendingText=windows%20-%20Pending&passingText=windows%20-%20OK&failingText=windows%20-%20Failinghttps://img.shields.io/pypi/v/annoy.svg?style=flat

    Annoy ( Approximate Nearest NeighborsOh Yeah) is a C++ library with Python bindings to search for points in space that are close to a given query point. It also creates large read-only file-based data structures that are mmappedinto memory so that many processes may share the same data.

    Install

    To install, simply do sudo pip install annoyto pull down the latest version from PyPI.

    For the C++ version, just clone the repo and #include "annoylib.h".

    Background

    There are some other libraries to do nearest neighbor search. Annoy is almost as fast as the fastest libraries, (see below), but there is actually another feature that really sets Annoy apart: it has the ability to use static files as indexes. In particular, this means you can share index across processes. Annoy also decouples creating indexes from loading them, so you can pass around indexes as files and map them into memory quickly. Another nice thing of Annoy is that it tries to minimize memory footprint so the indexes are quite small.

    Why is this useful? If you want to find nearest neighbors and you have many CPU's, you only need the RAM to fit the index once. You can also pass around and distribute static files to use in production environment, in Hadoop jobs, etc. Any process will be able to load (mmap) the index into memory and will be able to do lookups immediately.

    We use it at Spotifyfor music recommendations. After running matrix factorization algorithms, every user/item can be represented as a vector in f-dimensional space. This library helps us search for similar users/items. We have many millions of tracks in a high-dimensional space, so memory usage is a prime concern.

    Annoy was built by Erik Bernhardssonin a couple of afternoons during Hack Week.

    Summary of features

    • Euclidean distance, Manhattan distance, cosine distance, Hamming distance, or Dot (Inner) Product distance
    • Cosine distance is equivalent to Euclidean distance of normalized vectors = sqrt(2-2*cos(u, v))
    • Works better if you don't have too many dimensions (like <100) but seems to perform surprisingly well even up to 1,000 dimensions
    • Small memory usage
    • Lets you share memory between multiple processes
    • Index creation is separate from lookup (in particular you can not add more items once the tree has been created)
    • Native Python support, tested with 2.6, 2.7, 3.3, 3.4, 3.5

    Python code example

    fromannoyimportAnnoyIndeximportrandom
    
    f=40t=AnnoyIndex(f)#Length of item vector that will be indexedforiinxrange(1000):
        v=[random.gauss(0,1)forzinxrange(f)]
        t.add_item(i, v)
    
    t.build(10)#10 treest.save('test.ann')#...u=AnnoyIndex(f)
    u.load('test.ann')#super fast, will just mmap the fileprint(u.get_nns_by_item(0,1000))#will find the 1000 nearest neighbors

    Right now it only accepts integers as identifiers for items. Note that it will allocate memory for max(id)+1 items because it assumes your items are numbered 0 … n-1. If you need other id's, you will have to keep track of a map yourself.

    Full Python API

    • AnnoyIndex(f, metric='angular')returns a new index that's read-write and stores vector of fdimensions. Metric can be "angular", "euclidean", "manhattan", "hamming", or "dot".
    • a.add_item(i, v)adds item i(any nonnegative integer) with vector v. Note that it will allocate memory for max(i)+1items.
    • a.build(n_trees)builds a forest of n_treestrees. More trees gives higher precision when querying. After calling build, no more items can be added.
    • a.save(fn)saves the index to disk.
    • a.load(fn)loads (mmaps) an index from disk.
    • a.unload()unloads.
    • a.get_nns_by_item(i, n, search_k=-1, include_distances=False)returns the nclosest items. During the query it will inspect up to search_knodes which defaults to n_trees * nif not provided. search_kgives you a run-time tradeoff between better accuracy and speed. If you set include_distancesto True, it will return a 2 element tuple with two lists in it: the second one containing all corresponding distances.
    • a.get_nns_by_vector(v, n, search_k=-1, include_distances=False)same but query by vector v.
    • a.get_item_vector(i)returns the vector for item ithat was previously added.
    • a.get_distance(i, j)returns the distance between items iand j. NOTE: this used to return the squareddistance, but has been changed as of Aug 2016.
    • a.get_n_items()returns the number of items in the index.

    Notes:

    • There's no bounds checking performed on the values so be careful.
    • Annoy uses Euclidean distance of normalized vectors for its angular distance, which for two vectors u,v is equal to sqrt(2(1-cos(u,v)))

    The C++ API is very similar: just #include "annoylib.h"to get access to it.

    Tradeoffs

    There are just two parameters you can use to tune Annoy: the number of trees n_treesand the number of nodes to inspect during searching search_k.

    • n_treesis provided during build time and affects the build time and the index size. A larger value will give more accurate results, but larger indexes.
    • search_kis provided in runtime and affects the search performance. A larger value will give more accurate results, but will take longer time to return.

    If search_kis not provided, it will default to n * n_trees * Dwhere nis the number of approximate nearest neighbors and Dis a constant depending on the metric. Otherwise, search_kand n_treesare roughly independent, i.e. a the value of n_treeswill not affect search time if search_kis held constant and vice versa. Basically it's recommended to set n_treesas large as possible given the amount of memory you can afford, and it's recommended to set search_kas large as possible given the time constraints you have for the queries.

    How does it work

    Using random projectionsand by building up a tree. At every intermediate node in the tree, a random hyperplane is chosen, which divides the space into two subspaces. This hyperplane is chosen by sampling two points from the subset and taking the hyperplane equidistant from them.

    We do this k times so that we get a forest of trees. k has to be tuned to your need, by looking at what tradeoff you have between precision and performance.

    Hamming distance (contributed by Martin Aumüller) packs the data into 64-bit integers under the hood and uses built-in bit count primitives so it could be quite fast. All splits are axis-aligned.

    Dot Product distance (contributed by Peter Sobot) reduces the provided vectors from dot (or "inner-product") space to a more query-friendly cosine space using a method by Bachrach et al., at Microsoft Research, published in 2014.

    More info

    ANN benchmarks

    Source code

    It's all written in C++ with a handful of ugly optimizations for performance and memory usage. You have been warned :)

    The code should support Windows, thanks to Qiang Kouand Timothy Riley.

    To run the tests, execute python setup.py nosetests. The test suite includes a big real world dataset that is downloaded from the internet, so it will take a few minutes to execute.

    Discuss

    Feel free to post any questions or comments to the annoy-usergroup. I'm @fulhackon Twitter.

    faiss相似性搜索和向量聚类库 faiss: A library for efficient similarity search and clustering of dense vectors.

    $
    0
    0

    Faiss是一个有效的相似性搜索和密集向量聚类的库。它包含搜索任意大小的向量集的算法,包括不适合放入RAM的数据集。它还包含用于评估和参数调整的支持代码。Faiss是用C ++编写的,包含Python / numpy的完整包装。一些最有用的算法是在GPU上实现的。它由 Facebook AI Research开发。

    Faiss

    Faiss is a library for efficient similarity search and clustering of dense vectors. It contains algorithms that search in sets of vectors of any size, up to ones that possibly do not fit in RAM. It also contains supporting code for evaluation and parameter tuning. Faiss is written in C++ with complete wrappers for Python/numpy. Some of the most useful algorithms are implemented on the GPU. It is developed by Facebook AI Research.

    NEWS

    NEW: version 1.4.0 (2018-08-30) no more crashes in pure Python code

    NEW: version 1.3.0 (2018-07-12) support for binary indexes

    NEW: latest commit (2018-02-22) supports on-disk storage of inverted indexes, see demos/demo_ondisk_ivf.py

    NEW: latest commit (2018-01-09) includes an implementation of the HNSW indexing method, see benchs/bench_hnsw.py

    NEW: there is now a Facebook public discussion group for Faiss users at https://www.facebook.com/groups/faissusers/

    NEW: on 2017-07-30, the license on Faiss was relaxed to BSD from CC-BY-NC. Read LICENSE for details.

    Introduction

    Faiss contains several methods for similarity search. It assumes that the instances are represented as vectors and are identified by an integer, and that the vectors can be compared with L2 distances or dot products. Vectors that are similar to a query vector are those that have the lowest L2 distance or the highest dot product with the query vector. It also supports cosine similarity, since this is a dot product on normalized vectors.

    Most of the methods, like those based on binary vectors and compact quantization codes, solely use a compressed representation of the vectors and do not require to keep the original vectors. This generally comes at the cost of a less precise search but these methods can scale to billions of vectors in main memory on a single server.

    The GPU implementation can accept input from either CPU or GPU memory. On a server with GPUs, the GPU indexes can be used a drop-in replacement for the CPU indexes (e.g., replace IndexFlatL2with GpuIndexFlatL2) and copies to/from GPU memory are handled automatically. Results will be faster however if both input and output remain resident on the GPU. Both single and multi-GPU usage is supported.

    Building

    The library is mostly implemented in C++, with optional GPU support provided via CUDA, and an optional Python interface. The CPU version requires a BLAS library. It compiles with a Makefile and can be packaged in a docker image. See INSTALL.mdfor details.

    How Faiss works

    Faiss is built around an index type that stores a set of vectors, and provides a function to search in them with L2 and/or dot product vector comparison. Some index types are simple baselines, such as exact search. Most of the available indexing structures correspond to various trade-offs with respect to

    • search time
    • search quality
    • memory used per index vector
    • training time
    • need for external data for unsupervised training

    The optional GPU implementation provides what is likely (as of March 2017) the fastest exact and approximate (compressed-domain) nearest neighbor search implementation for high-dimensional vectors, fastest Lloyd's k-means, and fastest small k-selection algorithm known. The implementation is detailed here.

    Full documentation of Faiss

    The following are entry points for documentation:

    Authors

    The main authors of Faiss are:

    Reference

    Reference to cite when you use Faiss in a research paper:

    @article{JDH17,
      title={Billion-scale similarity search with GPUs},
      author={Johnson, Jeff and Douze, Matthijs and J{\'e}gou, Herv{\'e}},
      journal={arXiv preprint arXiv:1702.08734},
      year={2017}
    }

    Join the Faiss community

    For public discussion of Faiss or for questions, there is a Facebook public discussion group at https://www.facebook.com/groups/faissusers/

    We monitor the issues pageof the repository. You can report bugs, ask questions, etc.

    License

    Faiss is BSD-licensed. We also provide an additional patent grant.

    图像处理之特征提取 - 简书

    $
    0
    0

    知乎上看到一个话题——
    目前火热的 Deep Learning 会灭绝传统的 SIFT / SURF 特征提取方法吗?


    由于之前研究过SIFT和HOG这两种传统的特征提取方法,故本篇文章先对SIFT和HOG作一综述,并比较二者优缺点。之后,将SIFT和HOG同神经网络特征提取做一对比,浅谈对上述问题的看法。如果能写得快一些,再简单介绍其他几种传统的特征提取的方法——SURF、ORB、LBP、HAAR等等。


    目录

    [1] SIFT(尺度不变特征变换)
    [2] HOG(方向梯度直方图)
    [3] SIFT和HOG的比较
    [4] SIFT/HOG与神经网络特征提取的比较
    [5] 其他传统特征提取的方法(SURF、ORB、LBP、HAAR)


    先对几个概念和问题做一个解释:

    • 图像为什么要灰度化?
    1. 识别物体,最关键的因素是梯度(SIFT/HOG),梯度意味着边缘,这是最本质的部分,而计算梯度,自然就用到灰度图像了,可以把灰度理解为图像的强度。
    2. 颜色,易受光照影响,难以提供关键信息,故将图像进行灰度化,同时也可以加快特征提取的速度。
    • 仿射不变性

    平面上任意两条线,经过仿射变换后,仍保持原来的状态(比如平行的线还是平行,相交的线夹角不变等)

    • 什么是局部特征?局部特征应该具有的特点?

    局部特征从总体上说是图像或在视觉领域中一些有别于其周围的地方;局部特征通常是描述一块区域,使其能具有高可区分度;局部特征的好坏直接会决定着后面分类、识别是否会得到一个好的结果。

    局部特征应该具有的特点:可重复性、可区分性、准确性、有效性(特征的数量、特征提取的效率)、鲁棒性(稳定性、不变性)。


    [1] SIFT(尺度不变特征变换)

    1.1 SIFT特征提取的实质

    在不同的尺度空间上查找关键点(特征点),并计算出关键点的方向。SIFT所查找到的关键点是一些十分突出、不会因光照、仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等。

    1.2 SIFT特征提取的方法

    1. 构建DOG尺度空间:

    模拟图像数据的多尺度特征,大尺度抓住概貌特征,小尺度注重细节特征。通过构建高斯金字塔(每一层用不同的参数σ做高斯模糊(加权)),保证图像在任何尺度都能有对应的特征点,即保证 尺度不变性

    2. 关键点搜索和定位:

    确定是否为关键点,需要将该点与同尺度空间不同σ值的图像中的相邻点比较,如果该点为max或min,则为一个特征点。找到所有特征点后,要去除低对比度和不稳定的边缘效应的点,留下具有代表性的关键点(比如,正方形旋转后变为菱形,如果用边缘做识别,4条边就完全不一样,就会错误;如果用角点识别,则稳定一些)。去除这些点的好处是 增强匹配的抗噪能力和稳定性。最后,对离散的点做曲线拟合,得到精确的关键点的位置和尺度信息。

    3. 方向赋值:

    为了实现 旋转不变性,需要根据检测到的关键点的局部图像结构为特征点赋值。具体做法是用梯度方向直方图。在计算直方图时,每个加入直方图的采样点都使用圆形高斯函数进行加权处理,也就是进行高斯平滑。这主要是因为SIFT算法只考虑了尺度和旋转不变形,没有考虑 仿射不变性。通过高斯平滑,可以使关键点附近的梯度幅值有较大权重,从而部分弥补没考虑仿射不变形产生的特征点不稳定。注意,一个关键点可能具有多个关键方向,这 有利于增强图像匹配的鲁棒性

    4. 关键点描述子的生成:

    关键点描述子不但包括关键点,还包括关键点周围对其有贡献的像素点。这样可使关键点 有更多的不变特性,提高目标匹配效率。在描述子采样区域时,需要考虑旋转后进行双线性插值,防止因旋转图像出现白点。同时,为了保证 旋转不变性,要以特征点为中心,在附近领域内旋转θ角,然后计算采样区域的梯度直方图,形成n维SIFT特征矢量(如128-SIFT)。最后,为了 去除光照变化的影响,需要对特征矢量进行归一化处理。

    1.3 SIFT特征提取的优点

    1. SIFT特征是图像的局部特征,其对旋转、尺度缩放、亮度变化保持不变性,对视角变化、仿射变换、噪声也保持一定程度的稳定性;
    2. 独特性(Distinctiveness)好,信息量丰富,适用于在海量特征数据库中进行快速、准确的匹配;
    3. 多量性,即使少数的几个物体也可以产生大量的SIFT特征向量;
    4. 高速性, 经优化的SIFT匹配算法甚至可以达到实时的要求;
    5. 可扩展性,可以很方便的与其他形式的特征向量进行联合;
    6. 需要较少的经验主义知识,易于开发。

    1.4 SIFT特征提取的缺点

    1. 实时性不高,因为要不断地要进行下采样和插值等操作;
    2. 有时特征点较少(比如模糊图像);
    3. 对边缘光滑的目标无法准确提取特征(比如边缘平滑的图像,检测出的特征点过少,对圆更是无能为力)。

    1.5 SIFT特征提取可以解决的问题:

    目标的自身状态、场景所处的环境和成像器材的成像特性等因素影响图像配准/目标识别跟踪的性能。而SIFT算法在一定程度上可解决:

    1. 目标的旋转、缩放、平移(RST)
    2. 图像仿射/投影变换(视点viewpoint)
    3. 光照影响(illumination)
    4. 目标遮挡(occlusion)
    5. 杂物场景(clutter)
    6. 噪声
    近来不断有人改进,其中最著名的有 SURF(计算量小,运算速度快,提取的特征点几乎与SIFT相同)和 CSIFT(彩色尺度特征不变变换,顾名思义,可以解决基于彩色图像的SIFT问题)。

    [2] HOG(方向梯度直方图)

    2.1 HOG特征提取的实质

    通过计算和统计图像局部区域的梯度方向直方图来构成特征。Hog特征结合SVM分类器已经被广泛应用于图像识别中,尤其在行人检测中获得了极大的成功。

    2.2 HOG特征提取的方法

    1. 灰度化;
    2. 采用Gamma校正法对输入图像进行颜色空间的标准化(归一化),目的是 调节图像的对比度,降低图像局部的阴影和光照变化所造成的影响,同时可以抑制噪音的干扰
    3. 计算图像每个像素的梯度(包括大小和方向),主要是为了捕获轮廓信息,同时 进一步弱化光照的干扰
    4. 将图像划分成小cells(例如6*6像素/cell);
    5. 统计每个cell的梯度直方图(不同梯度的个数),即可形成每个cell的descriptor;
    6. 将每几个cell组成一个block(例如3*3个cell/block),一个block内所有cell的特征descriptor串联起来便得到该block的HOG特征descriptor。
    7. 将图像image内的所有block的HOG特征descriptor串联起来就可以得到该image(你要检测的目标)的HOG特征descriptor了。这个就是最终的可供分类使用的特征向量了。

    2.3 HOG特征提取特点

    1. 由于HOG是在图像的局部方格单元上操作,所以它对图像几何的和光学的形变都能保持很好的不变性,这两种形变只会出现在更大的空间领域上。
    2. 在粗的空域抽样、精细的方向抽样以及较强的局部光学归一化等条件下,只要行人大体上能够保持直立的姿势,可以容许行人有一些细微的肢体动作,这些细微的动作可以被忽略而不影响检测效果。因此HOG特征是特别适合于做图像中的人体检测的。

    [3] SIFT和HOG的比较

    共同点:都是基于图像中梯度方向直方图的特征提取方法

    不同点:

    SIFT 特征通常与使用SIFT检测器得到的兴趣点一起使用。这些兴趣点与一个特定的方向和尺度相关联。通常是在对一个图像中的方形区域通过相应的方向和尺度变换后,再计算该区域的SIFT特征。

    HOG特征的单元大小较小,故可以保留一定的空间分辨率,同时归一化操作使该特征对局部对比度变化不敏感。

    结合SIFT和HOG方法,可以发现SIFT对于复杂环境下物体的特征提取具有良好的特性;而HOG对于刚性物体的特征提取具有良好的特性。

    笔者曾做过一个自然场景分类的实验,发现SIFT的准确率比HOG高,而如果检测像人这种刚性的object,HOG的表现要比SIFT好。

    [4] SIFT/HOG与神经网络特征提取的比较

    众所周知,随着深度学习的发展,通过神经网络提取特征得到了广泛的应用,那么,神经网络提取的特征与传统的SIFT/HOG等特征提取方法有什么不同呢?

    4.1 神经网络提取到的特征

    我们知道,对于一副图像,像素级的特征没有任何价值,而如果特征是一个具有结构性(或者说有含义)的时候,比如摩托车是否具有车把手,是否具有车轮,就很容易把摩托车和非摩托车区分,学习算法才能发挥作用。

    早期,两个科学家Bruno Olshausen和 David Field通过实验研究了这个问题,发现一个复杂图像往往由一些基本结构组成。比如下图:一个图可通过用64种正交的edges(可以理解成正交的基本结构)来线性表示。比如样例的x可以用1-64个edges中的三个按照0.8,0.3,0.5的权重调和而成。而其他基本edges没有贡献,均为0 。


    具体实验可以参考文章:
    Deep Learning(深度学习)关于特征

    小块的图形可以由基本edge构成,更结构化,更复杂的,就需要更高层次的特征表示,高层表达由低层表达的组合而成。如图所示:

    这就是神经网络每层提取到的特征。由于是通过神经网络自动学习到了,因此也是无监督的特征学习过程(Unsupervised Feature Learning) 。直观上说,就是找到make sense的小patch再将其进行combine,就得到了上一层的feature,递归地向上learning feature。在不同object上做training是,所得的edge basis 是非常相似的,但object parts和models 就会completely different了。

    4.2 传统特征提取方法与神经网络特征提取的比较

    观点1:传统特征提取方法的研究过程和思路是非常有用的,因为这些方法具有较强的可解释性,它们对设计机器学习方法解决此类问题提供启发和类比。有部分人认为(也有部分人反对)现有的卷积神经网络与这些特征提取方法有一定类似性,因为每个滤波权重实际上是一个线性的识别模式,与这些特征提取过程的边界与梯度检测类似。同时,池化(Pooling)的作用是统筹一个区域的信息,这与这些特征提取后进行的特征整合(如直方图等)类似。通过实验发现卷积网络开始几层实际上确实是在做边缘和梯度检测。不过事实上卷积网络发明的时候,还没有这些特征提取方法。

    观点2:深度学习的数据需求量大对于视觉来说是个伪命题。许多研究成果已经表明深度学习训练得到的模型具有很强的迁移能力,因此在大数据集上训练完成的模型只要拿过来在小数据集上用就可以,不需要完全重新训练。这种方式在小数据集上的结果往往也比传统方法好。

    观点3:还是需要重新训练的,只能说大数据集训练好的模型提供了一个比较好的参数初始化。而且卷积前几层提取特征仅仅是对分类问题是对的,但是对于一些dense prediction还是不一样,毕竟提取特征不一定有用,还是task dependent。

    观点4:深度学习是一种自学习的特征表达方法,比SIFT/HOG这些依靠先验知识设计的feature的表达效果高。早在13年大家都发现神经网络的最后一层的local特征和SIFT性质差不多,但是表达能力强太多。SIFT能做的事情CNN都能做,表达效果也强,那深度学习取代SIFT是迟早的事情(或者说已经发生的事情)。深度神经网络识别率的提高不需要建立在需求大量训练样本的基础上,拿pre-train好的模型直接用就可以了。在一些没有训练样本的应用(图像分割(image stithing)/ 立体匹配(stereo mathing)) ,可以把卷积层的activation提取出来做stitching的local feature(感觉是一个可以探索的方向)。未来还有SIFT/SURF这种固定特征提取算法的生存空间吗?除非是嵌入式这种计算资源极端受限的情况,但是现在大家都在试着implement CNN FPGA甚至ASIC了。

    观点5:2016年ECCV上举办的一个local feature的工作会,发现在核心匹配问题上,CNN并没有什么突破性的进展。在Oxford大学的VGG组提供的Hpatch数据集上,发现rootsiftpca效果最好,如图:


    那么提出两个问题:(1)现在流行的特征学习方法siamese或triplet等结构是否缺失了什么? (2)虽然CNN可以挖掘patch里面包含的信息并建立对于复杂几何和光照变化的不变性,但是这种学习到的不变性是否过度依赖于数据而无法有效泛化到真实匹配场景中所遇到的影像之间的复杂变化呢?


    [5] 其他传统特征提取的方法(SURF、ORB、LBP、HAAR)

    SURF、ORB、LBP可以参考文章:
    图像特征检测描述(一):SIFT、SURF、ORB、HOG、LBP特征的原理概述及OpenCV代码实现

    5.1 SURF

    前面提到SITF的缺点是如果不借助硬件加速或专门的图像处理器很难达到实现,所以人们就要想办法对SITF算子进行改进,SURF算子便是对SIFT的改进,不过改进之后在算法的运行时间上还是没有质的飞跃。后面要介绍的ORB特征描述算子在运行时间上才是一种质的飞跃。

    SURF主要是把SIFT中的某些运算作了简化。SURF把SIFT中的高斯二阶微分的模板进行了简化,使得卷积平滑操作仅需要转换成加减运算,这样使得SURF算法的鲁棒性好且时间复杂度低。SURF最终生成的特征点的特征向量维度为64维。

    5.2 ORB

    ORB特征描述算法的运行时间远优于SIFT与SURF,可用于实时性特征检测。ORB特征基于FAST角点的特征点检测与描述技术,具有尺度与旋转不变性,同时对噪声及透视仿射也具有不变性,良好的性能使得用ORB在进行特征描述时的应用场景十分广泛。

    ORB特征检测主要分为以下两个步骤:

    ①方向FAST特征点检测:FAST角点检测是一种基于机器学习的快速角点特征检测算法,具有方向的FAST特征点检测是对兴趣点所在圆周上的16个像素点进行判断,若判断后的当前中心像素点为暗或亮,将候定其是否为角点。FAST角点检测计算的时间复杂度小,检测效果突出。FAST角点检测为加速算法实现,通常先对回周上的点集进行排序,排序使得其计算过程大大得到了优化。FAST对多尺度特性的描述是还是通过建立图像金字塔实现的,而对于旋转不变性即方向的特征则引入灰度质心法用于描述特征点的方向。

    ②BRIEF特征描述:BRIEF描述子主要是通过随机选取兴趣点周围区域的若干点来组成小兴趣区域,将这些小兴趣区域的灰度二值化并解析成二进制码串,将串特征作为该特征点的描述子,BRIEF描述子选取关键点附近的区域并对每一位比较其强度大小,然后根据图像块中两个二进制点来判断当前关键点编码是0还是1.因为BRIEF描述子的所有编码都是二进制数的,这样就节省了计算机存储空间。

    5.3 LBP

    LBP(Local Binary Pattern),局部二值模式是一种描述图像局部纹理的特征算子,具有旋转不变性与灰度不变性等显著优点。LBP特征描述的是一种灰度范围内的图像处理操作技术,针对的是输入源为8位或16位的灰度图像。LBP特征是高效的图像特征分析方法,经过改进与发展已经应用于多个领域之中,特别是人脸识别、表情识别、行人检测领域已经取得了成功。LBP特征将窗口中心点与邻域点的关系进行比较,重新编码形成新特征以消除对外界场景对图像的影响,因此一定程度上解决了复杂场景下(光照变换)特征描述问题。

    LBP算法根据窗口领域的不同分为经曲LBP和圆形LBP两种。下面分别介绍:

    ①经典LBP:经典LBP算子窗口为3×3的正方形窗口,以窗口中心像素为阈值,将其相邻8领域像素灰度与中心像素值比较,若中心像素值小于周围像素值,则该中心像素位置被标记为1,否则为0(显然这种规则下,对于中心点大于或等于这两种情况,算法无法区分,后续经过改进引入LBP+与LBP-因子用来区分这两种情况)。图像经过这种遍历操作后,图像就被二值化了,每一个窗口中心的8邻域点都可以由8位二进制数来表示,即可产生256种LBP码,这个LBP码值可以用来反映窗口的区域纹理信息。LBP具体在生成的过程中,先将图像划分为若干个子区域,子区域窗口可根据原图像的尺寸进行调整,而不一定非得为3×3的正方形窗口。一般对于512×640的图像,子区域窗口区域选取大小为16×16。

    ②圆形LBP:经典LBP用正方形来描述图像的纹理特征,其缺点是难以满足不同尺寸和频率的需求。Ojala等人对经典LBP进行了改进,提出了将3×3的正方形窗口领域扩展到任意圆形领域。由于圆形LBP采样点在圆形边界上,那么必然会导致部分计算出来的采样点坐标不是整数,因此这里就需要对得到的坐标像素点值进行处理,常用的处理方法是最近邻插值或双线性插值。

    放一张SIFT/HOG/LBP优缺点、适用范围对比图:

    5.4 HAAR

    人脸检测最为经典的算法Haar-like特征+Adaboost。这是最为常用的物体检测的方法(最初用于人脸检测),也是用的最多的方法。

    训练过程:输入图像->图像预处理->提取特征->训练分类器(二分类)->得到训练好的模型;

    测试过程:输入图像->图像预处理->提取特征->导入模型->二分类(是不是所要检测的物体)。

    Haar-like特征是很简单的,无非就是那么几种,如两矩形特征、三矩形特征、对角特征。后来,还加入了边缘特征、线特征、中心环绕特征等。使用积分图可以加速计算特征。最后,使用集成的方法Adaboost进行训练。


    本篇文章到此结束,有什么错误欢迎指正!


    补充:
    1、SIFT / HOG 不同点:SIFT提取的关键点是角点,HOG提取的是边缘特征。
    2、传统特征提取 / CNN特征提取不同点:传统特征提取方法的检测算子一般是人为设计好的,是经过大量的先验知识总结得到的;CNN特征提取相当于在训练一个个filter(过滤器、卷积核),这些filter相当于传统特征提取方法中的检测算子。因此,CNN特征提取是利用神经网络的自主学习得到的。

    ResNet, AlexNet, VGG, Inception: 理解各种各样的CNN架构

    $
    0
    0

    欢迎交流与转载,文章会同步发布在公众号:机器学习算法全栈工程师(Jeemy110)

    本文翻译自ResNet, AlexNet, VGG, Inception: Understanding various architectures of Convolutional Networks原作者保留版权

    卷积神经网络在视觉识别任务上的表现令人称奇。好的CNN网络是带有上百万参数和许多隐含层的“庞然怪物”。事实上,一个不好的经验规则是:网络越深,效果越好。AlexNet,VGG,Inception和ResNet是最近一些流行的CNN网络。为什么这些网络表现如此之好?它们是如何设计出来的?为什么它们设计成那样的结构?回答这些问题并不简单,但是这里我们试着去探讨上面的一些问题。网络结构设计是一个复杂的过程,需要花点时间去学习,甚至更长时间去自己动手实验。首先,我们先来讨论一个基本问题:

    为什么CNN模型战胜了传统的计算机视觉方法?

    图像分类指的是给定一个图片将其分类成预先定义好的几个类别之一。图像分类的传统流程涉及两个模块: 特征提取分类

    特征提取指的是从原始像素点中提取更高级的特征,这些特征能捕捉到各个类别间的区别。这种特征提取是使用无监督方式,从像素点中提取信息时没有用到图像的类别标签。常用的传统特征包括GIST, HOG, SIFT, LBP等。特征提取之后,使用图像的这些特征与其对应的类别标签训练一个分类模型。常用的分类模型有SVM,LR,随机森林及决策树等。

    上面流程的一大问题是特征提取不能根据图像和其标签进行调整。如果选择的特征缺乏一定的代表性来区分各个类别,模型的准确性就大打折扣,无论你采用什么样的分类策略。采用传统的流程,目前的一个比较好的方法是使用多种特征提取器,然后组合它们得到一种更好的特征。但是这需要很多启发式规则和人力来根据领域不同来调整参数使得达到一个很好的准确度,这里说的是要接近人类水平。这也就是为什么采用传统的计算机视觉技术需要花费多年时间才能打造一个好的计算机视觉系统(如OCR,人脸验证,图像识别,物体检测等),这些系统在实际应用中可以处理各种各样的数据。有一次,我们用了6周时间为一家公司打造了一个CNN模型,其效果更好,采用传统的计算机视觉技术要达到这样的效果要花费一年时间。

    传统流程的另外一个问题是它与人类学习识别物体的过程是完全不一样的。自从出生之初,一个孩子就可以感知周围环境,随着他的成长,他接触更多的数据,从而学会了识别物体。这是深度学习背后的哲学,其中并没有建立硬编码的特征提取器。它将特征提取和分类两个模块集成一个系统,通过识别图像的特征来进行提取并基于有标签数据进行分类。

    这样的集成系统就是多层感知机,即有多层神经元密集连接而成的神经网络。一个经典的深度网络包含很多参数,由于缺乏足够的训练样本,基本不可能训练出一个不过拟合的模型。但是对于CNN模型,从头开始训练一个网络时你可以使用一个很大的数据集如ImageNet。这背后的原因是CNN模型的两个特点:神经元间的权重共享和卷积层之间的稀疏连接。这可以从下图中看到。在卷积层,某一个层的神经元只是和输入层中的神经元局部连接,而且卷积核的参数是在整个2-D特征图上是共享的。


    为了理解CNN背后的设计哲学,你可能会问:其目标是什么?

    (1)准确度

    如果你在搭建一个智能系统,最重要的当然是要尽可能地准确。公平地来说,准确度不仅取决于网路,也取决于训练样本数量。因此,CNN模型一般在一个标准数据集ImageNet上做对比。

    ImageNet项目仍然在继续改进,目前已经有包含21841类的14,197,122个图片。自从2010年,每年都会举行ImageNet图像识别竞赛,比赛会提供从ImageNet数据集中抽取的属于1000类的120万张图片。每个网络架构都是在这120万张图片上测试其在1000类上的准确度。

    (2)计算量

    大部分的CNN模型都需要很大的内存和计算量,特别是在训练过程。因此,计算量会成为一个重要的关注点。同样地,如果你想部署在移动端,训练得到的最终模型大小也需要特别考虑。你可以想象到,为了得到更好的准确度你需要一个计算更密集的网络。因此,准确度和计算量需要折中考虑。

    除了上面两个因素,还有其他需要考虑的因素,如训练的容易度,模型的泛化能力等。下面按照提出时间介绍一些最流行的CNN架构,可以看到它们准确度越来越高。

    AlexNet

    AlexNet是一个较早应用在ImageNet上的深度网络,其准确度相比传统方法有一个很大的提升。它首先是5个卷积层,然后紧跟着是3个全连接层,如下图所示:

    Alex Krizhevs提出的AlexNet采用了ReLU激活函数,而不像传统神经网络早期所采用的Tanh或Sigmoid激活函数,ReLU数学表达为:

    f(x)=max(0,x)

    ReLU相比Sigmoid的优势是其训练速度更快,因为Sigmoid的导数在稳定区会非常小,从而权重基本上不再更新。这就是梯度消失问题。因此AlexNet在卷积层和全连接层后面都使用了ReLU。

    AlexNet的另外一个特点是其通过在每个全连接层后面加上Dropout层减少了模型的过拟合问题。Dropout层以一定的概率随机地关闭当前层中神经元激活值,如下图所示:

    为什么Dropout有效?

    Dropout背后理念和集成模型很相似。在Drpout层,不同的神经元组合被关闭,这代表了一种不同的结构,所有这些不同的结构使用一个的子数据集并行地带权重训练,而权重总和为1。如果Dropout层有 n个神经元,那么会形成 2^{n}个不同的子结构。在预测时,相当于集成这些模型并取均值。这种结构化的模型正则化技术有利于避免过拟合。Dropout有效的另外一个视点是:由于神经元是随机选择的,所以可以减少神经元之间的相互依赖,从而确保提取出相互独立的重要特征。

    VGG16

    VGG16是牛津大学VGG组提出的。VGG16相比AlexNet的一个改进是采用连续的几个3x3的卷积核代替AlexNet中的较大卷积核(11x11,5x5)。对于给定的感受野(与输出有关的输入图片的局部大小),采用堆积的小卷积核是优于采用大的卷积核,因为多层非线性层可以增加网络深度来保证学习更复杂的模式,而且代价还比较小(参数更少)。

    比如,3个步长为1的3x3卷积核连续作用在一个大小为7的感受野,其参数总量为 3*(9C^2),如果直接使用7x7卷积核,其参数总量为 49C^2,这里 C指的是输入和输出的通道数。而且3x3卷积核有利于更好地保持图像性质。VGG网络的架构如下表所示:

    可以看到VGG-D,其使用了一种块结构:多次重复使用同一大小的卷积核来提取更复杂和更具有表达性的特征。这种块结构( blocks/modules)在VGG之后被广泛采用。

    VGG卷积层之后是3个全连接层。网络的通道数从较小的64开始,然后每经过一个下采样或者池化层成倍地增加,当然特征图大小成倍地减小。最终其在ImageNet上的Top-5准确度为92.3%。

    GoogLeNet/Inception

    尽管VGG可以在ImageNet上表现很好,但是将其部署在一个适度大小的GPU上是困难的,因为需要VGG在内存和时间上的计算要求很高。由于卷积层的通道数过大,VGG并不高效。比如,一个3x3的卷积核,如果其输入和输出的通道数均为512,那么需要的计算量为9x512x512。

    在卷积操作中,输出特征图上某一个位置,其是与所有的输入特征图是相连的,这是一种密集连接结构。 GoogLeNet基于这样的理念:在深度网路中大部分的激活值是不必要的(为0),或者由于相关性是冗余。因此,最高效的深度网路架构应该是激活值之间是稀疏连接的,这意味着512个输出特征图是没有必要与所有的512输入特征图相连。存在一些技术可以对网络进行剪枝来得到稀疏权重或者连接。但是稀疏卷积核的乘法在BLAS和CuBlas中并没有优化,这反而造成稀疏连接结构比密集结构更慢。

    据此,GoogLeNet设计了一种称为inception的模块,这个模块使用密集结构来近似一个稀疏的CNN,如下图所示。前面说过,只有很少一部分神经元是真正有效的,所以一种特定大小的卷积核数量设置得非常小。同时,GoogLeNet使用了不同大小的卷积核来抓取不同大小的感受野。

    Inception模块的另外一个特点是使用了一中瓶颈层(实际上就是1x1卷积)来降低计算量:

    这里假定Inception模块的输入为192个通道,它使用128个3x3卷积核和32个5x5卷积核。5x5卷积的计算量为25x32x192,但是随着网络变深,网络的通道数和卷积核数会增加,此时计算量就暴涨了。为了避免这个问题,在使用较大卷积核之前,先去降低输入的通道数。所以,Inception模块中,输入首先送入只有16个卷积核的1x1层卷积层,然后再送给5x5卷积层。这样整体计算量会减少为16x192+25x32x16。这种设计允许网络可以使用更大的通道数。(译者注:之所以称1x1卷积层为瓶颈层,你可以想象一下一个1x1卷积层拥有最少的通道数,这在Inception模块中就像一个瓶子的最窄处)

    GoogLeNet的另外一个特殊设计是最后的卷积层后使用全局均值池化层替换了全连接层,所谓全局池化就是在整个2D特征图上取均值。这大大减少了模型的总参数量。要知道在AlexNet中,全连接层参数占整个网络总参数的90%。使用一个更深更大的网络使得GoogLeNet移除全连接层之后还不影响准确度。其在ImageNet上的top-5准确度为93.3%,但是速度还比VGG还快。

    ResNet

    从前面可以看到,随着网络深度增加,网络的准确度应该同步增加,当然要注意过拟合问题。但是网络深度增加的一个问题在于这些增加的层是参数更新的信号,因为梯度是从后向前传播的,增加网络深度后,比较靠前的层梯度会很小。这意味着这些层基本上学习停滞了,这就是梯度消失问题。深度网络的第二个问题在于训练,当网络更深时意味着参数空间更大,优化问题变得更难,因此简单地去增加网络深度反而出现更高的训练误差。残差网络 ResNet设计一种残差模块让我们可以训练更深的网络。

    深度网络的训练问题称为退化问题,残差单元可以解决退化问题的背后逻辑在于此:想象一个网络A,其训练误差为x。现在通过在A上面堆积更多的层来构建网络B,这些新增的层什么也不做,仅仅复制前面A的输出。这些新增的层称为C。这意味着网络B应该和A的训练误差一样。那么,如果训练网络B其训练误差应该不会差于A。但是实际上却是更差,唯一的原因是让增加的层C学习恒等映射并不容易。为了解决这个退化问题,残差模块在输入和输出之间建立了一个直接连接,这样新增的层C仅仅需要在原来的输入层基础上学习新的特征,即学习残差,会比较容易。

    与GoogLeNet类似,ResNet也最后使用了全局均值池化层。利用残差模块,可以训练152层的残差网络。其准确度比VGG和GoogLeNet要高,但是计算效率也比VGG高。152层的ResNet其top-5准确度为95.51%。

    ResNet主要使用3x3卷积,这点与VGG类似。在VGG基础上,短路连接插入进入形成残差网络。如下图所示:

    残差网络实验结果表明:34层的普通网络比18层网路训练误差还打,这就是前面所说的退化问题。但是34层的残差网络比18层残差网络训练误差要好。

    总结

    随着越来越复杂的架构的提出,一些网络可能就流行几年就走下神坛,但是其背后的设计哲学却是值得学习的。这篇文章对近几年比较流行的CNN架构的设计原则做了一个总结。译者注:可以看到,网络的深度越来越大,以保证得到更好的准确度。网络结构倾向采用较少的卷积核,如1x1和3x3卷积核,这说明CNN设计要考虑计算效率了。一个明显的趋势是采用模块结构,这在GoogLeNet和ResNet中可以看到,这是一种很好的设计典范,采用模块化结构可以减少我们网络的设计空间,另外一个点是模块里面使用瓶颈层可以降低计算量,这也是一个优势。这篇文章没有提到的是最近的一些移动端的轻量级CNN模型,如MobileNet,SqueezeNet,ShuffleNet等,这些网络大小非常小,而且计算很高效,可以满足移动端需求,是在准确度和速度之间做了平衡。

    欢迎交流与转载,文章会同步发布在公众号:机器学习算法全栈工程师(Jeemy110)

    tensorflow提取VGG特征 - weixin_38208741的博客 - CSDN博客

    $
    0
    0

    vgg-16一种深度卷积神经网络模型,16表示其深度。模型可以达到92.7%的测试准确度。它的数据集包括1400万张图像,1000个类别。 

    一个简单的演示,提取VGG的pool5层特征,存储为.mat文件
    1.     import scipy.io as sio  
    2.    from scipy.misc import imread, imresize  
    3.   
    4.    sess = tf.Session()  
    5.     imgs = tf.placeholder(tf.float32, [None, 224, 224, 3])  
    6.     vgg = vgg16(imgs, '/aa/data/vgg16_weights.npz', sess)  
    7.   
    8.     img1 = imread('/aa/data/laska.png', mode='RGB')  
    9.     img1 = imresize(img1, (224, 224))  
    10.     path = '/aa/data/AllSample/'  
    11.   
    12.     for i in range(1,211):  
    13.         img = imread(path+str(i)+'.jpg',mode='RGB')  
    14.         print(path+str(i)+'.jpg')  
    15.         img = imresize(img, (224, 224))  
    16.       
    17.         feature = sess.run(vgg.pool5, feed_dict={vgg.imgs: [img]})  
    18.         feature = np.reshape(feature,[7,7,512])  
    19.         dic = {'features':feature}  
    20.         sio.savemat('/aa/data/features/'+str(i)+'.mat',dic)  
    21.       
    22. #     features = feature.eval(session=sess)  
    23. #     features = np.reshape(features,[7,7,512])  
    24.      


    在我们的实际项目中,一般不会直接从第一层直接开始训练,而是通过在大的数据集上(如ImageNet)训练好的模型,把前面那些层的参数固定,在运用到我们新的问题上,修改最后一到两层,用自己的数据去微调(finetuning),一般效果也很好。

    所谓finetuning,就是说我们针对某相似任务已经训练好的模型,比如CaffeNet, VGG-16, ResNet等, 再通过自己的数据集进行权重更新, 如果数据量比较小,可以只更新最后一层,其他层的权重不变,如果数据量中等,可以训练后面几层,如果数据量很大,那OK,直接从头训练,只不过在训练时间上,需要花费比较多。

    在网络训练好之后,只需要forward过程就能做预测,当然,我们也可以直接把这个网络当成一个feature extractor来用,可以直接用任何一层的输出作为特征,根据R-CNN论文对Alexnet的实验结果,如果不做fine-tuning,pool5和fc6和fc7的特征效果并没有很强的提升,所以,如果直接用作feature extractor,直接用pool的最后一层输出就OK。


    vgg-16一种深度卷积神经网络模型,16表示其深度。模型可以达到92.7%的测试准确度。它的数据集包括1400万张图像,1000个类别。

    TensorFlow VGG-16 预训练模型

    https://github.com/ry/tensorflow-vgg16

    vgg-16是我最喜欢运行的图像分类的模型,因为它的简单性和准确性。这种模型的创造者发表了可用于Caffe的预先训练二进制。

    https://gist.github.com/ksimonyan/211839e770f7b538e2d8#file-readme-md

    MD5 (VGG_ILSVRC_16_layers.caffemodel) = 441315b0ff6932dbfde97731be7ca852

    这是将特定的文件转换到一个tensorflow模型并检查其正确性。

    运行make下载原始的caffe模型并转换。tf_forword.py里有一个怎样使用产生的vgg16.tfmodel的例子。如果你不想按照caffe,你可以从这下载输出 https://github.com/ry/tensorflow-vgg16/raw/master/vgg16-20160129.tfmodel.torrent

    TF模型的输入(“image”)应该是[批次,高度,宽度,通道],其中高度=宽度=224,通道=3.值应该在0到1之间。

    输出(“prob”)是一个1000维的类概率向量,这个向量的索引对应syset.txt里的行号。

    https://github.com/leihe001/tensorflow-vgg

    这是个基于 tensorflow-vgg16Caffe to TensorFlow的VGG16和VGG19的一个TensorFlow的实现。

    我们修改了tensorflow-vgg16的实现使用numpy加载取代默认的tensorflow加载,目的是加速初始化和减少总的内存使用量。此实现允许进一步修改网络,例如移除FC层,或者增加批大小。

    为了使用VGG网络,需要下载 VGG16 NPYVGG19的npy文件。

    ## 使用 使用这个编译VGG工程

    vgg = vgg19.Vgg19()
    vgg.build(images)

    或者

    vgg = vgg16.Vgg16()
    vgg.build(images)

    图像是一个维度是[None,224,224,3]的张量。

    张量可以是一个占位,一个变量甚至是一个常量。

    所有的VGG层(张量)可以通过vgg对象访问。例如,vgg.conv1_1, vgg.conv1_2, vgg.pool5, vgg.prob, ...test_vgg16.py and test_vgg19.py包含样例的使用。

    ##额外 在我的另一个tensorflow图像风格合成项目中使用了这个库: stylenet

    ##更新 添加一个可训练的VGG19版本vgg19_trainable.支持从现有的变量或从开始训练。(但是不包括训练器)

    test_vgg19_trainabel里添加了一个简单的测试。switch里有如何训练,关闭训练模型进行验证,以及如何保存的例子。

    我添加了一个单独的文件(不是修改现有的),因为我想保持原始VGG网络的简单性。
    Viewing all 11856 articles
    Browse latest View live


    <script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>