<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/rss.xsl" type="text/xsl"?>
<rss version="2.0">
  <channel>
    <title>IT瘾学习推荐</title>
    <link>https://itindex.net/tags/学习</link>
    <description>IT社区推荐资讯 - ITIndex.net</description>
    <language>zh</language>
    <copyright>https://itindex.net/</copyright>
    <generator>https://itindex.net/</generator>
    <docs>http://backend.userland.com/rss</docs>
    <image>
      <url>https://itindex.net/images/logo.gif</url>
      <title>IT社区推荐资讯 - ITIndex.net</title>
      <link>https://itindex.net/tags/学习</link>
    </image>
    <item>
      <title>6 大经典机器学习数据集，3w+ 用户票选得出，建议收藏</title>
      <link>https://itindex.net/detail/62669-%E7%BB%8F%E5%85%B8-%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0-%E6%95%B0%E6%8D%AE</link>
      <description>&lt;blockquote&gt;内容一览：本期汇总了超神经下载排名众多的 6 个数据集，涵盖图像识别、机器翻译、遥感影像等领域。这些数据集质量高、数据量大，经历人气认证值得收藏码住。  &lt;br /&gt;关键词：数据集   机器翻译   机器视觉&lt;/blockquote&gt; &lt;p&gt;数据集是机器学习模型训练的基础，优质的公开数据集对于模型训练效果、研究成果可靠度等具有重要意义。&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;HyperAI超神经自上线以来，为数据科学从业者提供了大量优质的公开数据集。&lt;/strong&gt; 本期内容分享，我们筛选出了 6 个热门数据集，  &lt;strong&gt;其总下载次数已达到 32,569 次。&lt;/strong&gt; 希望这些数据集能进一步为广大开发者服务~&lt;/p&gt; &lt;p&gt;注：本文梳理的数据集均来自为数据科学家服务的网站——超神经  &lt;br /&gt;  &lt;a href="https://hyper.ai/datasets" rel="nofollow noreferrer"&gt;https://hyper.ai/datasets&lt;/a&gt;&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;第 6 名：   &lt;a href="https://hyper.ai/datasets/4920" rel="nofollow noreferrer"&gt;Tanks Temple 3D 重建数据集&lt;/a&gt;&lt;/strong&gt;  &lt;br /&gt;  &lt;img alt="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="https://segmentfault.com/img/remote/1460000043474394" title="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="https://segmentfault.com/img/remote/1460000043474395" title="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;Tanks Temple 图像数据集提供高分辨率的视频，研究人员可以从视频中采集图像，  &lt;strong&gt;依据图像进行三维重建。&lt;/strong&gt; 该数据集包括训练数据和测试数据两类，其中测试数据分为中级组和高级组。&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;第 5 名：   &lt;a href="https://hyper.ai/datasets/4920" rel="nofollow noreferrer"&gt;DOTA 航拍图像数据集&lt;/a&gt;&lt;/strong&gt;   &lt;br /&gt;  &lt;img alt="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="https://segmentfault.com/img/remote/1460000043474396" title="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="https://segmentfault.com/img/remote/1460000043474397" title="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;DOTA 全称为 A Large-scale Dataset for Object DeTection in Aerial Images，是一个包含 2,806 张航拍图的图像数据集，  &lt;strong&gt;被用于在航拍图像中进行目标检测，发现和评估图像中的物体。&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;这些图像来源包含不同传感器和平台。每张图像的像素尺寸在 800  &lt;em&gt;800 到 4000&lt;/em&gt;4000 的范围内，其中包含不同尺度、方向和形状的物体。&lt;/p&gt; &lt;p&gt;往期推送请访问：  &lt;br /&gt;  &lt;a href="http://mp.weixin.qq.com/s?__biz=MzU3NTQ2NDIyOQ==&amp;mid=2247496885&amp;idx=1&amp;sn=add8058f38f5a39608d4915924c5e23b&amp;chksm=fd2002ffca578be91997c3dc38ff72aea8b818bae7be805072217220d9a71179776f71b7ec14&amp;scene=21#wechat_redirect" rel="nofollow noreferrer"&gt;DOTA 数据集：2806 张遥感图像，近 19 万个标注实例&lt;/a&gt;&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;第 4 名：   &lt;a href="https://hyper.ai/datasets/5711" rel="nofollow noreferrer"&gt;VGG-Face2 人脸识别数据集&lt;/a&gt;&lt;/strong&gt;  &lt;br /&gt;  &lt;img alt="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="https://segmentfault.com/img/remote/1460000043474398" title="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="https://segmentfault.com/img/remote/1460000043474399" title="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;VGG-Face2 是一个人脸图像数据集，包含共计 9131  个人的面部数据，图像均来自 Google 的图片搜索。  &lt;strong&gt;数据集中的人在姿势、年龄、种族和职业方面有很大差异。&lt;/strong&gt; 该数据集由牛津大学的工程科学系视觉几何组于 2015 年发布，相关论文有《Deep Face Recognition》。&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;第 3 名：   &lt;a href="https://hyper.ai/datasets/5419" rel="nofollow noreferrer"&gt;UCAS-AOD 遥感影像数据集&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="https://segmentfault.com/img/remote/1460000043474400" title="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="https://segmentfault.com/img/remote/1460000043474401" title="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;UCAS-AOD 是一个遥感影像数据集，  &lt;strong&gt;用于飞机和车辆检测。&lt;/strong&gt; 该数据集由国科大于 2014 年首次发布，并于 2015 年补充，相关论文有《Orientation Robust Object Detection in Aerial Images Using Deep Convolutional Neural Network》&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;第 2 名：   &lt;a href="https://hyper.ai/datasets/14137" rel="nofollow noreferrer"&gt;OpenMantra 漫画机器翻译数据集&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="https://segmentfault.com/img/remote/1460000043474402" title="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="https://segmentfault.com/img/remote/1460000043474403" title="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;OpenMantra 是一个针对日文漫画的机器翻译评估数据集，包含五种不同风格（fantacy、romance、battle、mystery、slice of life）的漫画。  &lt;strong&gt;数据集中共包含 1593 个句子，848 个场景画面和 214 页漫画，&lt;/strong&gt; 由东京大学 Mantra 团队发布。&lt;/p&gt; &lt;p&gt;往期推送请查看：  &lt;br /&gt;  &lt;a href="http://mp.weixin.qq.com/s?__biz=MzU3NTQ2NDIyOQ==&amp;mid=2247497461&amp;idx=1&amp;sn=3b7eede40073e65e0bd8bd73d15ede19&amp;chksm=fd2000bfca5789a92f1808b1e98464d31af07344bc98cae9f1fbad95de5e063dcd4659f77491&amp;scene=21#wechat_redirect" rel="nofollow noreferrer"&gt;漫画翻译、嵌字 AI，东京大学论文被 AAAI’21 收录&lt;/a&gt;&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;第 1 名：   &lt;a href="https://hyper.ai/datasets/4899" rel="nofollow noreferrer"&gt;ImageNet 10 图像识别数据集&lt;/a&gt;&lt;/strong&gt;  &lt;br /&gt;  &lt;img alt="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="https://segmentfault.com/img/remote/1460000043474404" title="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="https://segmentfault.com/img/remote/1460000043474405" title="&amp;#22312;&amp;#36825;&amp;#37324;&amp;#25554;&amp;#20837;&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;ImageNet 是目前世界上最大的图像识别数据库，由斯坦福大学教授李飞飞等人创建。&lt;/strong&gt; 主要用于机器视觉领域的图像分类和目标检测。&lt;/p&gt; &lt;p&gt;数据集根据 WordNet 层次结构组织，其中每个节点（也称为类别）由数百甚至数千张图像组成。该数据集共包含 2.2 万个图像类别，约 1500 万张图片。&lt;/p&gt; &lt;p&gt;往期推送请访问：  &lt;br /&gt;  &lt;a href="http://mp.weixin.qq.com/s?__biz=MzU3NTQ2NDIyOQ==&amp;mid=2247486955&amp;idx=1&amp;sn=622108b403f441fe71e8e4ddbe72529b&amp;chksm=fd23fba1ca5472b7a6f6129a8f064d2839fe68fbaef100734466a19f2f34fd22ca7e2d9d0f8e&amp;scene=21#wechat_redirect" rel="nofollow noreferrer"&gt;当年这个决定，让李飞飞奠定 AI 江湖的女王地位&lt;/a&gt;&lt;/p&gt; &lt;p&gt;以上就是本期推荐的 6 个 hyper.ai 高频下载数据集，更多数据科学优质公开数据集，可访问以下链接下载：  &lt;a href="https://hyper.ai/datasets" rel="nofollow noreferrer"&gt;https://hyper.ai/datasets&lt;/a&gt;&lt;/p&gt; &lt;p&gt;—— 完 ——&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>算法 机器学习 人工智能 深度学习 数据挖掘</category>
      <guid isPermaLink="true">https://itindex.net/detail/62669-%E7%BB%8F%E5%85%B8-%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0-%E6%95%B0%E6%8D%AE</guid>
      <pubDate>Mon, 20 Feb 2023 13:56:27 CST</pubDate>
    </item>
    <item>
      <title>文字语义纠错技术探索与实践-张健</title>
      <link>https://itindex.net/detail/62552-%E6%96%87%E5%AD%97-%E8%AF%AD%E4%B9%89-%E6%8A%80%E6%9C%AF</link>
      <description>&lt;h1&gt;  &lt;strong&gt;  背景    &lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;文本语义纠错的使用场景非常广泛，基本上只要涉及到写作就有文本纠错的需求。书籍面市前就有独立的校对的环节来保障出版之后不出现明显的问题。在新闻中我们也时不时看到因为文字审核没到位造成大乌龙的情况，包括上市公司在公开文书上把“临时大会”写成为“临死大会”，政府文件把“报效国家”写成了“报销国家”。有关文本纠错的辅助工具能给文字工作人员带来较大的便利，对审核方面的风险也大幅降低。&lt;/p&gt;



 &lt;p&gt;除了不同的写作场景，文本纠错还会用在其他一些智能处理系统中，具体的情况包括：音频通话记录经过自动语音识别（ASR）转写成文本之后，存在一些转译错误；光学字符识别（OCR）系统识别图片中的文字并进行提取，会存在字符识别错误；在搜索引擎或自动问答系统里面，用户在查询过程中的输入错误，往往会导致系统无法理解用户的真实意图，需要进行查询纠正改写。这些情况都需要通过文本纠错技术来进行修正，使产品整体的用户体验更加友好。&lt;/p&gt;



 &lt;p&gt;文本语义纠错在学术领域有三个子任务，分别是拼写检查（Spelling Check）、语法检错（Grammatical Error Detection）和语法纠错（Grammatical Error Correction）。其中语法检错是对文本中的语法错误进行检测，拼写检查是对文本中的错别字进行修正，语法纠错是纠正文本中的语法错误。拼写检查在英文场景表现为单词拼写错误，在中文场景表现为音近形近错别字。而语法纠错除此之外，还包括字词缺失、字词冗余、字词使用不当、语序不当等错误类型。语法纠错区别于拼写检查的一个显著特点是，语法纠错纠正后的文本和原始文本的长度不一定相等，而拼写检查纠正前后的文本长度都是保持一致的，这也决定了两者的算法支持存在差异。一般来说，拼写检查可以看作为语法纠错的一个任务子集。&lt;/p&gt;



 &lt;p&gt;我们对语法纠错的问题作一下形式化定义，输入的原始文本定义为X={x1,x2,...,xn};原始文本正确的纠正结果文本序列定义为Y={y1,y2,...,ym}，算法预测输出的文本，定义为P={p1,p2,...,pk}。&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;评估指标 &lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;在开始我们的文本语义纠错算法探索之旅之前，我们先思考一个问题，究竟怎么样的模型表现才是公认更有效的，这个好坏应该从何种方式、如何量化地评估出来。这也是我们在解决其他所有类型的NLP任务都需要先考虑的问题，这个问题就是如何定义我们的评测指标。下面罗列了纠错算法常用的一些评测指标：&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;01&lt;/strong&gt;  &lt;strong&gt;M2（MaxMatch）&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;M2指标主要是通过计算输出文本和原始文本之间的编辑集合G，然后与人工标注的编辑集合E结合，计算准确率、召回率、F0.5值（采用F0.5表示对准确率更加关注）。这里的编辑理解为一个转换动作，经过一组转换动作，可以完成原始文本到纠正文本的转换，M2指标定义形如：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/2f96b650-a98a-4bf3-bf58-fdc5258275b8.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;F0.5=1.25*RP/(R+0.25P)&lt;/p&gt;



 &lt;p&gt;下表罗列了一组示例和计算过程：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/81f46c04-6037-4075-b7aa-0d62628ab074.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;表 1 纠错文本示例&lt;/p&gt;



 &lt;p&gt;其中编辑集合G={孜→自，书→书写}，人工标注编辑集合E={孜→自,俱→具,读书→读}&lt;/p&gt;



 &lt;p&gt;可以计算出来:&lt;/p&gt;



 &lt;p&gt;P=1/2=0.5&lt;/p&gt;



 &lt;p&gt;R=1/3=0.33&lt;/p&gt;



 &lt;p&gt;F0.5=1.25*0.33*0.5/(0.33+0.25*0.5)=0.45&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;02 &lt;/strong&gt;  &lt;strong&gt;ERRANT&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;ERRANT[1]是升级版的M2。M2的局限性也比较明显，依靠前置的人工标注，有比较大的工作量，而且人工标注编辑集合产生的方式可能不太一致，导致匹配不准。ERRANT在生成标准答案的编辑集合和生成预测的编辑集合都采用了自动判别的方式，同时支持了25种的错误类型，输出了更丰富维度的错误报告信息。缺点是该工具面向英文，中文需要做较大改造。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;03&lt;/strong&gt;  &lt;strong&gt;面向标注形态的其他指标&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;上述两者在处理纠错任务评测时存在一些缺点，包括M2不支持检错性能评估，编辑不能正确反映合理纠错动作等等。  &lt;br /&gt;  &lt;br /&gt;我们会在一些学术评测上看到，根据对待纠文本进行的错误标注类型来制定的评测指标。下面举了NLPCC2022语法纠错评测指标为例，它对应的错误类型总共有赘余(Redundant Words，R)、遗漏(Missing Words，M)、误用(Word Selection，S)、错序(Word Ordering Errors，W)四类，评估的维度包含以下方面：&lt;/p&gt;



 &lt;ul&gt;
  &lt;li&gt;假阳性（False Positive）：正确句子被判包含错误的比例。&lt;/li&gt;



  &lt;li&gt;侦测层（Detective-level）：对句子是否包含错误做二分判断。从句子是否有错，判断p/r/f1&lt;/li&gt;



  &lt;li&gt;识别层（Identification-level）：给出错误点的错误类型。按一个句子的错误种类计算p/r/f1&lt;/li&gt;



  &lt;li&gt;定位层（Position-level）：对错误点的位置和覆盖范围进行判断，以字符偏移量计。错误位置是否对计算p/r/f1&lt;/li&gt;



  &lt;li&gt;修正层（Correction-level）：提交针对字符串误用（S）和缺失（M）两种错误类型的修正词语。修正词语可以是一个词，也可以是一个词组。M/S的修正词语角度&lt;/li&gt;
&lt;/ul&gt;



 &lt;p&gt;由于纠错任务本身的特殊性（同一个错误的文本可以有多种正确的纠正答案，或者同一个位置可以采用不同的错误类型进行标注），目前现存的评测指标大都有其局限性，如何定义主客观、统一、合理的语法纠错评测指标仍然在不断探讨。&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt; 公开数据集&lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;在确定了评估指标之后，我们已经确定了评判算法好坏的一个标准。锅已经端好，就等米下锅了,数据对于算法研发人员来说是必需品，一方面它是验证效果的信息来源,另一方面它是进行模型构建的训练语料。比较好的方式是从公开的渠道获取比较优质的标注数据。目前公开的中文语义纠错数据集包括NLPCC2018[2]、NLPTEA2020[3]、SIGHAN2015[4]等,较多是非母语学生学习汉语收集得来的语料集，训练和验证的数据标注形式如图所示:&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/ccee8a06-344d-443d-baaf-12959d28383b.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;图1 公开数据集（NLPTEA2020、NLPCC2020和SIGHAN2015）&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;无监督方法&lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;文本语义纠错的算法整体可以分成无监督和有监督的两种方式，我们先从无监督的方法开始看。无监督方法的核心是如何构建一个好用的语言模型，并且用在纠错的任务上。对于NLPer来说，我们经历了太多的预训练语言模型，像BERT、XLNet、GPT3等等，其本质还是语言模型或者说经典语言模型的一些变种。语言模型实际上是对文本序列的概率分布进行建模，通俗地来表达，语言模型是判断一句话是不是符合常理，或者说话应该怎么说才合理（符合概率分布）。这个正好就对应上了纠错任务的本质需求，我们从最经典的N元语言模型开始来介绍一下语法纠错的处理逻辑。   &lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;01&lt;/strong&gt;  &lt;strong&gt;n元语言模型&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;一个语言模型构建字符串的概率分布p(W)，假设p(W)是字符串作为句子的概率，则概率由下边的公式计算：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/68f2b5ca-b777-4865-8668-29f0032daf32.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;但是这样去计算句子概率会导致庞大的计算量，导致根据马尔科夫假设，一个词只和他前面n-1个词相关性最高，这就是n元语法模型，简化后的计算公式为：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/1ea20eca-647d-435c-a787-719f694e93a4.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;在得到这个结论之后，我们尝试使用N元语言模型来解决拼写检查的问题。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;假设我们采用的是5元语言模型，训练阶段使用大量的语料来进行统计所有的p(w5|w1w2w3w4)并存储起来。在预测阶段，设定待纠正的文本序列为W={w1,w2,...,wn}，针对每个位置的wk，我们通过预先构建好的混淆集获得w的音近形近字wk&amp;apos;。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;然后通过上述公式分别计算原始文本和修改文本的句子概率P(w1...wk...wn)、P(w1...wk&amp;apos;...wn)。如果P(w1...wk’...wn)&amp;gt;P(w1...wk...wn)，则说明修改后文本的通顺度提升（概率升高），可以接受该纠正修改（wk替换为wk&amp;apos;）。&lt;/p&gt;



 &lt;p&gt;从而我们的纠错执行过程则包含如下：&lt;/p&gt;



 &lt;ul&gt;
  &lt;li&gt;计算输入句子的归一化对数概率，并且为句子的每个字构建一个混淆集合；&lt;/li&gt;



  &lt;li&gt;对句子每个字针对其不同混淆字重新打分，应用单个最佳进行校正，将概率提高到当前最高值以上；&lt;/li&gt;



  &lt;li&gt;重复上面过程直至概率没变化。&lt;/li&gt;
&lt;/ul&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/2a13b0c9-02e2-4b46-bbca-739442d93356.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;图2 N元语言模型纠错执行计算过程&lt;/p&gt;



 &lt;p&gt;上述过程比较好理解，同时可以明显看出来一些硬伤，包括会OOV（未登录词）问题导致语言模型计算出来的概率为0；模型会过分优待高频短串，或者忽视低频短串。这时候需要通过平滑技术来改善概率算法，典型平滑方法包含Add-one、Interpolation和Modified Kneser-ney等。此外，仍有些难以通过技术手段解决的问题，包括上下文范围局限较大（n 的增加会导致计算和资源消耗成倍增加）和缺少泛化（缺乏实际予以的理解），此时需要引入基于神经网络的语言模型。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;02&lt;/strong&gt;  &lt;strong&gt;基于神经网络的语言模型&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;比较经典的基于神经网络的语言模型，数学表达式可以写为：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/832df924-e20f-4732-b0e7-854f6b37fbe5.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;以k元文法为例，把前k-1个词作为特征，用softmax预测最后一个词。&lt;/p&gt;



 &lt;p&gt;一般基于神经网络的语言模型设计得更加复杂，会把上下文的信息形成特征，来预测当中的每一个词。定义基于上下文context下wi的预测概率为P(wi|context_i),句子的概率可以表示为：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/87ec0acb-8593-4e18-88d4-28ebb4b57258.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;方法[5]就是采用了BERT和GPT作为基础的语言模型来计算句子的概率。&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;有监督方法&lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;无监督的纠错算法在处理文本时存在以下弱点：容易受局部高频或低频的序列影响，效果不够稳定；在需要对准确率和召回率进行平衡调整时，不太好通过阈值的方式进行控制；可以较好应用在拼写检查的任务上，但是对于句子长度有变化的语法纠错任务支持就比较弱。此时需要使用有监督算法来作为实现手段。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;01&lt;/strong&gt;  &lt;strong&gt;NMT/Seq2Seq&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;解决字词冗余/缺失这类纠错前后句子长度有变化的任务，我们第一感觉就想起可以通过文本生成的方式来训练对应的模型实现该功能。而且语法纠错任务和文本生成任务的形态基本上是一致的，也导致了文本生成模型很自然地被研究者注意，引入到语法纠错的任务领域。&lt;/p&gt;



 &lt;p&gt;NMT-based GEC[6]是第一篇通过使用神经网络机器翻译来实现语法纠错的文章。2014年seq2seq模型一提出即引发了较大反响，后续seq2seq成为了文本生成的主流结构。seq2seq将一个作为输入的序列映射为一个作为输出的序列，这一过程由编码（Encoder）输入与解码（Decoder）输出两个环节组成, 前者负责把序列编码成一个固定长度的向量，这个向量作为输入传给后者，输出可变长度的向量。下图展现了一个基础的seq2seq结构。&lt;/p&gt;


 &lt;div&gt;
  &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/326e96bc-a4f8-4c11-84c6-6e52e6de0f6a.jpg"&gt;&lt;/img&gt;&lt;/div&gt;


 &lt;p&gt;图3  seq2seq结构&lt;/p&gt;



 &lt;p&gt;方法[7]使用了经典的Encoder-Decoder模型结构来解决中文语法纠错问题，嵌入层使用了特殊的嵌入表示，同时在编码层使用了卷积神经网络强化了纠错的局部性，具体的模型结构如下：&lt;/p&gt;


 &lt;div&gt;
  &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/93b9891a-35da-4951-8c17-72af28cd587a.jpg"&gt;&lt;/img&gt;&lt;/div&gt;


 &lt;p&gt;图4 Encoder-Decoder结构纠错模型&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;02 &lt;/strong&gt;  &lt;strong&gt;LaserTagger&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;由于书写错误的出现概率普遍不高，纠错任务本身的输入输出存在大量重叠（基本不用改），所以大多数文本可以保持不变。但是我们在通过seq2seq的方式进行实现时，对于正常的字符也要全部进行预测，造成效率非常低下。因此谷歌在EMNLP 2019提出了LaserTagger，在使用Encoder-Decoder的模型结构条件下，把预测的内容从文字变成了编辑操作类型。lasertagger其模型结构（采用BERT作为编码层、自回归Transformer作为解码层）如下所示：  &lt;br /&gt;  &lt;br /&gt;  &lt;img src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/86e82b87-6d4c-4671-b7a3-c5e9c3a67112.jpg"&gt;&lt;/img&gt;&lt;/p&gt;



 &lt;p&gt;图5 LaserTagger纠错模型&lt;/p&gt;



 &lt;p&gt;编辑操作类型包含Keep（将单词复制到输出中），Delete（删除单词）和Add（在之前添加短语X），其中被添加的短语来自一个受限的词汇表。  &lt;br /&gt;通过结构的改造，lasertagger体现了推理速度快和样本训练效率高的有点。因为预测的类型只有三种，相对于seq2seq而言，解码的空间大幅降低，推理性能提升明显，相对于BERT+seq2seq的模型结构，larserTagger的性能提升接近100倍。同时因为预测的内容求解空间也大幅降低，所以对样本的需求量也大幅减少，在1000份的样本下也能取得不错的效果。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;03 &lt;/strong&gt;  &lt;strong&gt;PIE&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;与LaserTagger同年提出来的PIE（Parallel Iterative Edit Models）[8]同样是针对seq2seq 生成文本的可控性较差，推理速度也比较慢的问题进行来改进。与LarserTagger类似，PIE构造模型来对编辑操作进行预测，不过编辑操作的类型稍有区别，多了一个替换（replace)和词性变换（面向英文）。在处理替换和添加操作时，PIE将BERT编码层进行了扩展来支持替换和添加的信息输入，采用了一个双层的双向transformer，结构如下所示：  &lt;br /&gt;  &lt;br /&gt;  &lt;img src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/ce4efddc-017c-431b-8cca-212469d098b9.jpg"&gt;&lt;/img&gt;&lt;/p&gt;



 &lt;p&gt;图6 PIE纠错模型&lt;/p&gt;



 &lt;p&gt;上图表示了一个长度为3的文本输入（x1,x2,x3)。在最底层的输入层，M表示mask标识符的嵌入向量，p表示位置嵌入，x表示词嵌入。在中间层和输出层，r表示对应位置的替换信息，h表示对应位置的的原始信息，a表示对应位置的插入信息。之后利用三类信息来分别计算不同操作的概率，并归一化，CARDT 分别代表复制、插入、替换、删除、词形变换，计算公式如下：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/06b728d3-f079-45ff-97bf-9c5defebc4b9.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;纠错过程中，PIE模型输出概率最高的编辑操作，完成修改后再迭代地进行预测，直至句子不发生改变后停止。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;PIE定义的结构可以实现在并行解码的同时保持较高的准确率，它在这篇文章第一次提出了seq2edit的概念。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;04 &lt;/strong&gt;  &lt;strong&gt;GECToR&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;GECToR[9]提出了一种序列标注模型，编码层由预训练的 BERT 型 transformer 组成，上面堆叠两个线性层，顶部有 softmax 层。模型输出的标签包含了基本变换和g-变换两种类型。其中基本变换包含保留（KEEP）、删除（DELETE）、添加（APPEND）和替换（REPLACE)。g-变换主要面向英文，针对了英语的语法变化总结出了5大类（大小写、单词合并、单词拆分、单复数和时态）29个小类的状态变换。  &lt;br /&gt;  &lt;br /&gt;GECToR另外两个亮点是引入了不同的预训练Transformer解码器（包括XLNet、RoBERTa、ALBERT、BERT和GPT-2）并进行了比较，以及采用了三阶段的训练方式。第一阶段使用了大量（九百万）实验合成的包含语法错误+语法正确的句子对进行预训练，第二阶段使用了少量的公开纠错数据集的句子对进行Fine-tuning，第三阶段使用了语法错误+正确和语法正确+正确的句子对来进行Fine-tuning，实验证明第三阶段的Fine-tuning有效果提升。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;在预测阶段，GECToR也是采用了多轮预测的方案。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;05 &lt;/strong&gt;  &lt;strong&gt;PLOME&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;PLOME[10]在2021ACL发表，是针对中文文本纠错任务构建的预训练语言模型，结构和BERT比较类似（12层Transformer）。PLOME的创新点主要在于采用了基于混淆集的掩码策略、把拼音和字形信息作为模型输入以及把字符和拼音的预测任务作为了模型的训练和微调目标。  &lt;br /&gt;  &lt;br /&gt;PLOME的掩码策略主要是基于以下4种：字音混淆词替换(Phonic Masking)、字形混淆词替换(Shape Masking)、随机替换（Random Masking）、原词不变（Unchanging）。PLOME的掩码策略同样仅遮盖15%的token，且4种MASK策略占比分别为: 60% 、15%、10%、15%。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;词嵌入模块方面，PLOME采用了字符嵌入(character embedding)、位置嵌入( position embedding)、语音嵌入(phonic embedding)和形状嵌入(shape embedding)。其中，字符嵌入和位置嵌入与BERT的输入一致。其中构建语音嵌入时，使用Unihan数据库得到字符-拼音的映射表(不考虑音调)， 然后将每一个字的多个拼音字母序列输入到GRU网络中，得到该字的拼音嵌入向量。同样，构建字形嵌入时，使用Chaizi数据库得到字形的笔画顺序，然后将字形的笔画顺序序列输入到GRU网络中，得到该字的字形嵌入向量。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;在训练任务方面，PLOME训练了2个任务，字符预测和BERT一样，增加了拼音的预测，预测被替换词的正确发音，更够更好解决同音和音近错误。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;PLOME预训练语言模型的下游任务主要是文本纠错任务。该任务的输入是字符序列 ,输出是预测的字符序列。该论文仅在拼写检查任务上做了验证。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;06&lt;/strong&gt;  &lt;strong&gt; 其他策略&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;（1）COPY机制&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;COPY机制同样是利用目标文本和源文本有大量重复这个特点。研究[11]提出了Copy-Augment的GEC模型，其主要思想是：在生成序列过程中，考虑两个生成分布：分别是复制输入序列中的词语（0/1表示是否复制)的概率分布和从候选词典中的词语生成的概率分布。将两者的概率分布加权求和作为最终生成的概率分布，进而预测每一个时刻生成的词语。基本架构如下：  &lt;br /&gt;  &lt;img src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/9169ccac-2eb0-47b6-b540-3754a8ce576d.jpg"&gt;&lt;/img&gt;&lt;/p&gt;



 &lt;p&gt;图7 COPY机制&lt;/p&gt;



 &lt;p&gt;该模型将简单的词语复制任务交给了Copy机制，将模型结构中的Attention等结构更多地用来学习比较难的新词生成，对训练更加可控。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;（2）数据增强&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;数据增强可以通过基于规则和基于生成的方式实现。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;基于规则这个比较简单，我们可以按照错误的类型（字词冗余、缺失、词序错误等）针对性地制定策略构造包含语法错误的样本，然后扔到模型来进行训练。不过基于规则的方式有点过于粗暴，很可能规则生成的错误与实际产生的错误差距比较大，或者比较不符合常规认知。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;第二种方式就是基于生成。最早基于生成的数据增强方式应该是回译法，就是将文本从一个语种翻译到另外一个语种，然后再翻译回来，从而构造了句子对，这种数据增强形式针对正常的编辑写作可能不太有效，更加符合跨语言学习的用户的错误特点。另外有研究[12]结合了分类器和自编码器来联合训练，达到生成固定类型错误样本的目的。而研究[13]通过对GEC模型进行对抗攻击，可以生成有价值的带有语法错误的句子，可以利用生成的句子训练GEC模型，提升性能的同时提升鲁棒性。&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;处理难点与技术挑战 &lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;  &lt;strong&gt;01&lt;/strong&gt;  &lt;strong&gt;语料收集&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;目前公开的中文语义纠错数据集主要是不同母语的人学习汉语作为第二语言收集得来的语料集，目前大部分关于语法纠错的算法模型都是基于这些数据集来做效果验证的，不过我们实际中要处理的数据通常并不是同样的形式诞生，更多是掌握汉语作为母语的人由于失误导致的语法错误，这种情况和公开预料的情况差别比较大，错误的分布差距也比较大，从而通过公开语料集训练得来的模型在上线到正常的业务流程里面，效果通常都会比较一般。    &lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;02&lt;/strong&gt;  &lt;strong&gt;长依赖&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;长距离包括跨语句依赖在论文等文本中很常见，一旦出现错误，很难察觉并纠正。当前语法研究大多集中在单个语句的语法检查和纠错，很少涉及长距离语法问题，相关数据集和模型方法缺失，是语法纠错的难题之一。  &lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;03&lt;/strong&gt;  &lt;strong&gt;模型的泛化能力与鲁棒性&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;一般来说，不同行业、不同领域的文本在措辞运用、表达习惯和专有名词等方面都存在较大的差异。譬如说政务机关红头文件非常严谨的语言表达和自媒体新闻相对较自由的文风就有明显的差别，又譬如金融行研报告和医学论文在基本内容和专业术语上也截然不同。在一个领域性能出色的纠错模型在切换到另外一个领域，往往效果下降明显。如何提升模型的泛化能力和鲁棒性，面临着巨大的技术挑战。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;04&lt;/strong&gt;  &lt;strong&gt;效果指标与体验的平衡&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;SOTA的指标可以刷到，但是这些模型一旦介入实际场景的数据，效果会差得一塌糊涂，这个一方面是由于模型和场景紧密相关，另外一方面是，通常公开数据集的错误分布是呈高密度，但是实际场景是低密度，会容易导致非常高的误判。譬如说SOTA里面准确率的指标是80%，对于在低密度错误的样本中，很可能准确率会下降到20~30%左右。纠错系统的体验会比较差。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;05&lt;/strong&gt;  &lt;strong&gt;效果指标与纠错性能的平衡&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;工业界往往会采用pipeline的方式，先对文本进行检错，如果检测出来有错误，再对文本进行纠错处理。但是这个检错阶段的错误会传递到纠错阶段，导致效果下降。如果直接走seq2seq或seq2edit的纠错模型，或者需要融合多种模型策略来生成最终纠错结果，纠错的性能会下降非常快，部分实验3000字的纠错可能需要长达40~60秒，这个无法处理大量并发的文本纠错需求。我们需要再效果和性能上取得平衡，或者有更好的方法在保障效果指标的前提下提升纠错性能。&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;达观在语义纠错方面的产品实践 &lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;达观数据在语义纠错方面有比较深入的产品实践，开发出的投行质控系统和公文智能处理系统均处理了相关场景。&lt;/p&gt;



 &lt;p&gt;达观投行质控系统基于深度学习、NLP（自然语言处理）算法，帮助用户解决几大文书审核场景，包括:文书格式纠错，文字纠错和完整性审核；文档目录智能识别，一键定位：文档条款内容智能提取，方便业务人员对条款内容进行预审；支持文档多版本的内容比对等。&lt;/p&gt;



 &lt;p&gt;达观智能公文处理系统，严格遵循《党政机关公文处理工作条例》和《党政机关公文格式》规定，通过公文智能分析、公文知识库引用、公文审校、公文排版、公文格式纠错、公文内容语义纠错、公文在线比对修改等一体化的功能，实现基础字词校对准确率超90%、法律引用校验和公务文书完整性校对准确率超95%，有力提升政府机关整体公文质量，避免公文“带病”发布情况，确保政府机关公信度。&lt;/p&gt;



 &lt;p&gt;参考文献：&lt;/p&gt;



 &lt;p&gt;[1] Korre K, Pavlopoulos J. Errant: Assessing and improving grammatical error type classification[C]//Proceedings of the The 4th Joint SIGHUM Workshop on Computational Linguistics for Cultural Heritage, Social Sciences, Humanities and Literature. 2020: 85-89.&lt;/p&gt;



 &lt;p&gt;[2] Zhao Y, Jiang N, Sun W, et al. Overview of the nlpcc 2018 shared task: Grammatical error correction[C]//CCF International Conference on Natural Language Processing and Chinese Computing. Springer, Cham, 2018: 439-445.&lt;/p&gt;



 &lt;p&gt;[3] Rao G, Yang E, Zhang B. Overview of NLPTEA-2020 shared task for Chinese grammatical error diagnosis[C]//Proceedings of the 6th Workshop on Natural Language Processing Techniques for Educational Applications. 2020: 25-35.&lt;/p&gt;



 &lt;p&gt;[4] Tseng Y H, Lee L H, Chang L P, et al. Introduction to SIGHAN 2015 bake-off for Chinese spelling check[C]//Proceedings of the Eighth SIGHAN Workshop on Chinese Language Processing. 2015: 32-37.&lt;/p&gt;



 &lt;p&gt;[5] Alikaniotis D, Raheja V. The unreasonable effectiveness of transformer language models in grammatical error correction[J]. arXiv preprint arXiv:1906.01733, 2019.&lt;/p&gt;



 &lt;p&gt;[6] Yuan Z, Briscoe T. Grammatical error correction using neural machine translation[C]//Proceedings of the 2016 Conference of the North American Chapter of the Association for Computational Linguistics: Human Language Technologies. 2016: 380-386.&lt;/p&gt;



 &lt;p&gt;[7] Ren H, Yang L, Xun E. A sequence to sequence learning for Chinese grammatical error correction[C]//CCF International Conference on Natural Language Processing and Chinese Computing. Springer, Cham, 2018: 401-410.&lt;/p&gt;



 &lt;p&gt;[8] Awasthi A, Sarawagi S, Goyal R, et al. Parallel iterative edit models for local sequence transduction[J]. arXiv preprint arXiv:1910.02893, 2019.&lt;/p&gt;



 &lt;p&gt;[9] Omelianchuk K, Atrasevych V, Chernodub A, et al. GECToR--grammatical error correction: tag, not rewrite[J]. arXiv preprint arXiv:2005.12592, 2020.&lt;/p&gt;



 &lt;p&gt;[10] Liu S, Yang T, Yue T, et al. PLOME: Pre-training with misspelled knowledge for Chinese spelling correction[C]//Proceedings of the 59th Annual Meeting of the Association for Computational Linguistics and the 11th International Joint Conference on Natural Language Processing (Volume 1: Long Papers). 2021: 2991-3000.&lt;/p&gt;



 &lt;p&gt;[11] Zhao W, Wang L, Shen K, et al. Improving grammatical error correction via pre-training a copy-augmented architecture with unlabeled data[J]. arXiv preprint arXiv:1903.00138, 2019.&lt;/p&gt;



 &lt;p&gt;[12] Wan Z, Wan X, Wang W. Improving grammatical error correction with data augmentation by editing latent representation[C]//Proceedings of the 28th International Conference on Computational Linguistics. 2020: 2202-2212.&lt;/p&gt;



 &lt;p&gt;[13] Wang L, Zheng X. Improving grammatical error correction models with purpose-built adversarial examples[C]//Proceedings of the 2020 Conference on Empirical Methods in Natural Language Processing (EMNLP). 2020: 2858-2869.&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;作者简介&lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;张健，达观数据联合创始人，复旦大学计算机软件与理论硕士，曾就职于盛大集团和腾讯文学，担任人工智能和大数据技术专家职位。目前担任达观数据文本应用部总负责人，对于机器学习算法和自然语言处理领域的研发有丰富的实践经验和技术积累，负责客户意见洞察系统、智能客服工单分析系统、文本语义纠错系统、事件分析平台、文本智能审核系统等多个文本应用产品的开发和落地。荣获上海市浦东新区科学技奖、“2021上海科技青年35人引领计划”、上海市青年科技启明星等多个奖项。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>MIT自然语言处理 NLP开源工具 中文信息处理 人工智能 机器学习</category>
      <guid isPermaLink="true">https://itindex.net/detail/62552-%E6%96%87%E5%AD%97-%E8%AF%AD%E4%B9%89-%E6%8A%80%E6%9C%AF</guid>
      <pubDate>Wed, 21 Dec 2022 15:10:41 CST</pubDate>
    </item>
    <item>
      <title>知识图谱增强下的智能推荐系统与应用-于敬</title>
      <link>https://itindex.net/detail/62491-%E7%9F%A5%E8%AF%86-%E5%A2%9E%E5%BC%BA-%E6%99%BA%E8%83%BD</link>
      <description>&lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221115/91c10e8a-a59a-4c46-bcd6-5b8171f57b76.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;随着互联网技术的迅速发展，尤其是移动互联网的兴起，新产生的信息呈现爆炸式的增长。为了更好地解决信息获取中的信息过载（Information Overload）和长尾问题（Long Tail），推荐系统应运而生，目前基本上已经成为了各种产品的标配功能。推荐系统是信息过滤系统的一个分支，它可以自动地挖掘用户和物品之间的联系。具体来说，它试图基于用户本身的多维度属性数据（如年龄、地域、性别等）以及行为数据的反馈（如点击、收藏、点赞、购买等），结合物品自身属性数据（如标题、标签、类别、正文等），以预测用户对待推荐物品的评分或偏好。从用户的角度来看，推荐系统是基于用户个人的兴趣偏好进行千人千面的自动推荐，则有助于缓解信息过载问题。从物品的角度来看，其自身属性及对应的交互行为差异，通过各种推荐方式是可以触达到对其更感兴趣的用户群体中，缓解了曝光不足带来的长尾问题。从企业的角度来看，推荐系统带来了更好的产品交互方式，达到了沉浸式体验的效果，从而进一步提升了用户的黏性，并最终大幅度提升了转化收益。&lt;/p&gt;



 &lt;p&gt;  &lt;img src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221115/6c4285ff-bfa0-47ff-ac95-02afaf310d0c.jpg"&gt;&lt;/img&gt;图1 达观智能推荐系统&lt;/p&gt;



 &lt;p&gt;在智能推荐ToB企业服务领域，达观数据已经有了10余年的推荐技术沉淀和上千家客户的行业应用实践经验。早在2012年的时候，由达观数据创始人陈运文博士带领团队参加了在伦敦举办的EMI数据黑客竞赛并获得了国际冠军，该竞赛主要是围绕音乐推荐场景，如何基于用户听歌行为等数据进行分析挖掘来对预测用户兴趣偏好并进行歌曲推荐。经过激烈鏖战，由他们开发的智能推荐系统对500万听歌用户的数据进行建模，根据每个用户的个性化兴趣偏好从数十万首歌曲库中为每个用户生成千人千面的歌曲推荐结果，推荐精度力克包括来自剑桥大学、牛津大学、密歇根大学等等的300多支参赛队伍，一举获得冠军。达观智能推荐基于前沿的人工智能和大数据分析挖掘技术，经过多年的产品打磨和持续的行业应用探索，累计服务客户数量达到了上千家。  &lt;em&gt;（https://www.datagrand.com/products/recommend/）&lt;/em&gt;&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;基于过滤思想的推荐方法&lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;经过多年的推荐系统理论发展，已经产生了三代主要的推荐系统。第一代推荐系统（1995-2005），主要包括三种方法：基于内容过滤的方法、基于协同过滤的方法和混合方法，技术上主要是规则统计和机器学习。第二代推荐系统（2003-2014），主要是基于时间、位置、用户组评分等特征上下文，对这一代推荐系统的研究目前仍在进行中。第三代推荐系统的研究更侧重在基于表示学习的语义模型以及在推荐过程中会有较多的关于知识组件的使用。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;01&lt;/strong&gt;  &lt;strong&gt;基于协同过滤的推荐方法&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;协同过滤方法（Collaborative Filtering，CF）是一种传统的推荐方法，体现的是群体智慧，它基于用户的兴趣偏好和与物品的历史交互行为进行推荐。这种方法可以分为基于记忆的方法和基于模型的方法。而基于记忆的方法可以分为两类：基于用户的（User-based CF）和基于物品的（Item-based CF）。基于内存的方法最流行的算法是KNN算法，该算法使用了一些传统的相似性度量，如 Pearson、Spearman、Cosine、Jaccard 等。另一方面，在基于模型的方法中，最常用的是矩阵分解（MF）及其变体（NMF、SVD）。目前，又出现了一些新的基于模型的协同过滤方法，如贝叶斯、基于聚类的、基于规则的和基于图的推荐方法。  &lt;br /&gt;  &lt;br /&gt;协同过滤主要存在两个问题：当用户与物品之间的交互很少时用户数据的稀疏性，以及冷启动问题（新用户和新物品）。另外就是是传统的推荐技术没有利用推荐场景中的诸多语义信息、关键字关系和层次结构。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;02&lt;/strong&gt;  &lt;strong&gt;基于内容过滤的推荐方法&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;基于该方法的推荐系统通过学习和用户过去偏好的物品在内容特征方面比较相似的新物品进行推荐。这类方法可以分为基于案例推理（case-based reasoning）和基于属性（attribute-based）的技术。基于案例推理的技术主要是推荐与用户过去喜欢的物品高度相关的物品。相比之下，基于属性的技术基于将物品属性与用户属性相匹配来进行推荐结果生成。大多数基于内容过滤的推荐系统使用的模型包括：关键字匹配或向量空间模型（VSM）、基于词频-逆文档频率（TF-IDF）加权、主题建模等。  &lt;br /&gt;  &lt;br /&gt;基于内容过滤的推荐方法，推荐出来的物品具有较高的文本相关性，同时可以很好的解释推荐结果，但是推荐出来的结果往往惊喜度较差，同时文本特征较为稀疏时也会影响相关性的计算。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;03&lt;/strong&gt;  &lt;strong&gt;基于人口统计信息过滤的推荐方法&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;该方法的主要思想是具有某些共同个人属性（性别、年龄、国家等）的用户也具有共同偏好这一事实。基于此，这些系统可以通过根据人口统计属性对用户进行分类来生成推荐结果。当物品的信息量很有限时，这些方法特别有用。该方法的一个优点是它不需要用户对基于内容和协同过滤方法所必需的物品进行评分或者有交互反馈。  &lt;br /&gt;  &lt;br /&gt;然而，这种类型的推荐方式的主要问题，一是由于涉及安全和隐私问题，为用户收集完整的信息是不切实际的；二是该方法向相关人口统计群体的用户推荐相同的商品，个性化程度受限。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;04&lt;/strong&gt;  &lt;strong&gt;基于上下文感知过滤的推荐方法&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;该类推荐系统结合场景上下文信息进行推荐。这种方法假设当前推荐场景的上下文是用一组预定义的可观察属性定义的，其结构不会随着时间的推移而发生显着变化。所谓的上下文信息主要包括时间、位置或者其他人（如朋友、亲戚或同事）。这些上下文信息为推荐结果的生成提供了额外的信息，相对于仅考虑用户或者物品自身信息，会有更多的补充。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;05&lt;/strong&gt;  &lt;strong&gt;基于知识过滤的推荐方法&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;该类推荐系统主要是基于领域知识考虑如何推荐以满足用户的兴趣偏好。这些系统应该使用三种类型的知识：关于用户的知识、关于物品的知识以及关于物品与用户需求之间对应关系的知识。总体上来说，该方法主要是依靠知识图谱来为推荐系统更多的辅助信息以提升推荐精准度。后面会展开来详细介绍。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;06&lt;/strong&gt;  &lt;strong&gt;混合过滤的推荐方法&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;这些系统通常将协同过滤与内容过滤或协同过滤与任何其他推荐方法相结合进行推荐。结合的目标是利用每种方法的优势以提高整体系统性能和推荐效果。目前，一些关于混合方法的工作包括基于深度学习方法、贝叶斯网络、聚类、潜在特征和图结构等等。  &lt;br /&gt;  &lt;br /&gt;近年来，基于深度神经网络的方法，如 DNN 、Wide &amp;amp; Deep、DeepFM在排序学习（Learn to Rank，LTR）方面取得了令人瞩目的表现。这些方法遵循嵌入（Enmbedding）和多层感知机(Multilayer Perceptron，MLP)范式，其中大规模稀疏特征首先嵌入到低维向量中，然后连接在一起输入多层感知器以学习特征之间的非线性关系。先进的LTR方法发现了从用户的历史行为中提取用户兴趣以进行排名的有效性。具体来说，DIN（Deep Interest Network）使用注意力机制从用户对候选物品的历史行为中学习用户兴趣的表示。DIEN（Deep Interest Evolution Network）使用循环神经网络来捕捉用户兴趣的演变。DMT（Method Deep Multifaceted Transformers）利用多个转换器对用户的不同行为序列进行建模。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;总体上来说，推荐算法是推荐系统的核心元素。基于协同过滤的推荐方式是以交互数据中用户或物品的相似性对用户兴趣偏好进行建模，而基于内容过滤的推荐方法则主要是利用物品的内容特征。基于协同过滤的推荐系统已被广泛应用，因为它们可以有效地捕获用户偏好，并且可以在多种场景中可以快速方便的实现，而无需像基于内容过滤的推荐系统中提取各种特征。然而，基于协同过滤的推荐方法存在数据稀疏和冷启动问题。为了解决这些问题，已经提出了很多类型的混合推荐系统来统一交互级相似性和内容级相似性。在这个过程中，也探索了多种类型的辅助信息，例如物品属性、评论数据、用户的社交网络等等。实践证明，混合推荐系统通常可以获得更好的推荐结果，并且近年来越来越受欢迎。  &lt;br /&gt;&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;知识图谱概述&lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;知识图谱（Knowledge Graph，KG）是一种描述实体或概念并使用不同类型的语义关系将它们连接起来的结构。2012 年，Google提出术语“知识图谱”来指代语义知识在网络搜索中的使用，目的是提高搜索引擎的能力，增强用户的搜索体验。在“知识图谱”一词流行之前，DBPedia和其他链接数据集是由语义Web技术和Berners-Lee提出的链接数据设计问题生成的。如今，KG已经在业界获得了广泛关注并进行了大规模的系统应用。  &lt;br /&gt;  &lt;br /&gt;在过去的数年中，越来越多的语义数据遵循关联数据原则，通过将来自不同主题领域的各种信息（如人、书籍、音乐、电影和地理位置）连接到一个统一的全球数据空间中来发布。这些异构的数据相互联系，形成了一个巨大的信息资源库，称为知识库。已经构建了几个典型的知识库，包括YAGO、NELL、DBpedia、DeepDive等学术项目，以及微软的Satori、谷歌的Knowledge Graph等商业项目。使用来自知识库的异构连接信息有助于深入了解单个领域的数据难以发现的问题。&lt;/p&gt;



 &lt;p&gt;以下是部分知识库介绍：&lt;/p&gt;



 &lt;ol&gt;
  &lt;li&gt;Freebase是一个非常实用的并且可拓展的元组数据库系统，旨在成为世界知识的公共存储库。它的设计灵感来自广泛使用的信息社区，如语义网和维基百科。Freebase 中的数据是结构化的，通过协作创建的方式生成。它支持高度多样化和异构的数据，并具有高可扩展性。Freebase 目前包含125000000+ 元组、4000+类型和 7000+属性。MQL (Metaweb Query Language)作为一种对数据执行查询和操作的语言，通过基于HTTP协议的图查询（graph-query）API可以实现对Freebase的读写操作。MQL为Freebase中的元组数据提供了易于使用的面向对象的接口，它的产生旨在促进通过协作方式创建基于 Web 的面向数据的应用程序。&lt;/li&gt;



  &lt;li&gt;DBpedia是从111种语言的维基百科版本中提取结构化数据来构建的一个大规模多语言知识库。从英文版维基百科中抽取的最大DBpedia知识库包含4亿多条事实数据，用于描述370万种事物。从其它的110个维基百科版本中抽取的DBpedia知识库总共包含14.6亿事实数据，描述1000万种额外事物。DBpedia将27种不同语言版本的维基百科信息框（infoboxes）映射到一个单一的共享本体中，该本体由320个类和1650 个属性组成。这些映射是通过世界范围内的众包工作创建的，从而可以很好的融合来自不同维基百科版本的知识。该项目定期发布所有DBpedia知识库以供下载，并通过本地DBpedia章节的全球网络提供对111种语言版本中的14 种语言版本的SPARQL查询访问。除了定期发布之外，该项目还维护一个实时知识库，该知识库会在维基百科中的页面发生更改时进行更新。DBpedia设置了2700万个RDF链接，指向30多个外部数据源，从而使来自这些源的数据能够与DBpedia数据一起使用。&lt;/li&gt;



  &lt;li&gt;YAGO是由德国马普研究所研制的链接数据库。YAGO主要集成了Wikipedia、WordNet和GeoNames三个来源的数据。YAGO建立在实体和关系之上，目前包含超过 100 万个实体和 500 万个事实，1.2亿条三元组知识，包括 Is-A 层次结构以及实体之间的非分类关系，事实已自动从Wikipedia中提取并与 WordNet统一。YAGO将WordNet的词汇定义与Wikipedia的分类体系进行了融合集成，使得YAGO具有更加丰富的实体分类体系。YAGO还考虑了时间和空间知识，为很多知识条目增加了时间和空间维度的属性描述。   &lt;br /&gt;&lt;/li&gt;
&lt;/ol&gt;



 &lt;p&gt;知识图谱本质上是一种基于图的数据结构,是一种揭示实体之间关系的语义网络。通俗来讲，就是把不同种类的信息连接在一起得到的一个语义关系网，知识图谱以结构化的方式描述客观世界，沉淀背景知识，将信息知识表示成更接近人类认识世界的形式，已经被广泛应用于搜索引擎、智能推荐、智能问答、语言理解、决策分析等领域。&lt;/p&gt;



 &lt;p&gt;  &lt;img src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221115/f6446d32-f809-4e64-b692-3c4f78322bcf.jpg"&gt;&lt;/img&gt;&lt;/p&gt;



 &lt;p&gt;图2 达观知识图谱功能展示&lt;/p&gt;



 &lt;p&gt;达观知识图谱，是达观数据公司面向各行业知识图谱应用而推出的新一代产品，其整合了知识图谱的设计、构建、编辑、管理、应用等全生命周期实现，基于客户的多源异构数据整合构建知识中台，可以实现从业务场景出发到生成图谱、再到实现基于图谱的应用，显著提高了各行业中知识图谱的落地效率和效果。  &lt;br /&gt;&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;知识图谱和推荐系统&lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;传统的推荐系统更多的是将用户和物品之间的显式或隐式反馈作为输入，这带来了两个问题：&lt;/p&gt;



 &lt;ol&gt;
  &lt;li&gt;在实际场景中，用户和物品之间的交互信息特别稀疏。例如，一个在线购物应用可能包含数十万的商品，而用户实际购买的商品数量可能仅有数百。使用如此少量的行为反馈数据来预测大量未知信息会显着增加算法过拟合的风险。   &lt;br /&gt;&lt;/li&gt;



  &lt;li&gt;对于新用户和新物品的推荐，由于缺乏历史交互信息，系统推荐的精准度就会受到极大的负面影响。解决稀疏性和冷启动问题的一种常见方法是在推荐算法的输入中引入额外的辅助信息，例如用户属性、项目属性和上下文信息等等。&lt;/li&gt;
&lt;/ol&gt;



 &lt;p&gt;近年来，将知识图谱作为辅助信息引入推荐系统已经成为了工业界和学术界的研究热点。KG一方面可以提供丰富的领域知识作为补充信息来克服协同过滤和基于内容过滤的推荐方法所面临的问题；另一方面，推荐系统可以使用 KG 中存在的语义关系来提高其准确性并增加推荐物品的多样性。具体来说，KG 推荐利用了代表用户的实体、要推荐的物品及其交互之间的联系。推荐系统使用各种连接来识别目标用户可能感兴趣的物品集合。因此，复杂的关系表示为基于KG的推荐系统提供了额外的有价值的信息，以在节点之间应用推理来发现新的连接。相反，一般来说，基于特征向量的经典推荐方法会忽略这种连接，这可能会导致整体的推荐性能欠佳，尤其是在数据稀疏的情况下。&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;融入知识图谱的推荐系统&lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;KG是一个异质图，节点表示实体，边缘表示实体之间的关系。物品及其属性可以映射到 KG 中，以表征物品之间的相互关系。此外，用户及其信息也可以集成到KG中，这就使得用户和物品之间的关系以及用户偏好可以更准确地捕获。&lt;/p&gt;



 &lt;p&gt;一般来说，基于KG的推荐方法，第一步需要构建KG，可以是物品知识图谱（Item Knowledge Graph，IKG），也可以是用户物品知识图谱（User-Item Knowledge Graph，UIKG）。  &lt;br /&gt;&lt;/p&gt;



 &lt;ol&gt;
  &lt;li&gt;关于IKG。在IKG中，物品和他们关联的实体（如物品属性）作为节点，而边可以表示物品的属性级关系（如品牌、类别等），也可以表示为用户相关的关系（如“都浏览”、“都购买”）。&lt;/li&gt;



  &lt;li&gt;关于UIKG。在UIKG中，用户、物品和他们相关的实体都是节点，边可以表示用户和物品之间的关系（如点击、收藏、购买等）。&lt;/li&gt;
&lt;/ol&gt;



 &lt;p&gt;以IKG的构建为例，物品首先映射到外部 KG 以找到它们的关联实体，然后从 KG 中提取关联实体的多跳邻居，并形成推荐系统的子图。当然也可以不需要依赖外部KG，可以基于所提供的数据中的辅助信息来构建KG。  &lt;br /&gt;  &lt;br /&gt;可解释的推荐系统是近年来的另一个热门研究方向。一方面，在推荐结果呈现的实现如果可以向用户提供适当的推荐解释，则用户可以相对更好地接受推荐结果。另一方面，也可以更深入地了解推荐算法。与传统的推荐系统相比，基于知识图谱的推荐系统呈现了连接用户和物品的多种实体和关系，并且能够很好地展示推理过程。&lt;/p&gt;



 &lt;p&gt;基于知识图谱的推荐方法，按照如何应用知识图谱数据，可以分为三类，分别是基于嵌入的方法、基于连接的方法和基于传播的方法。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;01&lt;/strong&gt;  &lt;strong&gt;基于嵌入的方法&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;基于嵌入（Enbedding-based）的方法主要思想是使用KG中大量的事实知识来进一步地丰富用户和物品的多维度表示，其中主要包括两大基础模块，一个是图嵌入模块，用于学习KG中实体和关系的表示，也就是需要应用知识图嵌入（Knowledge Graph Embedding，KGE）算法将KG编码为低秩嵌入，KGE算法可以分为两类：平移距离模型，如TransE、TransH、TransR、TransD等，以及语义匹配模型，如 DistMult。  &lt;br /&gt;  &lt;br /&gt;另外一个是推荐模块，基于学习到的特征用于预测用户对物品的偏好。基于这两个模块在整个推荐框架中的关联方式的差异，基于嵌入的方法可以进一步细分为两阶段学习的方法、联合学习的方法和多任务学习的方法。该类方法面临的挑战包括如何使用合适的KGE方法以获得实体的嵌入表示以及如何将学习到的实体嵌入表示集成到推荐模块中。&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221115/f6719286-4fc4-41e7-a4bb-9d9b2da85a7d.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;图3 DKN框架&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;  &lt;strong&gt;（1）两阶段学习方法   &lt;br /&gt;   &lt;br /&gt;&lt;/strong&gt;两阶段学习方法是指分别对图嵌入模块和推荐模块进行训练。第一步，使用KGE算法学习实体和关系的嵌入表示，接着，将预训练好的图相关嵌入连同其它的用户特征和物品特征输入到推荐模型进行用户兴趣预测。图3是用于新闻推荐的DKN（Deep Knowledge-aware Network）两阶段学习框架图。在第一阶段，提取新闻标题中的实体并将其映射到 Satori KG以挖掘新闻之间的知识级关系。DKN 通过将用KCNN学习到的句子的文本嵌入表示和通过TransD将新闻内容中的实体的知识级嵌入二者结合来对新闻进行建模。为了捕捉用户对新闻的动态兴趣，通过引入注意力机制，聚合用户的历史点击新闻的嵌入来学习用户的表示。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;两阶段学习方法易于实现，其中 KG 嵌入通常被视为后续推荐模块的额外特征。另一个好处是可以在没有交互数据的情况下学习 KG 嵌入，因此，大规模交互数据集不会增加计算复杂度。此外，由于KG通常是稳定的，一旦学习好了嵌入表示，就没有必要频繁更新嵌入表示。但是，通过 KGE 模型优化的实体嵌入更适合于图内应用，例如 KG补全。由于 KGE 模块和推荐模块是松耦合的，因此学习到的嵌入也可能不适合后续的推荐任务。&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221115/63c75c22-fa89-406f-addd-9d1df350af98.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;图4 CKE推荐系统流程&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;  &lt;strong&gt;（2）联合学习法   &lt;br /&gt;&lt;/strong&gt;另一个趋势是以端到端（end-to-end）的训练方式联合学习（Joint Learning）图嵌入模块和推荐模块。这样，推荐模块可以指导图嵌入模块中的特征学习过程。CKE（Collaborative Knowledge Base Embedding）统一CF框架中的各种类型的辅助信息，包括物品的属性级特征、文本特征和视觉特征。属性级特征用TransR编码以从KG中学习结构知识，而文本特征和视觉特征用自动编码器进行提取。这三个特征学习模块的目标函数加上推荐模块共同学习模型参数。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;联合学习方法可以进行端到端的训练，并且可以使用 KG 结构对推荐系统进行正则化。然而，在实际应用过程中，需要对不同目标函数的组合进行微调。&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221115/bd9a851e-c904-48f0-b628-b44e16b254b5.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;图5  MKR框架及交叉压缩单元示例&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;  &lt;strong&gt;（3）多任务学习法   &lt;br /&gt;&lt;/strong&gt;最近的一个研究方向是采用多任务学习（Multi-Task Learning）的策略，在KG相关任务的指导下训练推荐任务。动机是用户-物品交互二分图中的物品及其在 KG 中的关联实体可能共享相似的结构。因此，物品和实体之间低级特征的转移有助于促进推荐系统的改进。MKR（Multi-task feature learning approach for Knowledge graph enhanced Recommendation）由一个推荐模块和一个KGE模块组成。这两个模块不是将 KG 嵌入输入到推荐模块中，而是独立的，并通过交叉压缩单元进行连接以共享知识。推荐模块被训练以估计用户对候选物品的偏好，而KGE模块被训练来估计给定头部实体和三元组中的尾部实体表示。具体来说，推荐模块基于MLP以获得最终用户表示。最终的物品表示由L层交叉压缩单元及其在KG中的相关实体来进行细化。使用非线性函数估计用户对候选物品的偏好程度。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;通过应用多任务学习策略，有助于防止推荐系统过拟合，提高模型的泛化能力。然而，与联合学习方法类似，它需要努力在一个框架下集成不同的任务。  &lt;br /&gt;综上，尽管两阶段学习方法易于实现，但学习到的实体嵌入可能不适合推荐任务，联合学习方法通过端到端训练学习优化的实体嵌入，多任务学习方法通过从KG相关任务中转移知识进一步提高模型的泛化能力。但是，它需要大量的实验来找到不同目标函数的最佳组合。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;02&lt;/strong&gt;  &lt;strong&gt;基于连接的方法&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;基于连接（Connection-based）的方法利用图中的连接模式来指导推荐。相关的大多数工作都使用UIKG来挖掘图中实体之间的关系。探索KG中的连接信息有两种主要方法。第一个方向是利用图中的元结构，包括元路径和元图，来计算实体之间的相似度。基于元结构的相似性可以作为用户和物品表示的约束，也可以用于预测用户对交互历史中相似用户或相似物品的兴趣偏好。第二种解决方案是将用户-物品对或物品-物品对之间的连接模式编码为向量，可以集成到推荐框架中。这种方法也叫基于路径嵌入的方法。这种方法的挑战包括：1）如何为不同的任务设计合适的元路径；2）如何对实体之间的连接模式进行建模。  &lt;br /&gt;  &lt;br /&gt;  &lt;strong&gt;（1）基于元结构的方法&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;基于元结构（Meta-structure based）的方法的一种实现是利用不同元路径中实体的连接相似性作为图谱正则化项来约束用户和物品的表示。其动机是基于元路径的实体相似度越高，则在潜在空间中越接近。  &lt;br /&gt;目标函数如式（1）所示：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221115/adafc654-04f5-4d06-9545-19749e082ecc.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;其中LRec表示推荐系统的目标函数，常见的选择是矩阵分解。相似性约束LSim指导用户嵌入和物品嵌入的学习。为了度量图中实体之间的连接相似性，通常使用PathSim, 如式（2）所示：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221115/9c802e8c-695c-4611-a461-deb4b403f8b3.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;其中Pm~n是实体m和n之间的一条路径。通常使用三种类型的实体相似性，具体如下：（a）用户-用户相似度，目标函数如式（3）所示：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221115/382e3b37-57cd-46f9-9c36-5aab988a6505.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;其中||Ui-Uj||F表示矩阵 Frobenius 范数， ɵ=[ɵ1,ɵ2,.....ɵL]表示每个元路径的权重，U=[u1，u2，...，um]表示所有用户的潜在向量，S[1-(i,j)]表示用户i和j在元路径中的相似度得分。如果用户共享基于元路径的高相似性，则用户-用户相似性会迫使用户的嵌入在潜在空间中接近。&lt;/p&gt;



 &lt;p&gt;（b）物品-物品相似度，目标函数如式（4）所示：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221115/d7b63be7-a3d5-4126-b234-b50710d060a6.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;其中 V=[v1,v2,...,vn]表示所有物品的潜在向量.与用户-用户相似度类似，如果物品的基于元路径的相似度很高，则物品的低秩表示应该是接近的。&lt;/p&gt;



 &lt;p&gt;（c）用户-物品相似度，目标函数如式（5）所示：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221115/967e12c6-a499-4ed6-bed4-01f926c3f3b6.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;如果基于元路径的相似度很高，则用户-物品相似度项将迫使用户和物品的潜在向量彼此接近。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;除了以上三种相似度的方法以外，基于元结构的方法也可以利用实体相似度来预测用户对未评分物品的兴趣，这可以作是KG中的偏好融合。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;综上，上述方法首先从交互矩阵及其基于元结构的相互相似性中学习用户和物品的潜在向量，然后基于增强的表示进行预测。也可以直接使用相似用户评分的加权集合来预测对未评分项目的偏好。基于元结构的方法是可以解释的，因为这些手动设计的元结构通过匹配候选物品与交互物品或目标用户之间的元结构来为推荐系统提供更多参考信息。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;基于元结构的方法易于实现，大多数工作都是基于模型复杂度相对较低的MF技术。然而，元路径或元图的选择需要领域知识，并且这些元结构对于不同的数据集可能会有很大差异。此外，在某些特定场景下可能不适合应用基于元结构的方法。例如，在新闻推荐任务中，属于一个新闻的实体可能属于不同的域，这使得元路径设计变得困难。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;  &lt;strong&gt;（2）基于路径嵌入的方法&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;基于元结构的方法的一个问题是连接模式没有明确建模，这使得很难学习用户-物品对和连接模式之间的相互影响。但是，基于路径嵌入的方法可以显式地学习连接模式的嵌入。通过学习连接UIKG中的用户-物品对或IKG 中的物品-物品对的路径的显式嵌入，以便直接建模用户-物品或物品-物品关系。以UIKG中的关系建模为例，假设KG中有K条连接ui和Vj的路径，路径p的嵌入表示为hp，则可以通过式（6）获得ui和Vj之间交互的最终表示：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221115/684caf7e-0939-43dc-97ca-9537a97d140c.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;其中g(∙)是从每个路径嵌入中汇总信息的函数，常见的选择是最大池化操作或加权求和操作。然后，ui和Vj的偏好可以通过式(7)建模：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221115/551a0ff2-171d-42ad-8e59-5c8249dd72e2.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;其中f(∙)是映射用户-物品对之间的交互表示以及用户-物品对嵌入到偏好分数的函数。&lt;/p&gt;



 &lt;p&gt;推荐结果可以通过检查每个元路径的权重来解释。较高的元路径权重意味着目标用户和候选物品之间的这种关系在做出决策时更重要。  &lt;br /&gt;  &lt;br /&gt;基于路径嵌入的方法将用户-物品对或物品-物品对的连接模式编码为潜在向量，从而可以考虑目标用户、候选物品和连接模式的相互影响.此外，大多数模型能够通过计算合适的路径并选择显著路径来自动挖掘连接模式，而无需预定义的元结构的帮助。因此，它很可能捕捉到富有表现力的连接模式。但是，如果图中的关系很复杂，则图中可能的路径数量可能会增长到很大。随意实际上，不可能利用大规模 KG 中每个实体对的所有路径，这可能会阻碍模型的性能。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;综上，基于连接的方法在很大程度上依赖于连接模式。但是元路径的表示能力是有限的，这阻碍了传统的基于元结构的方法的性能。基于路径嵌入的方法进一步克服了基于元结构的方法的另一个缺点，即需要领域知识和人工配置路径。这些方法枚举可能的路径并显式建模用户-物品对或物品-物品对之间的关系。然而，基于路径嵌入的方法在一定程度上牺牲了可扩展性，因为这些模型相对复杂，在枚举路径和学习表示时需要更多的计算。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;03&lt;/strong&gt;  &lt;strong&gt;基于传播的方法&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;基于嵌入的方法利用知识图谱中的语义关系来丰富用户和物品的表示，但难以捕捉实体之间的高阶关系。基于连接的方法使用图中的连接信息来指导推荐，但是，通过将复杂的用户物品连接模式分解为单独的线性路径，不可避免地会丢失信息。为了充分利用 KG 中的信息，基于传播的方法集成实体和关系的表示以及高阶连接模式，以实现更个性化的推荐。基于传播的方法的主要想法是嵌入传播，其中常见的实现方式是基于 GNN 技术。这些方法通过聚合KG 中多跳邻居的嵌入表示来细化实体表示。然后，可以使用用户和潜在项目的丰富表示来预测用户的偏好。&lt;/p&gt;



 &lt;p&gt;根据在消息传播过程中细化的实体类型产的差异可以进一步的进行细分为三类。这种方法的挑战包括：&lt;/p&gt;



 &lt;ol&gt;
  &lt;li&gt;如何为不同的邻居分配适当的权重&lt;/li&gt;



  &lt;li&gt;如何在不同的关系边上传播消息 &lt;/li&gt;



  &lt;li&gt;如何提高模型的可扩展性   &lt;br /&gt;&lt;/li&gt;
&lt;/ol&gt;



 &lt;p&gt;  &lt;strong&gt;（1）用户嵌入表示的细化&lt;/strong&gt;  &lt;br /&gt;根据用户的交互历史细化用户嵌入表示。先是构建IKG使用多个关系将交互物品和候选物品连接起来。则用户可以表示为他们交互物品及其多跳邻居的组合。具体来说，交互历史中的物品被选为传播过程的种子。然后，沿图中的链接提取多跳三元组集合S[k-ui](k=1,2,...，H),其中S[1-ui]是三元组集(eh,r,et)，头部实体是用户ui的交互过的物品列表。学习用户表示ui的过程可以表述为如下两步：&lt;/p&gt;



 &lt;p&gt;（a）通过聚合三元组集合S[k-ui](k=1,2,...，H)的每一层中的实体来计算用户的嵌入表示o[k-u]。&lt;/p&gt;



 &lt;p&gt;（b）合并o[k-u](k=1,2,...，H)，得到最终的用户嵌入表示ou。  &lt;br /&gt;&lt;/p&gt;



 &lt;p&gt;由于传播过程是从用户交互过的物品开始，到远邻结束，这个过程可以看作是在IKG中逐层向外传播用户的偏好。因此，这些方法可以解释为沿着 KG 中的路径从历史兴趣中传播用户的偏好。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;在这些方法中，边权重在IKG 中是明确的。因此，可以选择连接候选物品和交互项目的显著路径，并作为推荐结果的解释。尽管这些工作同时利用了实体嵌入和高阶连接信息，但只有用户嵌入表示在传播过程中得到更新。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;  &lt;strong&gt;（2）物品表示的细化   &lt;br /&gt;&lt;/strong&gt;上面介绍了通过在图中向外聚合实体来优化用户嵌入表示。另一种方式是通过聚合项目Vj的多跳邻居N[k-u](k=1,2,...，H)在IKG中向内的嵌入表示来学习候选物品Vj的高阶表示。在向内传播过程中，采用图注意力机制，其中不同邻居的权重是由用户和关系来确定的。主要是考虑到用户对不同的关系是有不同的偏好的，从而可以确定KG的信息流。&lt;/p&gt;



 &lt;p&gt;每一轮传播过程表示为如下两步:&lt;/p&gt;



 &lt;p&gt;（a）通过式（8）聚合实体ei的近邻：  &lt;img src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221115/869272fa-1fae-4ce2-9174-c38b1138187a.jpg"&gt;&lt;/img&gt;  &lt;br /&gt;（b）使用h—1阶邻居嵌入和自嵌入更新实体的h阶表示，如式（9）所示：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221115/f35d8e8b-2b49-48a4-81da-28507bf808b3.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;其中e[0-i]代表实体的初始表示，e[h-i]代表实体的h阶表示，它是实体初始表示和来自h跳邻居的表示的混合。聚合函数将N个邻居映射到向量∈Rd，更新函数g(∙)是一个非线性函数：Rd ⨉ Rd → Rd。通过迭代地重复这个过程H次，候选物品的表示则包含了来自H跳邻居的信息。  &lt;br /&gt;  &lt;br /&gt;综上，通过IKG中的向内传播来细化物品的嵌入表示。然而，类似于在 KG 中向外聚合的用户细化，只有一种类型的实体被细化。  &lt;br /&gt;  &lt;br /&gt;  &lt;strong&gt;（3）用户和物品表示的细化   &lt;br /&gt;&lt;/strong&gt;在UIKG中的传播过程中，用户、物品及其关联实体都连接在一个图中，用户-物品对之间的交互作为一种关系。用户嵌入和物品嵌入可以在传播过程中使用其对应的邻居进行细化，如式 (8) 和 (9) 所示。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;与IKG中的传播类似，UIKG中边的权重也是由用户确定的。因此，这些模型可以通过检查连接目标用户和候选物品的显著路径来为推荐结果提供解释。由于用户被合并为一种类型的节点，因此解释更加直观，因为每个交互物品的贡献都是可用的。通过将用户纳入KG，可以更大程度地探索高阶连接模式。缺点是图中的关系越多，会带来不相关的实体，可能会误导用户在聚合过程中的偏好。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;综上，基于传播的方法通常计算成本高。随着图变大，模型变得难以收敛。为了提高效率，可以使用更快的图卷积运算，并且通常在每一层中应用邻域采样。但是，随机抽样不可避免地会导致信息丢失，无法充分挖掘图中的知识。  &lt;br /&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;04&lt;/strong&gt;  &lt;strong&gt;基于KG的推荐方法总结&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;通过上述基于嵌入方法、基于连接方法和基于传播方法的介绍，可知基于嵌入的方法是最灵活的方法。一方面，使用KGE模块对KG进行编码相对容易，并且学习到的嵌入可以自然地融入到用户表示或项目表示中。而在基于连接的方法中，在图中定义元路径或元图可能很繁琐。对于基于传播的方法，需要仔细设计聚合和更新部分。另一方面，基于嵌入的方法适用于大多数应用场景，因为外部知识通常在不同的任务中可用。相反，在基于元结构的方法中，元路径对于不同的应用场景通常是多种多样的，并且不能泛化到新的数据集。此外，对于特定场景，如新闻推荐，很难定义元路径并应用基于元结构的方法。同时，基于路径嵌入的方法和基于传播的方法都不适用于具有大规模数据集的推荐场景，因为在枚举路径和邻居时计算复杂度可能会变得很大。此外，路径的质量和数量对于基于连接的方法至关重要，因此，稀疏数据集可能无法提供足够的路径来挖掘此类方法的关系和模型兴趣。然而，基于嵌入的方法和基于连接的方法都未能充分探索KG中的信息。近年来，随着GNN技术的发展，基于传播的方法已成为一种新的研究趋势。此外，基于连接的方法和基于传播的方法都可以用KG中的路径来解释，而基于嵌入的方法解释起来不太直观。  &lt;br /&gt;&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;基于KG推荐的可解释性&lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;KG中包含有大量的辅助信息可以用于推荐结果的解释，主要有以下几种方法：  &lt;br /&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;01&lt;/strong&gt;  &lt;strong&gt;关系嵌入的注意机制&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;这种方法主要应用于基于嵌入的方法。注意力机制应用于KG中实体之间关系的嵌入。从不同关系的注意力权重，可以得到每类物品属性对目标用户的意义。因此，这种技术可以为推荐提供偏好级别的解释。  &lt;br /&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;02&lt;/strong&gt;  &lt;strong&gt;定义元路径或者元图&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;所选物品与目标用户或交互物品之间的关系可以分解为若干元路径或元图的组合。通过将元路径或元图转换为可理解的规则，系统可以提供解释。  &lt;br /&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;03&lt;/strong&gt;  &lt;strong&gt;路径嵌入的注意机制&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;对于路径嵌入方法，连接目标用户和候选物品的特定路径的权重可通过注意力机制获得。每条路径的权重可以代表每条路径对用户的相对重要性。因此，可以根据图中的显著路径来提供解释。  &lt;br /&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;04&lt;/strong&gt;  &lt;strong&gt;UIKG中的强化学习&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;通过使用强化学习技术在UIKG中训练代理，可以挖掘连接用户物品对的实际路径。它可以直接显示KG中的推理过程，而不是为已经选择的推荐结果寻找事后解释。因此，推理过程对于目标用户来说是精确且值得信赖的。  &lt;br /&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;05&lt;/strong&gt;  &lt;strong&gt;提取边缘权重&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;基于传播的方法需要在聚合过程中为每种类型的邻居分配用户特定的权重。边权重控制图中实体之间的信息流，可以反映KG中每种关系的重要性。此外，KG中实体之间的边权重也可以从注意力权重或学习关系矩阵中获得。因此，可以通过找到连接候选物品和目标用户的显著路径或多跳邻居中的交互物品来生成解释。  &lt;br /&gt;&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;未来展望 &lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;通过前面的介绍可以知道，基于KG的推荐系统在推荐精准度和推荐结果可解释性方面具有诸多优势。在学术界和工业界也已经提出了很好的模型以充分利用KG中的辅助信息进行个性化精准推荐。但是在一些方向上依然还有很多工作值得深入研究，主要体现在：&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;01 &lt;/strong&gt;  &lt;strong&gt;动态推荐&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;尽管具有GNN或GCN架构的基于KG的推荐系统取得了良好的性能，但训练过程非常耗时。因此这样的模型可以被视为静态偏好推荐。然而，在某些场景下，例如在线购物、新闻推荐等，用户的兴趣会很快受到社交事件等的影响。在这种情况下，使用静态偏好建模的推荐可能不足以理解实时兴趣。为了捕捉动态偏好，利用动态图网络可能是一种解决方案。  &lt;br /&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;02 &lt;/strong&gt;  &lt;strong&gt;跨域推荐&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;在跨领域推荐的也有一些研究进展，主要是交互数据在各个领域是不平衡的。例如，在亚马逊平台上，图书子集大于其他域。通过迁移学习技术，可以共享来自具有相对丰富数据的源域的交互数据，以便在目标域中进行更好的推荐。  &lt;br /&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;03 &lt;/strong&gt;  &lt;strong&gt;知识增强语言表示&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;为了提高各种 NLP 任务的性能，有一种趋势是将外部知识集成到语言表示模型中，使知识表示和文本表示可以相互提炼。将知识增强文本表示策略应用于基于文本的推荐任务中，可以更好地进行表示学习，以提供更准确的推荐。  &lt;br /&gt;  &lt;br /&gt;   &lt;em&gt;参考文献   &lt;br /&gt;&lt;/em&gt;  &lt;em&gt;[1] Bollacker K, Evans C, Paritosh P, et al. Freebase: a collaboratively created graph database for structuring human knowledge[C]//Proceedings of the 2008 ACM SIGMOD international conference on Management of data. 2008: 1247-1250.&lt;/em&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;em&gt;[2] Lehmann J, Isele R, Jakob M, et al. Dbpedia–a large-scale, multilingual knowledge base extracted from wikipedia[J]. Semantic web, 2015, 6(2): 167-195.&lt;/em&gt;  &lt;em&gt;[3] Suchanek F M, Kasneci G, Weikum G. Yago: a core of semantic knowledge[C]//Proceedings of the 16th international conference on World Wide Web. 2007: 697-706.&lt;/em&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;em&gt;[4] Bordes A, Usunier N, Garcia-Duran A, et al. Translating embeddings for modeling multi-relational data[J]. Advances in neural information processing systems, 2013, 26.&lt;/em&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;em&gt;[5] Wang Z, Zhang J, Feng J, et al. Knowledge graph embedding by translating on hyperplanes[C]//Proceedings of the AAAI conference on artificial intelligence. 2014, 28(1).&lt;/em&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;em&gt;[6] Lin Y, Liu Z, Sun M, et al. Learning entity and relation embeddings for knowledge graph completion[C]//Twenty-ninth AAAI conference on artificial intelligence. 2015.&lt;/em&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;em&gt;[7] Ji G, He S, Xu L, et al. Knowledge graph embedding via dynamic mapping matrix[C]//Proceedings of the 53rd annual meeting of the association for computational linguistics and the 7th international joint conference on natural language processing (volume 1: Long papers). 2015: 687-696.&lt;/em&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;em&gt;[8] Yang B, Yih W, He X, et al. Embedding entities and relations for learning and inference in knowledge bases[J]. arXiv preprint arXiv:1412.6575, 2014.&lt;/em&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;em&gt;[9] Zou X. A survey on application of knowledge graph[C]//Journal of Physics: Conference Series. IOP Publishing, 2020, 1487(1): 012016.&lt;/em&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;em&gt;[10] Q. Guo et al., &amp;quot;A Survey on Knowledge Graph-Based Recommender Systems,&amp;quot; in IEEE Transactions on Knowledge and Data Engineering, vol. 34, no. 8, pp. 3549-3568, 1 Aug. 2022, doi: 10.1109/TKDE.2020.3028705.&lt;/em&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;em&gt;[11] Chicaiza J, Valdiviezo-Diaz P. A comprehensive survey of knowledge graph-based recommender systems: Technologies, development, and contributions[J]. Information, 2021, 12(6): 232.&lt;/em&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;em&gt;[12] Choudhary S, Luthra T, Mittal A, et al. A survey of knowledge graph embedding and their applications[J]. arXiv preprint arXiv:2107.07842, 2021.&lt;/em&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;em&gt;[13] Gao Y, Li Y F, Lin Y, et al. Deep learning on knowledge graph for recommender system: A survey[J]. arXiv preprint arXiv:2004.00387, 2020.&lt;/em&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;em&gt;[14] Wang H, Zhang F, Xie X, et al. DKN: Deep knowledge-aware network for news recommendation[C]//Proceedings of the 2018 world wide web conference. 2018: 1835-1844.&lt;/em&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;em&gt;[15] Zhang F, Yuan N J, Lian D, et al. Collaborative knowledge base embedding for recommender systems[C]//Proceedings of the 22nd ACM SIGKDD international conference on knowledge discovery and data mining. 2016: 353-362.&lt;/em&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;em&gt;[16] Wang H, Zhang F, Zhao M, et al. Multi-task feature learning for knowledge graph enhanced recommendation[C]//The world wide web conference. 2019: 2000-2010.   &lt;br /&gt;&lt;/em&gt;&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;作者简介&lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;于敬，达观数据联合创始人，搜索推荐图谱产品团队的总负责人。同济大学计算机硕士，上海市青年科技启明星、上海市五一劳动奖章、上海市职工优秀创新成果奖、ACM CIKM算法竞赛国际冠军等奖项荣誉获得者。国际计算机学会（ACM）会员、中国计算机学会（CCF）高级会员、上海计算机学会（SCS）会员。曾先后在盛大创新院、盛大文学和腾讯文学从事技术研发工作，在智能推荐、搜索引擎、机器学习、大数据技术等领域有丰富的研究和工程经验，拥有十余项授权专利。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>人工智能 推荐系统 深度学习 自然语言处理 知识图谱</category>
      <guid isPermaLink="true">https://itindex.net/detail/62491-%E7%9F%A5%E8%AF%86-%E5%A2%9E%E5%BC%BA-%E6%99%BA%E8%83%BD</guid>
      <pubDate>Thu, 17 Nov 2022 15:35:47 CST</pubDate>
    </item>
    <item>
      <title>使用Excel搭建推荐系统</title>
      <link>https://itindex.net/detail/62488-excel-%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F</link>
      <description>&lt;p&gt;在上一篇  &lt;a href="https://www.biaodianfu.com/spreadsheets-and-excel.html"&gt;重新认识Excel&lt;/a&gt;的文章中，提到了Excel无所不能，然后就想到了曾经看到的这篇关于如何使用Excel搭建推荐引擎的文章。于是找了出来做了下简单的翻译（只翻译了重点部分）。&lt;/p&gt;
 &lt;p&gt;在互联网上有无限的货架空间，找到你想看的东西可能会让人筋疲力尽。幸运的是，与决策疲劳作斗争是 Netflix 的工作……而且他们很擅长。太擅长了。他们神奇地向您推荐完美的电影，这样您的眼睛就会一直盯着管子，他们会把您的拖延变成周末沙发上的狂欢。该死的，Netflix。你的秘诀是什么？你怎么这么了解我们？“魔法”非常简单，本教程使用分步电子表格揭示了其中的秘密。&lt;/p&gt;
 &lt;p&gt;尽管自Netflix Prize 竞赛以来有大量关于推荐系统的论文或视频，但大多数要么 (A) 技术太高，初学者无法使用，要么 (B) 水平太高，不实用。&lt;/p&gt;
 &lt;p&gt;在这篇文章中，我们将从头开始构建一个电影推荐系统，其中包含简单的英语解释和可以在 Excel 中遵循的分步公式。所有梯度下降推导都是手工计算的，您可以使用 Excel 下拉过滤器来微调模型的超参数并增强您的理解。&lt;/p&gt;
 &lt;h2&gt;学习内容&lt;/h2&gt;
 &lt;ul&gt;
  &lt;li&gt;构建帮助赢得 100 万美元 Netflix 奖金的算法版本 SVD++ 背后的确切步骤。&lt;/li&gt;
  &lt;li&gt;机器如何实际学习（梯度下降）。即使您从未告诉过 Netflix，也可以观看 Netflix 了解您的电影品味。&lt;/li&gt;
  &lt;li&gt;超参数调优。了解如何调整模型输入 （学习率、L2 正则化、# of epochs、权重初始化）以获得更好的预测。&lt;/li&gt;
  &lt;li&gt;模型评估和可视化。了解训练数据和测试数据之间的区别，如何防止过度拟合，并了解如何可视化模型的特征。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;在简要介绍了推荐系统之后，我将通过以下 4 个部分来构建一个模型来预测少数好莱坞明星的电影评分。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;第一部分：模型概览&lt;/li&gt;
  &lt;li&gt;第二部分：观看魔法秀（权重初始化和训练）&lt;/li&gt;
  &lt;li&gt;第三部分：魔法揭秘（梯度下降、导数）。我将逐步讲解机器学习魔法背后的数学，我将使用实数作为例子代入批量梯度下降的公式（不会使用“宏”或者Excel求解器之类的东西隐藏细节）。&lt;/li&gt;
  &lt;li&gt;第四部分：模型评估和可视化&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;推荐系统简介&lt;/h2&gt;
 &lt;p&gt;电影推荐系统可以简化为两大类：协同过滤（询问密友）和基于内容的过滤（标签匹配）&lt;/p&gt;
 &lt;h3&gt;协同过滤&lt;/h3&gt;
 &lt;p&gt;协同过滤基于类似行为进行推荐。&lt;/p&gt;
 &lt;p&gt;如果Ross和Rachel过去喜欢类似的东西，那么我们将Rachel喜欢而Ross没看过的电影向Ross推荐。你可以将他们看成是“协同”过滤网络货架上的噪音的“品味分身”。如果两个用户的评分有强相关性，那么我们就认定这两个用户“相似”。评分可以是隐式的，也可以是显式的：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;隐式（Binging）—— 整个周末，Ross和Rachel都沉溺于老剧《老友记》。尽管他们没人点赞，但我们相当确定他们喜欢《老友记》（以及他们可能有点自恋）。&lt;/li&gt;
  &lt;li&gt;显式（喜欢）—— Ross和Rachel都点了赞。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;协同过滤有两种：近邻方法和潜因子模型（矩阵分解的一种形式）。本文将聚焦一种称为  &lt;a href="https://www.biaodianfu.com/matrix-factorization.html#SVD++"&gt;SVD++&lt;/a&gt;的潜因子模型。&lt;/p&gt;
 &lt;h3&gt;基于内容的过滤&lt;/h3&gt;
 &lt;p&gt;基于你过去喜欢的内容的明确标签（类型、演员，等等），Netflix向你推荐具有类似标签的新内容。&lt;/p&gt;
 &lt;h3&gt;100 万美元的赢家&lt;/h3&gt;
 &lt;p&gt;当数据集足够大时，协同过滤（CF）是电影推荐器中的不二之选。&lt;/p&gt;
 &lt;p&gt;虽然这两个大类之间有无数的混合和变化，但出人意料的是，当CF模型足够好时，加上元数据并没有帮助。这是为什么呢？人会说谎，行动不会。让数据自己说话。人们所说的他们喜欢的东西（用户偏好、调查等）与他们的行为之间存在很大差距。最好让人们的观看行为自己说话。窍门：想要改善Netflix推荐？访问  &lt;a href="http://www.netflix.com/WiViewingActivity"&gt;/WiViewingActivity&lt;/a&gt;清理你的观看记录，移除你不喜欢的项。）&lt;/p&gt;
 &lt;p&gt;2009年，Netflix奖励了一队研究人员一百万美元，这个团队开发了一个算法，将Netflix的预测精确度提升了10%. 尽管获胜算法实际上是超过100种算法的集成，SVD++（一种协同过滤算法）是其中最关键的算法之一，贡献了大多数收益，目前仍在生产环境中使用。&lt;/p&gt;
 &lt;p&gt;我们将创建的SVD++模型（奇异值分解逼近）和Simon Funk的博客文章中提到差不多。这篇不出名的文章是2006年Simon在Netflix竞赛开始时写的，首次提出了SVD++模型。在SVD++模型成功之后，几乎所有的Netflix竞赛参加者都用它。&lt;/p&gt;
 &lt;h3&gt;SVD++ 关键思想&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;“奇异值”（电影评分）可以“分解”或由一组隐藏的潜在因素（用户偏好和电影特征）决定，这些潜在因素直观地代表了类型、演员等。&lt;/li&gt;
  &lt;li&gt;潜在因素可以使用梯度下降和已知的电影评分迭代学习。&lt;/li&gt;
  &lt;li&gt;用户/电影偏见会影响某人的评分，并且也会被学习。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;简单，但功能强大。让我们深入挖掘。&lt;/p&gt;
 &lt;h2&gt;第一部分：模型概览&lt;/h2&gt;
 &lt;h3&gt;数据&lt;/h3&gt;
 &lt;p&gt;博客文章模型使用 30 个虚构评分（5 个用户 x 6 部电影）来简化教程。要在我们进行过程中跟随并试验模型，您可以在  &lt;a href="https://drive.google.com/open?id=1y4X8H56TS6M7AXAU7yIm0EyxhqNUy1sz"&gt;此处&lt;/a&gt;下载电子表格（Excel 或 Google 表格） 。&lt;/p&gt;
 &lt;h3&gt;拆分数据——训练集和测试集&lt;/h3&gt;
 &lt;p&gt;我们将使用25项评价来训练模型，剩下5项评价测试模型的精确度。&lt;/p&gt;
 &lt;p&gt;我们的目标是创建一个在25项已知评价（训练数据）上表现良好的系统，并希望它在5项隐藏（但已知）评价（测试数据）上做出良好的预测。&lt;/p&gt;
 &lt;p&gt;如果我们有更多数据，我们本可以将数据分为3组——训练集（约70%）、验证集（约20%）、测试集（约10%）。&lt;/p&gt;
 &lt;h3&gt;评价预测公式&lt;/h3&gt;
 &lt;p&gt;评价预测是用户/电影特征的矩阵乘法（“点积”）加上用户偏置，再加上电影偏置。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="422" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel1.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;公式为：&lt;/p&gt;
 &lt;p&gt;$$\hat{r}_{i,j}=((u_1 m_1)+(u_2 m_2)+(u_3 m_3)+u_{bias}+m_{bias})$$&lt;/p&gt;
 &lt;p&gt;其中：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;$\hat{r}_{i,j}$表示用户i对电影j的预测评价&lt;/li&gt;
  &lt;li&gt;$u_1$、$u_2$、$u_3$为用户潜因子&lt;/li&gt;
  &lt;li&gt;$m_1$、$m_2$、$m_3$为电影潜因子&lt;/li&gt;
  &lt;li&gt;$u_{bias}$为用户偏置&lt;/li&gt;
  &lt;li&gt;$m_{bias}$为电影偏置&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;用户/电影特征&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;直觉上说，这些特征表示类型、演员、片长、导演、年代等因素。尽管我们并不清楚每项特征代表什么，但是当我们将其可视化后（见第四部分）我们可以凭直觉猜测它们可能代表什么。&lt;/li&gt;
  &lt;li&gt;出于简单性，我使用了3项特征，但实际的模型可能有50、100乃至更多特征。特征过多时，模型将“过拟合/记忆”你的训练数据，难以很好地推广到测试数据的预测上。&lt;/li&gt;
  &lt;li&gt;如果用户的第1项特征（让我们假定它表示“喜剧”）值较高，同时电影的“喜剧”特征的值也很高，那么电影的评价会比较高。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;用户/电影偏置&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;用户偏置取决于评价标准的宽严程度。如果Netflix上所有的平均评分是3.5，而你的所有评分的均值是4.0，那么你的偏置是0.5. 电影偏置同理。如果《泰坦尼克号》的所有用户的评分均值为4.25，那么它的偏置是0.75（= 4.25 – 3.50）。&lt;/p&gt;
 &lt;h3&gt;RMSE —— 评估预测精确度&lt;/h3&gt;
 &lt;p&gt;RMSE = Root Mean Squared Error （均方根误差）&lt;/p&gt;
 &lt;p&gt;RMSE是一个数字，尝试回答以下问题“平均而言，预测评价和实际平均差了几颗星（1-5）？”&lt;/p&gt;
 &lt;p&gt;RMSE越低，意味着预测越准……&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="532" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-2.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;观察：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;我们只在意绝对值差异。相比实际评分高估了1分的预测，和相比实际评分低估了1分的预测，误差相等，均为1。&lt;/li&gt;
  &lt;li&gt;RMSE是误差同数量级的平均，而不是误差绝对值的平均。在我们上面的例子中，误差绝对值的平均是75（1 + 1 + 0.25 = 2.25，2.25 / 3 = 0.75），但RMSE是0.8292. RMSE给较大的误差更高的权重，这很有用，因为我们更不希望有较大的误差。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;超参数调整&lt;/h3&gt;
 &lt;p&gt;通过电子表格的下拉过滤器，可以调整模型的3个超参数。你应该测试下每种超参数，看看它们对误差的影响。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;训练epoch数—— 1个epoch意味着整个训练集都过了一遍&lt;/li&gt;
  &lt;li&gt;学习率—— 控制调整权重/偏置的速度&lt;/li&gt;
  &lt;li&gt;L2（lambda）惩罚因子—— 帮助模型预防过拟合训练数据，以更好地概括未见测试数据。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" height="109" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-3.png" width="324"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;现在，让我们看一场魔法秀，看看模型是如何从随机权重开始，学习最优权重的。&lt;/p&gt;
 &lt;h2&gt;第二部分：观看魔法秀（权重初始化和训练）&lt;/h2&gt;
 &lt;p&gt;观看梯度下降的实际操作感觉就像您在观看 David Blaine 的魔术。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;他到底是怎么知道我会在52张牌中选这张的呢？&lt;/li&gt;
  &lt;li&gt;等等，他刚刚是不是浮空了？&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;最后你深感敬畏，想要知道魔术是如何变的。我会分两步演示，接着揭露魔法背后的数学。&lt;/p&gt;
 &lt;h3&gt;抽一张卡，随便抽一张（权重初始化）&lt;/h3&gt;
 &lt;p&gt;在训练开始，用户/电影特征的权重是随机分配的，接着算法在训练中学习最佳的权重。&lt;/p&gt;
 &lt;p&gt;为了揭示这看起来有多么“疯狂”，我们可以随机猜测数字，然后让计算机学习最佳数字。下面是两种权重初始化方案的比较：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;简单—— 用户特征我随机选择了1、0.2、0.3，剩下的特征都分配0.1.&lt;/li&gt;
  &lt;li&gt;Kaiming He—— 更正式、更好的初始化方法，从高斯分布（“钟形曲线”）中随机抽样作为权重，高斯分布的均值为零，标准差由特征个数决定（细节见后）。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;观赏魔术（查看训练误差）&lt;/h3&gt;
 &lt;p&gt;看看使用以上两种方案学习权重最佳值的效果，从开始（epoch 0）到结束（epoch 50），RMSE训练误差是如何变化的：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="1200" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-5.gif" width="1068"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;如你所见，两种权重初始化方法在训练结束时都会收敛到类似的“误差”（0.12 与 0.17），但“   &lt;a href="http://www.jefkine.com/deep/2016/08/08/initialization-of-deep-networks-case-of-rectifiers/"&gt;Kaiming He”&lt;/a&gt;方法更快地收敛到较低的误差。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;关键要点：无论我们从哪个权重开始，机器都会随着时间的推移学习到好的值！&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="365" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-6.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;注意：如果你想要试验其他初始化权重，可以在电子表格的“hyperparameters_and_initial_wts”表的G3-J7、N3-Q8单元格中输入你自己的值。权重取值范围为-1到1.&lt;/p&gt;
 &lt;p&gt;想要了解更多关于Kaiming He初始化的内容，请接着读下去；否则，可以直接跳到第3部分学习算法的数学。&lt;/p&gt;
 &lt;h3&gt;Kaiming He权重初始化&lt;/h3&gt;
 &lt;p&gt;权重 = 正态分布随机抽样，分布均值为0，标准差为 (=SquareRoot(2/# of features))&lt;/p&gt;
 &lt;p&gt;电子表格中的值由以下公式得到：=NORMINV(RAND(),0,SQRT(2/3))&lt;/p&gt;
 &lt;p&gt;$$W_l \sim \mathcal{N}(0, \sqrt{\frac{2}{n_l}}) \text{and} \mathbf{b}=0$$&lt;/p&gt;
 &lt;h2&gt;第三部分：魔法揭秘&lt;/h2&gt;
 &lt;p&gt;现在，是时候书呆一点，一步一步地了解梯度下降的数学了。如果你不是真想知道魔法是如何起效的，那么可以跳过这一部分，直接看第4部分。&lt;/p&gt;
 &lt;p&gt;梯度下降是在训练时使用的迭代算法，通过梯度下降更新电影特征、用户偏好的权重和偏置，以做出更好的预测。&lt;/p&gt;
 &lt;p&gt;梯度下降的一般周期为：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;第 1 步 – 定义成本/损失函数以最小化和初始化权重&lt;/li&gt;
  &lt;li&gt;第 2 步 — 计算预测&lt;/li&gt;
  &lt;li&gt;第 3 步 – 计算梯度（相对于每个权重的成本变化）&lt;/li&gt;
  &lt;li&gt;第 4 步——在最小化成本的方向上更新每个权重“一点点地”（学习率）&lt;/li&gt;
  &lt;li&gt;第 5 步 — 重复第 2-4 步&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;你可以访问电子表格的“training”（训练）表，其中第11-16行是更新Tina Fey的第一项用户特征的过程。&lt;/p&gt;
 &lt;p&gt;由于数据集很小，我们将使用批量梯度下降。这意味着我们在训练时将使用整个数据集（在我们的例子中，一个用户的所有电影），而不是像随机梯度下降之类的算法一样每次迭代一个样本（在我们的例子中，一个用户的一部电影），当数据集较大时，随机梯度下降更快。&lt;/p&gt;
 &lt;h3&gt;定义最小化的代价函数&lt;/h3&gt;
 &lt;p&gt;我们将使用下面的公式，我们的目标是找到合适的潜因子（矩阵U、M）的值，以最小化SSE（平方误差之和）加上一个帮助模型提升概括性的L2权重惩罚项。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="529" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-7.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;下面是Excel中的代价函数计算。计算过程忽略了1/2系数，因为它们仅用于梯度下降以简化计算。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="396" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-8.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;L2正则化和过拟合&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;我们加入了权重惩罚（L2正则化或“岭回归”）以防止潜因子值过高。这确保模型没有“过拟合”（也就是记忆）训练数据，否则模型在未见的测试电影上表现不会好。&lt;/p&gt;
 &lt;p&gt;之前，我们没有使用L2正则化惩罚（系数为0）的情况下训练模型，50个epoch后，RMSE训练误差为0.12.&lt;/p&gt;
 &lt;p&gt;但是模型在测试数据上的表现如何呢？&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="402" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-9.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;如果我们将 L2 惩罚因子从 0.000 更改为 0.300，我们的模型应该可以更好地概括未见过的测试数据：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="426" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-10.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;计算预测&lt;/h3&gt;
 &lt;p&gt;我们将计算Tina的电影预测。我们将忽略《泰坦尼克号》，因为它在测试数据集中，不在训练数据集中。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="313" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-11.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;计算梯度&lt;/h3&gt;
 &lt;p&gt;目标是找到误差对应于将更新的权重的梯度（“坡度”）。&lt;/p&gt;
 &lt;p&gt;得出梯度之后，稍微将权重“移动一点点”，沿着梯度的反方向“下降”，在对每个权重进行这一操作后，下一epoch的代价应该会低一些。&lt;/p&gt;
 &lt;p&gt;“移动一点点”具体移动多少，取决于学习率。在得到梯度（3.3）之后，会用到学习率。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="238" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-12.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;梯度下降法则：将权重往梯度的反方向移动，以减少误差&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;第1步：计算Tina Fey的第一个潜因子的代价梯度（$u_1$）&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;1.1整理代价目标函数，取代价在Tina Fey的第一个潜因子（$u_1$）上的偏导数。&lt;/p&gt;
 &lt;p&gt;$$\frac{\partial J(cost)}{\partial u_1}=\frac{1}{2} \sum_{(i, j): r(i,)=1}(\hat{r}_{i, j}-r_{i, j})^2+\frac{1}{2} \lambda(\sum_i\left\|u_i\right\|^2+\sum_j\left\|m_j\right\|^2)$$&lt;/p&gt;
 &lt;p&gt;1.2整理预测评价函数，改写为用户潜因子的平方和加上电影潜因子的平方和&lt;/p&gt;
 &lt;p&gt;$$\frac{\partial J}{\partial u_1}=\frac{1}{2} \sum(((u_1 m_1)+(u_2 m_2)+(u_3 m_3)+u_{bias }+m_{bias})-r_{i, j})^2+ \frac{1}{2} \lambda \sum(u_1^2+u_2^2+u_3^2)+\frac{1}{2} \lambda \sum (m_1^2+m_2^2+m_3^2)$$&lt;/p&gt;
 &lt;p&gt;1.3将公式每部分中的$u_1$视为常数，取$u_1$在公式每部分的代价上的偏导数。&lt;/p&gt;
 &lt;p&gt;1.3.1（Part 1 of 3）应用“链式法则”以得到偏导数。链式法则意味着我们将((外层函数的导数)*内层函数)* (内部函数的导数)&lt;/p&gt;
 &lt;p&gt;外层函数的导数：&lt;/p&gt;
 &lt;p&gt;$$\begin{gathered}=\frac{1}{2} \sum(((u_1 m_1)+(u_2 m_2)+(u_3 m_3)+u_{bias}+m_{bias})-r_{i, j})^2 \leftarrow \text { power rule} \\=2 \times \frac{1}{2}(((u_1 m_1)+(u_2 m_2)+(u_3 m_3)+u_{bias}+m_{bias})-r_{i, j})^1 \\=\text { (predicted rating }-\text { actual rating }) \\=(\text { error })\end{gathered}$$&lt;/p&gt;
 &lt;p&gt;内层函数的导数：&lt;/p&gt;
 &lt;p&gt;$$\begin{gathered}=\frac{1}{2} \sum((u_1 m_1)+(u_2 m_2)+(u_3 m_3)+u_{bias}+m_{bias})-r_{i, j} \leftarrow \text {constant rule} \\ ((u_1 m_1)+(0)+(0)+0+0)-0 \\ =m_1 \\ 1.3.1=(\text{error} x m_1)\end{gathered}$$&lt;/p&gt;
 &lt;p&gt;1.3.1（Part 2 of 3）应用“幂法则”以得到偏导数。根据幂法则，指数为2，所以将指数降1，并乘上系数1/2. $u_2$和$u_3$视作常数，变为0.&lt;/p&gt;
 &lt;p&gt;$$\begin{gathered}=\frac{1}{2} \lambda \sum(u_1^2+u_2^2+u_3^2) \\=2 \times \frac{1}{2} \times \lambda(u_1^1+0+0) \\=\lambda \times u_1 \\1.3 .2= (\lambda \times u_1)\end{gathered}$$&lt;/p&gt;
 &lt;p&gt;1.3.3 (Part 3 of 3 ) 应用“常数法则”以得到偏导数。&lt;/p&gt;
 &lt;p&gt;$$\begin{gathered}=\frac{1}{2} \lambda \sum(m_1^2+m_2^2+m_3^2) \\=0\end{gathered}$$&lt;/p&gt;
 &lt;p&gt;由于$u_1$对这些项毫无影响，结果是0。&lt;/p&gt;
 &lt;p&gt;$$1.3.3 =0$$&lt;/p&gt;
 &lt;p&gt;1.4 结合1.3.1、1.3.2、1.3.3得到代价在$u_1$上的偏导数。&lt;/p&gt;
 &lt;p&gt;$$\begin{aligned} \quad \text { Part } 1+\text { Part } 2+\text { Part } 3 \\ \frac{\partial J}{\partial u_1} &amp;amp;=(\text { error } x m_1)+(\lambda \times u_1)+0 \\ &amp;amp;=(\text { error } x m_1)+(\lambda \times u_1)\end{aligned}$$&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;第2步：对训练集中Tina看过的每部电影，利用前面的公式计算梯度，接着计算Tina看过的所有电影的平均梯度。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="509" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-13.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;更新权重&lt;/h3&gt;
 &lt;p&gt;学习Tina的旧$u_1$，学习率($\alpha$)，以及上面计算的平均梯度，更新$u_1$。我们将使用的学习率为0.3。&lt;/p&gt;
 &lt;p&gt;Gradient descent formula:&lt;/p&gt;
 &lt;p&gt;$$New \ u_1= old \ u_1-\alpha (average \ gradient)$$&lt;/p&gt;
 &lt;p&gt;$$New \ u_1=(0.66)-0.3(1.92)$$&lt;/p&gt;
 &lt;p&gt;$$New \ u_1=(0.66)+0.58$$&lt;/p&gt;
 &lt;p&gt;$$New \ u_1=(0.08)$$&lt;/p&gt;
 &lt;p&gt;“training”（训练）表的X11-X16单元格对应上面的计算过程。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="251" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-14.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;你可以看到，电影特征和用户/电影偏置以类似的方式更新。&lt;/p&gt;
 &lt;p&gt;每一个训练epoch更新所有的电影/用户特征及偏置。&lt;/p&gt;
 &lt;h2&gt;第四部分：模型评估和可视化&lt;/h2&gt;
 &lt;p&gt;现在我们已经训练好了模型，让我们可视化电影的2个潜因子。&lt;/p&gt;
 &lt;p&gt;如果我们的模型更复杂，包括10、20、50+潜因子，我们可以使用一种称为“  &lt;a href="https://www.biaodianfu.com/pca.html"&gt;主成分分析（PCA）&lt;/a&gt;”的技术提取出最重要的特征，接着将其可视化。&lt;/p&gt;
 &lt;p&gt;相反，我们的模型仅仅包括3项特征，所以我们将可视化其中的2项特征，基于学习到的特征将每部电影绘制在图像上。绘制图像之后，我们可以解释每项特征“可能代表什么”。&lt;/p&gt;
 &lt;p&gt;从直觉出发，电影特征1可能解释为悲剧与喜剧，而电影特征3可能解释为男性向与女性向。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="735" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-15.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;这不是完美的解释，但还算一种合理的解释。《勇士》（warrior）一般归为剧情片，而不是喜剧片。不过其他电影基本符合以上解释。&lt;/p&gt;
 &lt;h2&gt;总结&lt;/h2&gt;
 &lt;p&gt;电影评价由一个电影向量和一个用户向量组成。在你评价了一些电影之后（显式或隐式），推荐系统将利用群体的智慧和你的评价预测你可能喜欢的其他电影。向量（或“潜因子”）的维度取决于数据集的大小，可以通过试错法确定。&lt;/p&gt;
 &lt;p&gt;我鼓励你实际操作下电子表格，看看改变模型的超参数会带来什么改变。&lt;/p&gt;
 &lt;p&gt;参考链接：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://towardsdatascience.com/netflix-and-chill-building-a-recommendation-system-in-excel-c69b33c914f4"&gt;Netflix and Chill: Building a Recommendation System in Excel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;div&gt;
  &lt;h3&gt;相关文章:&lt;/h3&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/lda-latent-dirichlet-allocation.html" rel="bookmark" title="&amp;#20027;&amp;#39064;&amp;#27169;&amp;#22411;LDA(Latent Dirichlet Allocation)&amp;#21021;&amp;#25506;"&gt;主题模型LDA(Latent Dirichlet Allocation)初探 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/learning-to-ranking.html" rel="bookmark" title="&amp;#25490;&amp;#24207;&amp;#20248;&amp;#21270;&amp;#31639;&amp;#27861;Learning to Ranking"&gt;排序优化算法Learning to Ranking &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/matrix-factorization.html" rel="bookmark" title="&amp;#25512;&amp;#33616;&amp;#31639;&amp;#27861;&amp;#20043;&amp;#30697;&amp;#38453;&amp;#20998;&amp;#35299;"&gt;推荐算法之矩阵分解 &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>器→工具 工具软件 数据 术→技巧 机器学习</category>
      <guid isPermaLink="true">https://itindex.net/detail/62488-excel-%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F</guid>
      <pubDate>Sun, 13 Nov 2022 23:06:06 CST</pubDate>
    </item>
    <item>
      <title>探索人机深度融合的高可用性人工智能应用</title>
      <link>https://itindex.net/detail/62471-%E6%B7%B1%E5%BA%A6-%E9%AB%98%E5%8F%AF%E7%94%A8%E6%80%A7-%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD</link>
      <description>&lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221025/3124cb84-010f-4d51-be3c-00dafe4546ed.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;目前，人工智能技术在世界范围内热度极高，但却出现了“雷声大、雨点小”的现象。一方面，随着近年来深度学习技术的不断发展，计算能力的不断提高，更深更复杂网络的普及使用，加上深度学习端到端的特性，看起来好像人工智能就是端到端的标注，不断地做数据清洗，增加标注数据，加深模型参数，就可以实现计算机像人类一样工作。另一方面，人工智能在实际应用场景落地时经常失败，常听到有“只见人工，不见智能”，“有多少人工就有多少智能”的吐槽。因此，目前许多人工智能技术的实现现阶段还不能脱离人工经验。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;人工智能落地需要人工智慧，这里核心在于结合场景和算法特点做合理的设计&lt;/strong&gt;，而非关注更多标准化的标注或者设计更精深的算法网络。达观是ToB的自然语言处理（NLP）公司，主要做办公文档自动处理。近年来在金融、政务、制造业等行业成功落地了非常多的NLP项目。&lt;/p&gt;



 &lt;p&gt;NLP也被誉为人工智能皇冠上的明珠，AI落地特别是NLP落地尤其不容易，通过机器处理办公文档远比从一堆图片中找出有猫的图片要复杂得多。因为让机器处理办公文档，往往存在缺少大量的训练语料情况，不同行业间需要处理的具体问题千差万别，人工都需要专业培训甚至几年工作经验才能处理妥当。本文主要结合达观的实践落地经验，探讨在具体NLP项目落地时，计算机“智能”需要哪些必不可少的“人工”。&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;“人工”化繁为简，拆解复杂问题&lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;  &lt;strong&gt;人工智能难以落地的主要原因之一是要处理的问题过于复杂&lt;/strong&gt;，如果只靠算法模型的自学习，很难学到对应知识，从而作出正确决策，就像用大学生的题目考小学生，不可能考得好。但如果我们能  &lt;strong&gt;人工对负责问题进行拆解，分解成多个简单问题，那每个简单的问题可能通过模型就能解决&lt;/strong&gt;。但如何拆解？拆解成什么程度模型是可以处理的呢？达观的经验是：当面对一个NLP问题，人类看完后立刻就能反应出结果的，这样的问题模型就是我们定义的“简单问题”，是机器可以解决的。下面以合同文档抽取的场景为例帮助大家理解。&lt;/p&gt;



 &lt;p&gt;假设我们需要构建模型从PDF格式的合同中抽取出甲方、乙方、违约条款等字段信息，看看机器是怎么一步步进行拆解的：&lt;/p&gt;



 &lt;p&gt;首先看机器的输入数据。PDF格式内部只是规定了每个字符或者线条应该在屏幕上什么位置，这些元素本身没有任何语义上的信息，在计算机看来这份文档其实只有字符以及其位置等简单信息，并没有人看渲染好的PDF文件的对齐、大小、重要性等更多信息。如果通过端到端的方式，把文字以及坐标一起输入到模型，让模型自己学习文档结构，理论上可以抽取出需要的字段。这种方式乍听之下可以一试，但实际效果是非常差的。因为让人看到一堆字和坐标，希望判断出抽取的字段，那本身就是非常复杂的事情，所以我们还需要进一步拆解。&lt;/p&gt;



 &lt;img alt="" height="482" src="https://www.52nlp.cn/wp-content/uploads/2022/10/&amp;#30011;&amp;#26495;-7-&amp;#25335;&amp;#36125;-3-1024x482.jpg" width="1024"&gt;&lt;/img&gt;



 &lt;p&gt;文档解析模型负责解析PDF协议，并且通过一定算法将文档结构化，也就是转成章节、表格、段落等文字流，再输入到字段抽取的模型。这两个模型是否足够简单并能落地呢？&lt;/p&gt;



 &lt;p&gt;大部分文档下，哪个是文字块，哪个是表格，哪个是图片，人是可以瞬间判断出来的。而文字块拆成章节、标题、段落，尤其是有些文档段落开始并没有明显空格，那人还是需要仔细看，有时候还要分析上下文才能分析出来。所以我们将文档解析继续拆解成元素识别模型和段落识别模型。&lt;/p&gt;



 &lt;img alt="" height="418" src="https://www.52nlp.cn/wp-content/uploads/2022/10/&amp;#30011;&amp;#26495;-7-&amp;#25335;&amp;#36125;-2-1024x418.jpg" width="1024"&gt;&lt;/img&gt;



 &lt;p&gt;对于字段抽取，有些字段比较简单，比如甲方、乙方，人眼就能看出结果，这些字段直接通过模型抽取问题不大；有些字段稍微复杂一些，比如合同总金额有时候是在文本中的，有时候是在表格里面的，人在看的时候也需要反应一下才能得到信息，所以可以对字段抽取再进行拆解。表格里面需要专门的表格抽取模型，如果是无线表格，人在看的时候往往还需要将虚线进行对应，所以也可以拆出无线表格识别的模型。文本抽取中，有些字段是长文本。比如违约条款，人在找的时候往往是通过前后文找到抽取的开始和结束，而短字段则更关注抽取本身以及上下文的内容。通过对每个步骤的复杂度进行分析，可以进一步拆解为下面结构。&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221025/14b59b65-86bd-4302-a812-5051f3faa7d8.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;这就是文档抽取常见的模型，但在实际使用中，根据具体数据情况以及分析字段的特点，可能还会再进行拆解。比如某些字段可能是在固定的某些章节或者段落中，用全局的文本进行训练和预测有很大的干扰，那我们就可以再增加一个字段章节预测的模型，定位该字段所在章节。再比如租房合同抽取的字段的文本是比较简单的选择性文本，对于模型来说也有一定困难。在销售合同文本中常常出现：&lt;/p&gt;



 &lt;p&gt;如果需要退货，采用B进行退货退款：&lt;/p&gt;



 &lt;ol&gt;  &lt;li&gt;不能退货&lt;/li&gt;  &lt;li&gt;可以退货，收取20%赔偿金&lt;/li&gt;  &lt;li&gt;可以退货，收取50%赔偿金&lt;/li&gt;&lt;/ol&gt;



 &lt;p&gt;这样的文本则需要拆成2个模型，一个是抽取选择项的模型，另一个是抽取选择列表的模型。&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;“人工”模型选择与优化&lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;  &lt;strong&gt;模型选择也是需要“人工”经验的&lt;/strong&gt;，  &lt;strong&gt;需要结合标注数据规模、数据特点、模型难度等进行选择和处理。&lt;/strong&gt;比如前面提到的章节预测的模型，如果章节标题特征比较明显，则可以直接通过关键词或者机器学习模型来进行分类处理，如果写法不太规范，需要通过章节标题和章节内容进行判断，则可能考虑基于bert的深度学习算法。就我们达观的经验而言，不同模型，如果使用完全相同的数据，调优后效果差距在5%以内，如果场景能比较好的使用上预训练模型，比如bert，那效果能提升10%-15%。&lt;/p&gt;



 &lt;p&gt;选定模型之后，也可以通过增加一些特征从而进一步降低模型的难度，提高准确度。在垂直领域文档处理上，业务词典是常用的方法。业务词典包括了专有名词，也包括了字段的重要关键信息的特征。比如我们要抽取合同的乙方，对于公司采购而言，很多都是有供应商库的，或者说可以获得之前与他们签合同的乙方的名称。这个名称构成的词典可能不全，所以不能只靠这个来匹配，但将这个“乙方专有名词”输入模型作为参考特征，是非常有用的。字段的重要关键信息的特征，指的是抽取的这个字段非常关键的上下文。比如抽取“甲方”这个字段，虽然话术可以有多种，比如甲方是xxx，甲方：xxx，甲方是本次的承办单位xxxx等等，但基本都会带“甲方”几个关键字，所以如果把这些专有名词也加入模型，准确度往往会有不小的提升。下面这个是重要词（专有名词或者业务词）使用的例子。&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221025/2949fd99-9642-4e6c-befa-3fa5a60e6b44.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;假设“委员”和“委员会”是重要词。需要对“美国联邦通信委员会最近正式批准苹果展开5G通信试验”的每个字生成词向量。这里的方法是通过2-gram，3-gram，4-gram和5-gram对每个字进行编码，编成8个位，每种gram各2个位表示上文是否是重要词和下文是否是重要词。以“委”字为例编码方式为：&lt;/p&gt;



 &lt;ul&gt;  &lt;li&gt;2-gram，就是“信委”和“委员”，“信委” 不是核心词，而“委员”是核心词，所以编码为“01”&lt;/li&gt;  &lt;li&gt;3-gram，就是“通信委”和“委员会”，“通信委” 不是核心词，而“委员会”是核心词，所以编码为“01”&lt;/li&gt;  &lt;li&gt;4-gram，就是“邦通信委”和“委员会最”都不是核心词，所以编码为“00”&lt;/li&gt;  &lt;li&gt;5-gram，就是“联邦通信委”和“委员会最近”都不是核心词，所以编码为“00”&lt;/li&gt;&lt;/ul&gt;



 &lt;p&gt;其他行业知识也可以用类似的方式生成字向量。把所有的行业向量和原始的字向量进行拼接，作为模型的输入，这样模型就能直接获得行业经验，从而有更好的效果。&lt;/p&gt;



 &lt;p&gt;&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;“人工”构建知识图谱&lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;  &lt;strong&gt;有些文本问题有很强的业务性，难以进行拆解，或者业务逻辑太复杂，很难让机器学习到对应的知识。&lt;/strong&gt;清华大学人工智能研究院院长张钹院士在一次演讲中提到“人的智能没法通过单纯的大数据学习把它学出来，那怎么办？很简单，加上知识，让它有推理的能力，做决策的能力，这样就能解决突发事件。”  &lt;strong&gt;达观在落地实践中就是通过知识图谱来解决这种复杂的问题。&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;知识图谱的概念由 Google 在 2012 年正式提出，是一种语义网络知识库，将现有知识的以结构化多关系图（Multi-Relational Graph）的形式进行储存、使用、展示形成。通过将多个实体关系三元组进行融合，形成包含多个不同的实体节点和多种类别的关系边的多关系图，即知识图谱。知识图谱落地也有很多挑战，构建和维护知识图谱的工作量是非常大的，很多项目因为构建过程太过复杂而最终失败。需要合理设计和运用知识图谱，也需要“人工”经验。达观通过知识图谱辅助智能制造有很多成功的落地案例，下面结合实际应用场景，谈下里面的一些经验。&lt;/p&gt;



 &lt;p&gt;生产制造过程中，有很多时候会遇到一些故障，比如手机发热，螺丝拧不上等问题，不快速解决会影响生产流程。之前遇到这类问题只能通过咨询经验丰富的“专家”，但总会存在专家找不到或者专家不一定有空的情况。我们希望通过NLP和知识图谱技术可以解决这个问题。&lt;/p&gt;



 &lt;p&gt;达观通过对里面的数据进行研究发现，要找到这些问题的答案经常要涉及好多文件，比如产品说明书，故障手册等。有些问题容易获得答案，但有些问题可能需要通过一些复杂的推理才能获得答案，甚至不一定能找到答案。面对这个问题，我们设计了制造业失效图谱。&lt;/p&gt;



 &lt;p&gt;为了解决专家录入进行构建的成本过高的问题，一方面我们设计的失效图谱schema只和失效本身相关，其他生成过程中的知识并不纳入产品范围之类，从而减少生成图谱的工作量。另一方面，我们在  &lt;strong&gt;图谱构建的时候，以人工结合智能&lt;/strong&gt;。从相关的文档，比如产品说明书，故障维修手册，失效分析文档等内容中提取相关属性数据，经过人工审核，再录入到图谱中。这种人机结合的方式生成图谱相比于纯人工生成图谱可以大幅减少工作量。图谱数据的抽取主要采用基于pipeline抽取和联合抽取的方法。&lt;/p&gt;



 &lt;p&gt;pipeline抽取，是用NER技术先抽取出实体和属性后，再通过分类方法对实体两两进行分类判断。这种方法的优点是灵活性高，不同类型的实体可以用不同的模型进行抽取，关系抽取的分类算法也可以结合实际数据进行优化和调整，缺点在于可能产生错误传播，实体错误后面的关系肯定是错误的，以及忽略了实体属性抽取和关系抽取内部的可能联系。&lt;/p&gt;



 &lt;p&gt;基于联合抽取的方法是同时抽取实体、属性、关系。针对实体抽取出的实体对，在当前句子对应的依存句法树中找到能够覆盖该实体对的最小依存句法树， 并基于 TreeLSTM 生成该子树对应的向量表示，最后，根据子树根节点对应的 TreeLSTM 向量进行关系分类。&lt;/p&gt;



 &lt;p&gt;一些知识可以通过抽取已有的文档，但有些文档缺失或者抽取难度很高的，则由专家来进行人工录入，从而构造了一个针对失效的知识图谱。有了这个图谱，就形成了计算机的知识。&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221025/0f01399c-2f0f-4daf-a870-5d7351fab0de.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;失效图谱例&lt;/p&gt;



 &lt;p&gt;基于图谱赋予的知识，企业可以使用基于知识图谱的问答（KBQA）来解决生产中实际碰到的问题，我们叫“归因分析”。基于图谱的问答需要能理解各种query的真实意图，尤其是query可能输错，可能表述不规范，需要还能对应到图谱得到正确的答案。这里面也需要对问题进行拆解，分解成一个个可以解决的模型。&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221025/b9c55a01-5124-4364-b1c7-c56ec8808440.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;KBQA处理流程&lt;/p&gt;



 &lt;p&gt;一般来说，KBQA分为数据预处理，问句分析，知识检索，答案生成4个阶段。&lt;/p&gt;



 &lt;ol&gt;  &lt;li&gt;数据预处理，指的是query进行基础的NLP处理，包括了分词，格式转换，归一化，纠错等过程。这里面和传统搜索中的数据预处理比较不同的是，纠错往往可以结合图谱里面的各种名称进行纠错，并且可以保留多个纠错结果，在后面的过程中再结合其他信息判断是否需要纠错，或纠错成哪个结果。&lt;/li&gt;  &lt;li&gt;问句分析，核心是要对query进行意图识别，并且进行实体链接。意图识别指用户的query是关于什么的，比如是问解决办法还是问原因。实体链接就是将问句文本的某些字符串映射到知识库中对应的实体上。实体链接是问答系统的核心问题之一，因为实体链接如果出错，后面的结果会非常不相关。这里面的难点在于用户query的名称和图谱中实体的名称并不是完全一致。所以我们也会加上模糊搜索以及同义词等方式来解决这个问题。&lt;/li&gt;  &lt;li&gt;知识检索，需要从图谱中选出符合该query相关的子图，并且对其排序。由于问题可能需要图谱通过多跳获得答案，所以这个步骤里面返回的实体节点可能有多个。&lt;/li&gt;  &lt;li&gt;答案生成，一方面是根据返回的结果找出最符合的一个，并且根据问句以及图谱的信息，通过NLG的技术生成可能的文字答案。&lt;/li&gt;&lt;/ol&gt;



 &lt;h1&gt;  &lt;strong&gt;”人工”进行场景选择和产品形态设计&lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;针对人工智能产品或解决方案，一般大家都在讨论技术如何提升，效果如何优化。以达观在过去几年落地的很多AI项目来说，  &lt;strong&gt;场景选择和产品形态的设计其实是落地非常关键的环节&lt;/strong&gt;。从落地的角度，本质需求是希望可以更快地高质量完成预计的工作，并不是需要一个多少准确率的模型。而且这里的高质量，在办公文档处理上的落地需求往往是100%准确。而目前的算法基本都不能达到100%准确，而且算法本身并不知道错在哪里，这也是AI落地碰到的最大挑战。因为当需要所有数据进行复核，“快速”这个需求就会大打折扣。如何“快速”审核就是需要在场景选择以及产品形态上做很多工作。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;01&lt;/strong&gt;  &lt;strong&gt;比对数据&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;用第三方数据或者有规则进行校验，就能快速发现AI错误之处。比如电子合同和图片合同进行文档比对的场景，ocr的错误通过比对，可以快速的找到出现ocr错误的地方，人工可以快速进行查看。&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221025/7afe0cec-68e2-4b0d-a64c-351655af39e9.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;文档比对产品kh&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;02&lt;/strong&gt;  &lt;strong&gt;业务关系&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;文档中识别的元素有些是有业务关系的，可以通过字段的关系来验证识别是否正确。比如下图总和的值应该是上面列表中数值计算后的结果。如果识别出来的结果总和公式不正确，那很可能是中间哪个元素识别出现了问题，如果识别出来的结果总和公式正确，那基本识别本身也是正确的。&lt;/p&gt;



 &lt;img alt="" height="699" src="https://www.52nlp.cn/wp-content/uploads/2022/10/640-1024x699.jpg" width="1024"&gt;&lt;/img&gt;



 &lt;p&gt;财务文档中的勾稽关系&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;03&lt;/strong&gt;  &lt;strong&gt;高效审核&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;人工审核过程的产品交互是非常重要的，需要对比较耗费时间的环节结合具体业务场景的审核过程进行合理交互设计。审核过程主要是“找到”和“修订”两个动作，达观通过对抽取结果进行高亮，点击字段跳转等功能帮助审核人员快速“找到”抽取结果以及上下文，通过划选和快捷键等功能加速人工“修订”的时间。&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221025/8cbcff10-5342-4f71-9710-21996999eac0.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;人工审核产品交互&lt;/p&gt;



 &lt;p&gt;人工智能落地是一个挺有挑战性的工作，既需要攻克技术难度，不断提升算法精度，也需要了解业务，了解场景，才能选择合适的场景，构建合理的算法流程，设计方便的产品交互，把这些“人工”的价值发挥出来，  &lt;strong&gt;以人机协同的方式促进算力和模型的提高，才能真正实现“人工智能”。&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>自然语言处理 深度学习</category>
      <guid isPermaLink="true">https://itindex.net/detail/62471-%E6%B7%B1%E5%BA%A6-%E9%AB%98%E5%8F%AF%E7%94%A8%E6%80%A7-%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD</guid>
      <pubDate>Wed, 26 Oct 2022 18:49:35 CST</pubDate>
    </item>
    <item>
      <title>面向推荐的汽车知识图谱构建</title>
      <link>https://itindex.net/detail/62357-%E6%B1%BD%E8%BD%A6-%E7%9F%A5%E8%AF%86</link>
      <description>&lt;h3&gt;一、背景&lt;/h3&gt; &lt;h4&gt;1、引言&lt;/h4&gt; &lt;p&gt;知识图谱的概念，最早由 Google 在2012 年提出， 旨在实现更智能的搜索引擎，并在2013年之后开始在学术界和工业级普及。目前，随着人工智能技术的高速发展，知识图谱已广泛应用于搜索、推荐、广告、风控、智能调度、语音识别、机器人等多个领域。&lt;/p&gt; &lt;h4&gt;2、发展现状&lt;/h4&gt; &lt;p&gt;知识图谱作为人工智能的核心技术驱动力，能缓解深度学习依赖海量训练数据、大规模算力的问题，它能够广泛适配不同的下游任务，且具有良好的解释性，因此，全球大型互联网公司都在积极部署本企业的知识图谱。  &lt;br /&gt;例如2013年Facebook发布Open Graph，应用于社交网络智能搜索；2014年百度推出的知识图谱，主要应用于搜索、助理、及toB商业场景；2015年阿里推出的商品知识图谱，在前端导购、平台治理和智能问答等业务上起到关键作用；腾讯于17年推出的腾讯云知识图谱，有效助力于金融搜索、实体风险预测等场景；美团于2018年推出的美团大脑知识图谱，已经在智能搜索推荐、智能商户运营等多个业务中落地。  &lt;br /&gt;  &lt;img alt="image.png" src="https://segmentfault.com/img/bVc1AWr" title="image.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h4&gt;3、目标及收益&lt;/h4&gt; &lt;p&gt;目前领域图谱主要集中在电商、医疗、金融等商业领域，而关于汽车知识的语义网络及知识图谱构建缺少系统性的指导方法。本文以汽车领域知识为例，围绕车系、车型、经销商、厂商、品牌等实体及相互关系，提供一种从零搭建领域图谱的思路，并对搭建知识图谱中的步骤及方法进行了详细说明，以及介绍了基于本图谱的几个典型落地应用。  &lt;br /&gt;其中，数据源采用汽车之家网站，汽车之家是由导购、资讯、评测、口碑等多个板块组成的汽车服务类平台，在看、买、用等维度积累了大量的汽车数据，通过构建知识图谱把以汽车为核心的内容进行组织和挖掘，提供丰富的知识信息，结构化精准刻画兴趣，支持推荐用户冷启、召回、排序、展示等多个维度，给业务提升带来效果。&lt;/p&gt; &lt;h3&gt;二、图谱构建&lt;/h3&gt; &lt;h4&gt;1、构建的挑战&lt;/h4&gt; &lt;p&gt;知识图谱是真实世界的语义表示，，其基本组成单位是【实体-关系-实体】，【实体-属性-属性值】的三元组（Triplet），实体之间通过关系相互联结，从而构成语义网络。图谱构建中会面临较大的挑战，但构建之后，可在数据分析、推荐计算、可解释性等多个场景展现出丰富的应用价值。  &lt;br /&gt;构建挑战：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;schema难定义：目前尚无统一成熟的本体构建流程，且特定领域本体定义通常需专家参与；&lt;/li&gt;  &lt;li&gt;数据类型异构：通常情况下，一个知识图谱构建中面对的数据源不会是单一类型，包含结构化、半结构化，及非结构化数据，面对结构各异的数据，知识转模及挖掘的难度较高；&lt;/li&gt;  &lt;li&gt;依赖专业知识：领域知识图谱通常依赖较强的专业知识，例如车型对应的维修方法，涉及机械、电工、材料、力学等多个领域知识，且此类关系对于准确度的要求较高，需要保证知识足够正确，因此也需要较好的专家和算法相结合的方式来进行高效的图谱构建；&lt;/li&gt;  &lt;li&gt;数据质量无保证：挖掘或抽取信息需要知识融合或人工校验，才能作为知识助力下游应用。&lt;/li&gt;  &lt;li&gt;收益：&lt;/li&gt;  &lt;li&gt;知识图谱统一知识表示：通过整合多源异构数据，形成统一视图；&lt;/li&gt;  &lt;li&gt;语义信息丰富：通过关系推理可以发现新关系边，获得更丰富的语义信息；&lt;/li&gt;  &lt;li&gt;可解释性强：显式的推理路径对比深度学习结果具有更强的解释性；&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;高质量且能不断积累：根据业务场景设计合理的知识存储方案，实现知识更新和累积。&lt;/p&gt;   &lt;h4&gt;2、图谱架构设计&lt;/h4&gt;   &lt;p&gt;技术架构主要分为构建层、存储层及应用层三大层，架构图如下：&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;构建层：包括schema定义，结构化数据转模，非结构化数据挖掘，以及知识融合；&lt;/li&gt;  &lt;li&gt;存储层：包括知识的存储和索引，知识更新，元数据管理，以及支持基本的知识查询；&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;服务层：包括智能推理、结构化查询等业务相关的下游应用层。    &lt;br /&gt;    &lt;img alt="image.png" src="https://segmentfault.com/img/bVc1AWx" title="image.png"&gt;&lt;/img&gt;&lt;/p&gt;   &lt;h4&gt;3、具体构建步骤及流程&lt;/h4&gt;   &lt;p&gt;依据架构图，具体构建流程可分为四步：本体设计、知识获取、知识入库，以及应用服务设计及使用。    &lt;br /&gt;3.1 本体构建    &lt;br /&gt;本体（Ontology）是公认的概念集合，本体的构建是指依据本体的定义，构建出知识图谱的本体结构和知识框架。    &lt;br /&gt;基于本体构建图谱的原因主要有以下几点：&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;明确专业术语、关系及其领域公理，当一条数据必须满足Schema预先定义好的实体对象和类型后，才允许被更新到知识图谱中。&lt;/li&gt;  &lt;li&gt;将领域知识与操作性知识分离，通过Schema可以宏观了解图谱架构及相关定义，无须再从三元组中归纳整理。&lt;/li&gt;  &lt;li&gt;实现一定程度的领域知识复用。在构建本体之前，可以先调研是否有相关本体已经被构建出来了，这样可以基于已有本体进行改进和扩展，达到事半功倍的效果。&lt;/li&gt;  &lt;li&gt;基于本体的定义，可以避免图谱与应用脱节，或者修改图谱schema比重新构建成本还要高的情况。例如将“宝马x3”、“2022款宝马x3”都作为汽车类实体来储存，在应用时都可能造成实例关系混乱、可用性差的问题，这种情况可以在设本体计阶段，通过将“汽车类实体”进行“车系”、“车型”子类细分的方法来避免。   &lt;br /&gt;按照知识的覆盖面来看，知识图谱可以划分为通用知识图谱和领域知识图谱，目前通用知识图谱已有较多案例，例如Google的Knowledge Graph、微软的Satori和Probase等，领域图谱则为金融、电商等具体行业图谱。通用图谱更注重广度，强调融合更多的实体数量，但对精确度的要求不高，很难借助本体库对公理、规则及约束条件进行推理和使用；而领域图谱的知识覆盖范围较小，但知识深度更深，往往是在某一专业领域上的构建。   &lt;br /&gt;考虑对准确率的要求，领域本体构建多倾向于手工构建的方式，例如代表性的七步法、IDEF5方法等[1]，该类方法的核心思想是，基于已有结构化数据，进行本体分析，将符合应用目的和范围的本体进行归纳及构建，再对本体进行优化和验证，从而获取初版本体定义。若想获取更大范畴的领域本体，则可以从非结构化语料中补充，考虑手工构建过程较大，本文以汽车领域为例，提供一种半自动本体构建的方式，详细步骤如下：&lt;/li&gt;  &lt;li&gt;首先收集大量汽车非结构化语料（例如车系咨询、新车导购文章等），作为初始个体概念集，利用统计方法或无监督模型（TF-IDF、BERT等）获取字特征和词特征；&lt;/li&gt;  &lt;li&gt;其次利用BIRCH聚类算法对概念间层次划分，初步构建起概念间层级关系，并对聚类结果进行人工概念校验和归纳，获取本体的等价、上下位概念；&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;最后使用卷积神经网络结合远程监督的方法，抽取本体属性的实体关系，并辅以人工识别本体中的类及属性的概念，构建起汽车领域本体。    &lt;br /&gt;上述方法可有效利用BERT等深度学习的技术，更好地捕捉语料间的内部关系，使用聚类分层次对本体各模块进行构建，辅以人工干预，能够快速、准确的完成初步本体构建。下图为半自动化本体构建示意图：    &lt;br /&gt;    &lt;img alt="image.png" src="https://segmentfault.com/img/bVc1AWJ" title="image.png"&gt;&lt;/img&gt;    &lt;br /&gt;利用Protégé本体构建工具[2]，可以进行本体概念类、关系、属性和实例的构建，下图为本体构建可视化示例图：    &lt;br /&gt;    &lt;img alt="image.png" src="https://segmentfault.com/img/bVc1AWQ" title="image.png"&gt;&lt;/img&gt;    &lt;br /&gt;本文将汽车领域的顶层本体概念划分为三类，实体、事件及标签体系：    &lt;br /&gt;1）实体类代表特定意义的概念类实体，包括词汇实体和汽车类实体，其中汽车类实体又包括组织机构和汽车概念等子实体类型；    &lt;br /&gt;2）标签体系代表各个维度的标签体系，包括内容分类、概念标签、兴趣标签等以物料维度刻画的标签；    &lt;br /&gt;3）事件类代表一个或多个角色的客观事实，不同类型事件间具有演变关系。    &lt;br /&gt;Protégé可以导出不同类型的Schema配置文件，其中owl.xml结构配置文件如下图所示。该配置文件可直接在MYSQL、JanusGraph中加载使用，实现自动化的创建Schema。    &lt;br /&gt;    &lt;img alt="image.png" src="https://segmentfault.com/img/bVc1AW4" title="image.png"&gt;&lt;/img&gt;&lt;/p&gt;   &lt;h4&gt;3.2 知识获取&lt;/h4&gt;   &lt;p&gt;知识图谱的数据来源通常包括三类数据结构，分别为结构化数据、半结构化数据、非结构化数据。 面向不同类型的数据源，知识抽取涉及的关键技术和需要解决的技术难点有所不同。&lt;/p&gt;   &lt;h5&gt;3.2.1 结构化知识转模&lt;/h5&gt;   &lt;p&gt;结构化数据是图谱最直接的知识来源，基本通过初步转换就可以使用，相较其他类型数据成本最低，所以一般图谱数据优先考虑结构化数据。结构化数据可能涉及多个数据库来源，通常需要使用ETL方法转模，ETL即Extract（抽取）、Transform（转换）、Load（装载），抽取是将数据从各种原始的业务系统中读取出来，这是所有工作的前提；转换是按照预先设计好的规则将抽取的数据进行转换，使本来异构的数据格式可以统一起来；装载是将转换完的数据按计划增量或全部导入到数据仓库中。    &lt;br /&gt;通过上述ETL流程可将不同源数据落到中间表，从而方便后续的知识入库。下图为车系实体属性、关系表示例图：    &lt;br /&gt;    &lt;img alt="image.png" src="https://segmentfault.com/img/bVc1AXa" title="image.png"&gt;&lt;/img&gt;    &lt;br /&gt;车系与品牌关系表：    &lt;br /&gt;    &lt;img alt="image.png" src="https://segmentfault.com/img/bVc1AXb" title="image.png"&gt;&lt;/img&gt;&lt;/p&gt;   &lt;h5&gt;3.2.2 非结构化知识抽取-三元组抽取&lt;/h5&gt;   &lt;p&gt;除了结构化数据，非结构化数据中也存在着海量的知识（三元组）信息。一般来说企业的非结构化数据量要远大于结构化数据，挖掘非结构化知识能够极大拓展和丰富知识图谱。    &lt;br /&gt;三元组抽取算法的挑战    &lt;br /&gt;问题1：单个领域内，⽂档内容和格式多样，需要⼤量的标注数据，成本⾼    &lt;br /&gt;问题2：领域之间迁移的效果不够好，跨领域的可规模化拓展的代价⼤    &lt;br /&gt;模型基本都是针对特定⾏业特定场景，换⼀个场景，效果会出现明显下降。    &lt;br /&gt;解决思路，Pre-train + Finetune的范式，预训练：重量级底座让模型“⻅多识⼴”充分利⽤⼤规模多⾏业的⽆标⽂档，训练⼀个统⼀的预训练底座，增强模型对各类⽂档的表示和理解能⼒。    &lt;br /&gt;微调：轻量级⽂档结构化算法。在预训练基础上，构建轻量级的⾯向⽂档结构化的算法，降低    &lt;br /&gt;标注成本。    &lt;br /&gt;针对⽂档的预训练⽅法    &lt;br /&gt;现有关于⽂档的预训练模型，如果文本较短的类型，Bert可以完全编码整篇⽂档；⽽我们实际的⽂档通常⽐较⻓，需要抽取的属性值有很多是超过1024个字的，Bert进⾏编码会造成属性值截断。    &lt;br /&gt;针对长文本预训练方法优点和不足    &lt;br /&gt;Sparse Attention的⽅法通过优化Self-Attention，将O(n2)的计算优化⾄O(n)，⼤⼤提⾼了输⼊⽂本⻓度。虽然普通模型的⽂本⻓度从512提升到4096，但是依旧不能完全解决截断⽂    &lt;br /&gt;本的碎⽚化问题。百度提出了ERNIE-DOC[3]使用了Recurrence Transformer方法，理论上可以建模⽆限⻓的⽂本。由于建模要输⼊所有的⽂本信息，耗时⾮常⾼。    &lt;br /&gt;上述两种基于⻓⽂本的预训练⽅法，都没有考虑⽂档特性，如空间(Spartial)、视觉(Visual)等信息。并且基于⽂本设计的PretrainTask，整体是针对纯⽂本进⾏的设计，⽽没有针对⽂档的逻辑结构设计。    &lt;br /&gt;针对上述不足这里介绍一种⻓⽂档预训练模型DocBert[4],DocBert模型设计：    &lt;br /&gt;使⽤⼤规模（百万级）⽆标注⽂档数据进⾏预训练，基于⽂档的⽂本语义(Text)、版⾯信息    &lt;br /&gt;(Layout)、视觉特征(Visual)构建⾃监督学习任务，使模型更好地理解⽂档语义和结构信息。    &lt;br /&gt;1.Layout-Aware MLM：在Mask语⾔模型中考虑⽂本的位置、字体⼤⼩信息，实现⽂档布局感知的语义理解。    &lt;br /&gt;2.Text-Image Alignment：融合⽂档视觉特征，重建图像中被Mask的⽂字，帮助模型学习⽂本、版⾯、图像不同模态间的对⻬关系。    &lt;br /&gt;3.Title Permutation：以⾃监督的⽅式构建标题重建任务，增强模型对⽂档逻辑结构的理解能⼒。    &lt;br /&gt;4.Sparse Transformer Layers：⽤Sparse Attention的⽅法，增强模型对⻓⽂档的处理能⼒。    &lt;br /&gt;    &lt;img alt="image.png" src="https://segmentfault.com/img/bVc1AXh" title="image.png"&gt;&lt;/img&gt;&lt;/p&gt;   &lt;h5&gt;3.2.3 挖掘概念，兴趣词标签，关联到车系、实体&lt;/h5&gt;   &lt;p&gt;除了结构化和非结构化文本中获取三元组，汽车之家还挖掘物料所包含的分类、概念标签和兴趣关键词标签，并建立物料和车实体之间的关联，为汽车知识图谱带来新的知识。下面从分类、概念标签、兴趣词标签来介绍汽车之家所做的内容理解部分工作以及思考。    &lt;br /&gt;分类体系作为内容刻画基础，对物料进行粗粒度的划分。建立的统一的内容体系更多的是基于人工定义的方式，通过AI模型进行划分。在分类方法上我们我们采用了主动学习，对于比较难分的数据进行标注，同时采用数据增强，对抗训练，以及关键词融合方式提高分类的效果。    &lt;br /&gt;    &lt;img alt="image.png" src="https://segmentfault.com/img/bVc1AXp" title="image.png"&gt;&lt;/img&gt;    &lt;br /&gt;概念标签粒度介于分类和兴趣词标签之间，比分类粒度更细，同时比兴趣词对于兴趣点刻画更加完整，我们建立了车视野、人视野、内容视野三个维度，丰富了标签维度，细化了标签粒度。丰富且具体的物料标签，更加方便搜索推荐基于标签的模型优化，且可用于标签外展起到吸引用户及二次引流等作用。概念标签的挖掘，结合在query等重要数据上采用机器挖掘方式，并对概括性进行分析，通过人工review，拿到概念标签集合，采用多标签模型分类。    &lt;br /&gt;兴趣词标签是最细粒度的标签，映射为用户兴趣，根据不同用户兴趣偏好进可以更好的进行行个性化推荐。关键词的挖掘采用多种兴趣词挖掘相结合的方式，包括Keybert提取关键子串，并结合TextRank、positionRank、singlerank、TopicRank、MultipartiteRank等+句法分析多种方法，产生兴趣词候选。    &lt;br /&gt;    &lt;img alt="image.png" src="https://segmentfault.com/img/bVc1AXs" title="image.png"&gt;&lt;/img&gt;    &lt;br /&gt;挖掘出来的词，相似度比较高，需要对同义词进行识别，需要提升人工的效率，因此我们也通过聚类进行自动化语义相似识别。用于聚类的特征有word2vec，bert embding等其他人工特征。然后使用聚类方法，最后经过人工矫正我们离线产生了一批高质量的关键词。    &lt;br /&gt;对于不同粒度的标签还是在物料层面的，我们需要把标签和车建立起关联，首先我们分别计算出标题\文章的所属标签，然后识别出标题\文章内的实体，得到若干标签-实体伪标签，最后根据大量的语料，共现概率高的标签就会标记为该实体的标签。通过以上三个任务，我们在获得了丰富且海量的标签。对车系、实体关联上这些标签，会极大丰富我们的汽车图谱，建立了媒体和用户的关注车标签。&lt;/p&gt;   &lt;h5&gt;3.2.4人效提升：&lt;/h5&gt;   &lt;p&gt;伴随着更大规模的训练样本，如何获得更好的模型质量，如何解决标注成本高，标注周期长成为亟待解决的问题。首先我们可以使用半监督学习，利用海量未标注数据进行预训练。然后采用主动学习方式，最大化标注数据的价值，迭代选择高信息量样本进行标注。最后可以利用远程监督，发挥已有知识的价值，发觉任务之间的相关性。例如在有了图谱和标题后，可以用远程监督的方法基于图谱构造NER训练数据。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt; &lt;h4&gt;3.3 知识入库&lt;/h4&gt; &lt;p&gt;知识图谱中的知识是通过RDF结构来进行表示的，其基本单元是事实。每个事实是一个三元组(S, P, O)，在实际系统中，按照存储方式的不同，知识图谱的存储可以分为基于RDF表结构的存储和基于属性图结构的存储。图库更多是采用属性图结构的存储，常见的存储系统有Neo4j、JanusGraph、OritentDB、InfoGrid等。  &lt;br /&gt;图数据库选择  &lt;br /&gt;通过 JanusGraph 与 Neo4J、ArangoDB、OrientDB 这几种主流图数据库的对比，我们最终选择JanusGraph 作为项目的图数据库，之所以选择 JanusGraph，主要有以下原因：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;基于 Apache 2 许可协议开放源码，开放性好。&lt;/li&gt;  &lt;li&gt;支持使用 Hadoop 框架进行全局图分析和批量图处理。&lt;/li&gt;  &lt;li&gt;支持很大的并发事务处理和图操作处理。通过添加机器横向扩展 JanusGraph 的事务 处理能力，可以在毫秒级别相应大图的复杂查询。&lt;/li&gt;  &lt;li&gt;原生支持 Apache TinkerPop 描述的当前流行的属性图数据模型。&lt;/li&gt;  &lt;li&gt;原生支持图遍历语言 Gremlin。&lt;/li&gt;  &lt;li&gt;下图是主流图数据库对比   &lt;br /&gt;   &lt;img alt="image.png" src="https://segmentfault.com/img/bVc1AXt" title="image.png"&gt;&lt;/img&gt;   &lt;br /&gt;   &lt;strong&gt;Janusgraph介绍&lt;/strong&gt;   &lt;br /&gt;JanusGraph[5]是一个图形数据库引擎。其本身专注于紧凑图序列化、丰富图数据建模、高效的查询执行。图库schema 构成可以用下面一个公式来表示：   &lt;br /&gt;janusgraph schema = vertex label + edge label + property keys   &lt;br /&gt;这里值得注意的是property key通常用于graph index。&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;为了更好的图查询性能janusgraph建立了索引，索引分为Graph Index，Vertex-centric Indexes。Graph Index包含组合索引(Composite Index)和混合索引(Mixed Index).  &lt;br /&gt;组合索引仅限相等查找。（组合索引不需要配置外部索引后端，通过主存储后端支持(当然也可以配置hbase，Cassandra，Berkeley))  &lt;br /&gt;举例：  &lt;br /&gt;mgmt.buildIndex(&amp;apos;byNameAndAgeComposite&amp;apos;, Vertex.class).addKey(name).addKey(age).buildCompositeIndex() #构建一个组合索引“name-age”  &lt;br /&gt;g.V().has(&amp;apos;age&amp;apos;, 30).has(&amp;apos;name&amp;apos;, &amp;apos;小明&amp;apos;)#查找 名字为小明年龄30的节点  &lt;br /&gt;混合索引需要ES作为后端索引以支持除相等以外的多条件查询（也支持相等查询，但相等查询，组合索引更快）。根据是否需要分词分为full-text search，和string search  &lt;br /&gt;JanusGraph数据存储模型  &lt;br /&gt;了解Janusgraph存储数据的方式，有助于我们更好的利用该图库。JanusGraph 以邻接列表格式存储图形，这意味着图形存储为顶点及其邻接列表的集合。 顶点的邻接列表包含顶点的所有入射边（和属性）。  &lt;br /&gt;  &lt;img alt="image.png" src="https://segmentfault.com/img/bVc1AXy" title="image.png"&gt;&lt;/img&gt;  &lt;br /&gt;JanusGraph 将每个邻接列表作为一行存储在底层存储后端中。 （64 位）顶点 ID（JanusGraph 唯一分配给每个顶点）是指向包含顶点邻接列表的行的键。 每个边和属性都存储为行中的一个单独的单元格，允许有效的插入和删除。 因此，特定存储后端中每行允许的最大单元数也是 JanusGraph 可以针对该后端支持的顶点的最大度数。  &lt;br /&gt;如果存储后端支持 key-order，则邻接表将按顶点 id 排序，JanusGraph 可以分配顶点 id，以便对图进行有效分区。 分配 id 使得经常共同访问的顶点具有绝对差异小的 id。&lt;/p&gt; &lt;h4&gt;3.4 图谱查询服务&lt;/h4&gt; &lt;p&gt;Janusgraph进行图搜索用的是gremlin语言，我们提供了统一的图谱查询服务，外部使用不用关心gremlin语言的具体实现，采用通用的接口进行查询。我们分为三个接口：条件搜索接口，以节点为中心向外查询，和节点间路径查询接口。下面是几个gremlin实现的例子：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;条件搜索：查询10万左右，销量最高的车：   &lt;br /&gt;g.V().has(&amp;apos;price&amp;apos;,gt(8)).has(&amp;apos;price&amp;apos;,lt(12)).order().by(&amp;apos;sales&amp;apos;,desc).valueMap().limit(1)   &lt;br /&gt;输出：   &lt;br /&gt;==&amp;gt;{name=[xuanyi], price=[10], sales=[45767]}   &lt;br /&gt;轩逸销量最高，为45767&lt;/li&gt;  &lt;li&gt;以节点为中心向外查询：查询以小明为中心，2度的节点   &lt;br /&gt;g.V(xiaoming).repeat(out()).times(2).valueMap()&lt;/li&gt;  &lt;li&gt;节点间路径查询：荐给小明推荐两篇文章，这两篇文章分别介绍的是卡罗拉和轩逸，查询小明 和 这两篇文章的路径：   &lt;br /&gt;g.V(xiaoming).repeat(out().simplePath()).until(or(has(&amp;quot;car&amp;quot;, &amp;apos;name&amp;apos;, &amp;apos;kaluola&amp;apos;),has(&amp;quot;car&amp;quot;, &amp;apos;name&amp;apos;, &amp;apos;xuanyi&amp;apos;))).path().by(&amp;quot;name&amp;quot;)   &lt;br /&gt;输出   &lt;br /&gt;==&amp;gt;path[xiaoming, around 10w, kaluola]   &lt;br /&gt;==&amp;gt;path[xiaoming, around 10w, xuanyi]   &lt;br /&gt;发现小明和这两篇文章之间有个节点“10万左右”&lt;/li&gt;&lt;/ul&gt; &lt;h3&gt;三、知识图谱在推荐的应用&lt;/h3&gt; &lt;p&gt;知识图谱中存在大量的非欧式数据，基于KG的推荐应用有效利用非欧式数据提升推荐系统准确度，进而让推荐系统能达到传统系统所无法达到的效果。基于KG的推荐可以分成以三类，基于KG表征技术（KGE）、基于路径的方法、图神经网络。本章将从KG在推荐系统中冷启、理由、排序三方面的应用和论文进行介绍。&lt;/p&gt; &lt;h4&gt;3.1 知识图谱在推荐冷启动的应用&lt;/h4&gt; &lt;p&gt;知识图谱能够从user-item交互中建模KG中隐藏的高阶关系,很好地解决了因用户调用有限数量的行为而导致的数据稀疏性，进而可以应用在解决冷启动问题。这一问题业界也有相关研究。  &lt;br /&gt;Sang 等[6]提出了一种双通道神经交互方法，称为知识图增强的残差递归神经协同过滤（KGNCF-RRN），该方法利用KG上下文的长期关系依赖性和用户项交互进行推荐。（1）对于KG上下文交互通道，提出了残差递归网络（RRN）来构造基于上下文的路径嵌入，将残差学习融入传统的递归神经网络（RNN）中，以有效地编码KG的长期关系依赖。然后将自关注网络应用于路径嵌入，以捕获各种用户交互行为的多义。（2） 对于用户项目交互通道，用户和项目嵌入被输入到新设计的二维交互图中。（3）最后，在双通道神经交互矩阵之上，使用卷积神经网络来学习用户和项目之间的复杂相关性。该方法能捕捉丰富的语义信息，还能捕捉用户与项目之间复杂的隐含关系，用于推荐。  &lt;br /&gt;Du Y等[7]提出了一种新的基于元学习框架的冷启问题解决方案MetaKG，包括collaborative-aware meta learner和knowledge-aware meta learner，捕捉用户的偏好和实体冷启动知识。collaborative-aware meta learner学习任务旨在聚合每个用户的偏好知识表示。相反，knowledge-aware meta learner学习任务要在全局泛化不同的用户偏好知识表示。在两个learner的指导下，MetaKG可以有效地捕捉到高阶的协作关系关系和语义表示，可以轻松适应冷启动场景。此外，作者还设计了一种自适应任务，可以自适应地选择KG信息进行学习，以防止模型被噪声信息干扰。MetaKG架构如下图所示。  &lt;br /&gt;  &lt;img alt="image.png" src="https://segmentfault.com/img/bVc1AXE" title="image.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h4&gt;3.2 知识图谱在推荐理由生成的应用&lt;/h4&gt; &lt;p&gt;推荐理由能提高推荐系统的可解释性，让用户理解生成推荐结果的计算过程，同时也可以解释item受欢迎的原因。用户通过推荐理由了解推荐结果的产生原理，可以增强用户对系统推荐结果的信心，并且在推荐失误的情况下对错误结果更加宽容。  &lt;br /&gt;最早可解释推荐是以模板为主，模板的好处是保证了可读性和高准确率。但是需要人工整理模板，并且泛华性不强，给人一种重复的感觉。后来发展不需要预设的free-form形式，并且加以知识图谱，以其中的一条路径作为解释，配合标注还有一些结合KG路径的生成式的方法，模型中选择的每个点或边都是一个推理过程，可以向用户展示。最近Chen Z [8]等人提出一种增量多任务学习框架ECR，可以实现推荐预测、解释生成和用户反馈集成之间的紧密协作。它由两大部分组成。第一部分，增量交叉知识建模，学习推荐任务和解释任务中转移的交叉知识，并说明如何使用交叉知识通过使用增量学习进行更新。第二部分，增量多任务预测，阐述如何基于交叉知识生成解释，以及如何根据交叉知识和用户反馈预测推荐分数。  &lt;br /&gt;  &lt;img alt="image.png" src="https://segmentfault.com/img/bVc1AXH" title="image.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h4&gt;3.3 知识图谱在推荐排序的应用&lt;/h4&gt; &lt;p&gt;KG可以通过给item用不同的属性进行链接，建立user-item之间interaction，将uesr-item graph和KG结合成一张大图，可以捕获item间的高阶联系。传统的推荐方法是将问题建模为一个监督学习任务，这种方式会忽略item之间的内在联系(例如凯美瑞和雅阁的竞品关系)，并且无法从user行为中获取协同信号。下面介绍两篇KG应用在推荐排序的论文。  &lt;br /&gt;Wang[9]等人设计了KGAT算法，首先利用GNN迭代对embedding进行传播、更新，从而能够在快速捕捉高阶联系；其次，在aggregation时使用attention机制，传播过程中学习到每个neighbor的weight，反应高阶联系的重要程度；最后，通过N阶传播更新得到user-item的N个隐式表示，不同layer表示不同阶数的连接信息。KGAT可以捕捉更丰富、不特定的高阶联系。  &lt;br /&gt;  &lt;img alt="image.png" src="https://segmentfault.com/img/bVc1AXM" title="image.png"&gt;&lt;/img&gt;  &lt;br /&gt;Zhang[20]等人提出RippleNet模型，其关键思想是兴趣传播：RippleNet将用户的历史兴趣作为KG中的种子集合(seed set)，然后沿着KG的连接向外扩展用户兴趣，形成用户在KG上的兴趣分布。RippleNet最大的优势在于它可以自动地挖掘从用户历史点击过的物品到候选物品的可能路径，不需要任何人工设计元路径或元图。  &lt;br /&gt;  &lt;img alt="image.png" src="https://segmentfault.com/img/bVc1AX2" title="image.png"&gt;&lt;/img&gt;  &lt;br /&gt;RippleNet将用户U和项目V作为输入，并输出用户U单击项目V的预测概率。对于用户U，将其历史兴趣V_{u}作为种子，在图中可以看到最初的起点是两个，之后不断向周围扩散。给定itemV和用户U的1跳ripple集合V_{u_{}^{1}}中的每个三元组\left( h_{i},r_{i},t_{i} \right)，通过比较V与三元组中的节点h_{i}和关系r_{i}分配相关概率。  &lt;br /&gt;  &lt;img alt="image.png" src="https://segmentfault.com/img/bVc1AYb" title="image.png"&gt;&lt;/img&gt;  &lt;br /&gt;在得到相关概率后，将V_{u_{}^{1}}中三元组的尾部乘以相应的相关概率进行加权求和，得到用户U的历史兴趣关于V的一阶响应，用户兴趣由V_{u}转移到o_{u}^{1}，可以计算得到o_{u}^{2}、o_{u}^{3}...o_{u}^{n}，进而计算得到U关于item V的特征可以被计算为融合他的所有阶数响应。&lt;/p&gt; &lt;h3&gt;四、总结&lt;/h3&gt; &lt;p&gt;综上，我们主要围绕推荐，介绍了图谱构建详细流程，对其中的困难和挑战做出了分析。同时也综述了很多重要的工作，以及给出了具体的解决方案，思路以及建议。最后介绍了包括知识图谱的应用，特别在推荐领域中冷起、可解释性、召回排序介绍了知识图谱的作用与使用。&lt;/p&gt; &lt;hr&gt;&lt;/hr&gt; &lt;p&gt;  &lt;strong&gt;引用&lt;/strong&gt;：  &lt;br /&gt;[1] Kim S，Oh S G．Extracting and Applying Evaluation Criteria for Ontology Quality Assessment［J］．Library Hi Tech，2019.  &lt;br /&gt;[2] Protege:   &lt;a href="https://protegewiki.stanford.edu" rel="nofollow noreferrer"&gt;https://protegewiki.stanford.edu&lt;/a&gt;  &lt;br /&gt;[3] Ding S ,  Shang J ,  Wang S , et al. ERNIE-DOC: The Retrospective Long-Document Modeling Transformer[J].  2020.  &lt;br /&gt;[4] DocBert，[1] Adhikari A ,  Ram A ,  Tang R , et al. DocBERT: BERT for Document Classification[J].  2019.  &lt;br /&gt;[5] JanusGraph，  &lt;a href="https://docs.janusgraph.org/" rel="nofollow noreferrer"&gt;https://docs.janusgraph.org/&lt;/a&gt;  &lt;br /&gt;[6] Sang L, Xu M, Qian S, et al. Knowledge graph enhanced neural collaborative filtering with residual recurrent network[J]. Neurocomputing, 2021, 454: 417-429.  &lt;br /&gt;[7] Du Y ,  Zhu X ,  Chen L , et al. MetaKG: Meta-learning on Knowledge Graph for Cold-start Recommendation[J]. arXiv e-prints, 2022.  &lt;br /&gt;[8] Chen Z ,  Wang X ,  Xie X , et al. Towards Explainable Conversational Recommendation[C]// Twenty-Ninth International Joint Conference on Artificial Intelligence and Seventeenth Pacific Rim International Conference on Artificial Intelligence {IJCAI-PRICAI-20. 2020.  &lt;br /&gt;[9] Wang X ,  He X ,  Cao Y , et al. KGAT: Knowledge Graph Attention Network for Recommendation[J]. ACM, 2019.  &lt;br /&gt;[10] Wang H ,  Zhang F ,  Wang J , et al. RippleNet: Propagating User Preferences on the Knowledge Graph for Recommender Systems[J]. ACM, 2018.&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>深度学习 人工智能 神经网络 机器学习 算法</category>
      <guid isPermaLink="true">https://itindex.net/detail/62357-%E6%B1%BD%E8%BD%A6-%E7%9F%A5%E8%AF%86</guid>
      <pubDate>Mon, 08 Aug 2022 17:36:26 CST</pubDate>
    </item>
    <item>
      <title>ComSec概念概述</title>
      <link>https://itindex.net/detail/62047-comsec-%E6%A6%82%E5%BF%B5</link>
      <description>&lt;h1&gt;  &lt;a href="https://luo41.top/#comsec&amp;#24635;&amp;#32467;"&gt;&lt;/a&gt; ComSec总结&lt;/h1&gt; &lt;p&gt;这学期选修了bintou老师的ComSec计算机安全课，上课期间觉得自己学的马马虎虎的，学习了一些计算机安全中的密码学算法和安全概念，但对这些概念都不是特别清晰，期末复习的过程中，对其中的一些算法和概念更理解了一些，趁着刚考完还没忘记，总结一波挂在博客，不当之处，欢迎指正。&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://luo41.top/#&amp;#20844;&amp;#38053;&amp;#23494;&amp;#30721;&amp;#23398;&amp;#27010;&amp;#36848;"&gt;&lt;/a&gt; 公钥密码学概述&lt;/h2&gt; &lt;p&gt;公钥密码学也称为  &lt;strong&gt;非对称密码学&lt;/strong&gt;，与传统的对称密码学区别在于，加密和解密使用  &lt;strong&gt;不同的密钥&lt;/strong&gt;，也就是所说的  &lt;strong&gt;key&lt;/strong&gt;，其中公开出来的密钥是  &lt;strong&gt;公钥&lt;/strong&gt;，另一个是私钥，由持有者  &lt;strong&gt;严格保密&lt;/strong&gt;。且  &lt;strong&gt;公钥密码&lt;/strong&gt;是基于数学函数而不是基于替换和置换。&lt;/p&gt; &lt;p&gt;公钥密码学提出的解决的基本问题包括有&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;密钥交换&lt;/li&gt;  &lt;li&gt;数字签名&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;公钥密码的六个组成成分分别是  &lt;strong&gt;明文&lt;/strong&gt;，  &lt;strong&gt;密文&lt;/strong&gt;，  &lt;strong&gt;公钥&lt;/strong&gt;，  &lt;strong&gt;私钥&lt;/strong&gt;，  &lt;strong&gt;加密算法&lt;/strong&gt;，  &lt;strong&gt;解密算法&lt;/strong&gt;。&lt;/p&gt; &lt;p&gt;公钥密码的重要特点是&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;仅根据密码算法和加密密钥，确定解密密钥在   &lt;strong&gt;计算上不可行&lt;/strong&gt;，所谓计算上不可行即是   &lt;code&gt;解决一个问题所需要的时间比输入规模的多项式增长更快，如输入长度为n位，计算复杂度是&lt;/code&gt;2n2^{n}2n。&lt;/li&gt;  &lt;li&gt;两个密钥中的任意一个都可用来加密，另一个用来解密，也就是说，存在两个方式，一种是   &lt;code&gt;公钥加密，私钥解密&lt;/code&gt;，一种是   &lt;code&gt;私钥加密，公钥解密&lt;/code&gt;。根据这两种方式，公钥密码体制分成了两个模式，一个是   &lt;strong&gt;加密模式&lt;/strong&gt;，一个是   &lt;strong&gt;认证模式&lt;/strong&gt;。&lt;/li&gt;&lt;/ul&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#21152;&amp;#23494;&amp;#27169;&amp;#24335;"&gt;&lt;/a&gt; 加密模式&lt;/h3&gt; &lt;p&gt;所谓  &lt;strong&gt;加密模式&lt;/strong&gt;，也就是用  &lt;code&gt;公钥加密，私钥解密&lt;/code&gt;的模式。&lt;/p&gt; &lt;p&gt;加密模式的运行过程如下，A向B发消息X，已知B的公钥为KUbK_{Ub}KUb​，私钥为KRbK_{Rb}KRb​，加密算法为E，解密算法为D，则加密过程为Y=EKUb(X)Y=E_{K_{Ub}}(X)Y=EKUb​​(X)，解密过程X=DKRb(Y)X=D_{K_{Rb}}(Y)X=DKRb​​(Y)。之所以能够保证  &lt;strong&gt;传递消息是保密的&lt;/strong&gt;，是因为只有  &lt;code&gt;私钥才可以解密&lt;/code&gt;，而私钥只有B持有。所以所有想给B发消息的人，只需要用B的公钥和对应的加密算法对消息进行加密即可，而加密的消息只有B能解，保证了  &lt;strong&gt;消息的保密性&lt;/strong&gt;。&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#35748;&amp;#35777;&amp;#27169;&amp;#24335;"&gt;&lt;/a&gt; 认证模式&lt;/h3&gt; &lt;p&gt;所谓  &lt;strong&gt;认证模式&lt;/strong&gt;，也就是用  &lt;code&gt;私钥加密，公钥解密&lt;/code&gt;的模式。&lt;/p&gt; &lt;p&gt;认证模式的运行过程如下，A向B发消息，已经A的公钥为KUaK_{Ua}KUa​，A的私钥为KRaK_{Ra}KRa​，加密算法为E，解密算法为D，则加密过程为Y=EKRa(X)Y=E_{K_{Ra}}(X)Y=EKRa​​(X)，解密过程为X=DKUa(Y)X=D_{K_{Ua}}(Y)X=DKUa​​(Y)，注意这里虽然说的是加密算法和解密算法，但是  &lt;strong&gt;认证模式&lt;/strong&gt;下并不能保证消息的保密性，因为只要知道A的公钥的人就可以解出明文，而公钥又是公开的，所以并不能提供消息的保密性，那么认证模式是来干嘛的呢？认证模式  &lt;strong&gt;只有私钥能加密&lt;/strong&gt;，借助这个  &lt;strong&gt;唯一加密&lt;/strong&gt;的特性，可以实现数字签名，私钥就相当于你的  &lt;strong&gt;字迹&lt;/strong&gt;，别人无法模仿的，别人一看到这个字迹就知道是你写的，就相当于别人用公钥验证私钥，验证成功，知道消息是你发的，提供了  &lt;strong&gt;认证性&lt;/strong&gt;。&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#21516;&amp;#26102;&amp;#20855;&amp;#26377;&amp;#20445;&amp;#23494;&amp;#19982;&amp;#35748;&amp;#35777;&amp;#30340;&amp;#20844;&amp;#38053;&amp;#20307;&amp;#36136;"&gt;&lt;/a&gt; 同时具有保密与认证的公钥体质&lt;/h3&gt; &lt;p&gt;那么如果我们传送消息时，想要同时有  &lt;strong&gt;加密&lt;/strong&gt;和  &lt;strong&gt;认证&lt;/strong&gt;两种需求，如何做呢？很简单，就是用两对公私钥对，一对采用  &lt;strong&gt;加密模式&lt;/strong&gt;，一对才用  &lt;strong&gt;认证模式&lt;/strong&gt;。&lt;/p&gt; &lt;p&gt;假定A向B发消息，A的密钥对为{KUaK_{Ua}KUa​，KRaK_{Ra}KRa​}，B的密钥对为{KUbK_{Ub}KUb​，KRaK_{Ra}KRa​}，A要求发的消息同时具有  &lt;strong&gt;加密性&lt;/strong&gt;和  &lt;strong&gt;认证性&lt;/strong&gt;，怎么做呢？&lt;/p&gt; &lt;p&gt;首先是加密性，加密性需要用  &lt;code&gt;公钥加密，私钥解密&lt;/code&gt;，用谁的公钥呢？B的，因为B要查看消息，所以B要用自己的私钥去解密消息，所以A就用B的公钥去加密要传送的消息。  &lt;strong&gt;认证&lt;/strong&gt;需要用私钥加密，用谁的私钥呢？A的，因为B要确认消息是A所发出的，所以用A的私钥对消息进行加密，B收到消息后用解密算法进行验证，实现认证。&lt;/p&gt; &lt;p&gt;整个过程如下：假定发送的  &lt;strong&gt;明文为X&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;首先用B的公钥对消息进行加密Z=EKUb(X)Z=E_{K_{Ub}}(X)Z=EKUb​​(X)，这一步实现的是  &lt;strong&gt;加密&lt;/strong&gt;，然后再执行Y=EKRa(Z)Y=E_{K_{Ra}}(Z)Y=EKRa​​(Z)，这一步实现的是  &lt;strong&gt;认证&lt;/strong&gt;，然后将密文Y传送给B，B收到Y后首先做Z=DKUa(Y)Z=D_{K_{Ua}}(Y)Z=DKUa​​(Y)解出Z，然后执行X=DKRb(Z)X=D_{K_{Rb}}(Z)X=DKRb​​(Z)解出明文X，实现了  &lt;strong&gt;加密&lt;/strong&gt;和  &lt;strong&gt;认证&lt;/strong&gt;。&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#20844;&amp;#38053;&amp;#23494;&amp;#30721;&amp;#20307;&amp;#21046;&amp;#24212;&amp;#28385;&amp;#36275;&amp;#30340;&amp;#35201;&amp;#27714;"&gt;&lt;/a&gt; 公钥密码体制应满足的要求&lt;/h3&gt; &lt;p&gt;公钥密文体制的运行也需要一些要求&lt;/p&gt; &lt;p&gt;  &lt;code&gt;一般要求：&lt;/code&gt;&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;加密解密算法相同，但使用密钥不同&lt;/li&gt;  &lt;li&gt;发送方拥有加密或解密密钥，而接收方拥有另一个密钥。&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;  &lt;code&gt;安全性要求&lt;/code&gt;：&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;两个密钥之一必须保密，   &lt;strong&gt;作为私钥&lt;/strong&gt;&lt;/li&gt;  &lt;li&gt;无解密密钥，解密不可行&lt;/li&gt;  &lt;li&gt;知道算法和其中一个密钥，以及若干个密文不能确定另一个密文。&lt;/li&gt;&lt;/ol&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#20851;&amp;#20110;&amp;#20844;&amp;#38053;&amp;#23494;&amp;#30721;&amp;#30340;&amp;#20960;&amp;#31181;&amp;#35823;&amp;#35299;"&gt;&lt;/a&gt; 关于公钥密码的几种误解&lt;/h3&gt; &lt;ol&gt;  &lt;li&gt;   &lt;p&gt;公钥密码比传统密码更安全？&lt;/p&gt;   &lt;p&gt;任何加密算法的    &lt;strong&gt;安全性&lt;/strong&gt;依赖于    &lt;strong&gt;密钥的长度&lt;/strong&gt;和    &lt;strong&gt;破译密文所需要的计算量&lt;/strong&gt;。从抗密码分析的角度来看，原则上不能说传统密码优于公钥密码，而不能说公钥密码优于传统密码。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;公钥密码是通用方法，所以传统密码已经过时？&lt;/p&gt;   &lt;p&gt;其实恰恰相反，由于现有的公钥密码方法所需的    &lt;strong&gt;计算量大&lt;/strong&gt;，取缔传统密码似乎不太可能，仅限用在    &lt;strong&gt;密钥管理&lt;/strong&gt;和    &lt;strong&gt;签名&lt;/strong&gt;这类应用上。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;公钥密码实现密钥分配非常简单？&lt;/p&gt;   &lt;p&gt;事实上，使用公钥密码也需要某种形式的    &lt;strong&gt;协议&lt;/strong&gt;，该协议通常包含一个    &lt;strong&gt;中心代理&lt;/strong&gt;，并且它的处理过程也不比传统密码中的那些过程简单和有效。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt; &lt;h2&gt;  &lt;a href="https://luo41.top/#rsa"&gt;&lt;/a&gt; RSA&lt;/h2&gt; &lt;p&gt;RSA公开密钥密码体制是最著名且被广泛使用的公开密钥密码体制。&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#rsa&amp;#31639;&amp;#27861;&amp;#30340;&amp;#22522;&amp;#26412;&amp;#36807;&amp;#31243;"&gt;&lt;/a&gt; RSA算法的基本过程&lt;/h3&gt; &lt;p&gt;首先，明文和密文是0~n-1之间的整数，通常n的大小为1024位或309位十进制。&lt;/p&gt; &lt;p&gt;  &lt;code&gt;RSA算法描述&lt;/code&gt;&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;加密&lt;/strong&gt;：C=Me mod NC=M^{e} \ mod \ NC=Me mod N  where 0&amp;lt;= M &amp;lt; N&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;解密&lt;/strong&gt;：$M=C^{d} mod \ N \ $&lt;/p&gt; &lt;p&gt;公钥为(e,N)，私钥为(d,N)。&lt;/p&gt; &lt;p&gt;必须满足的以下条件&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;   &lt;p&gt;Med≡M mod NM^{ed} ≡ M \ mod \ NMed≡M mod N&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;计算MeM^{e}Me和CdC^{d}Cd是比较容易的(    &lt;strong&gt;效率&lt;/strong&gt;)。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;由e和n确定d是不可行的(    &lt;strong&gt;安全&lt;/strong&gt;)。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#23494;&amp;#38053;&amp;#20135;&amp;#29983;&amp;#36807;&amp;#31243;"&gt;&lt;/a&gt; 密钥产生过程&lt;/h3&gt; &lt;ol&gt;  &lt;li&gt;   &lt;p&gt;随机选择两个不等的    &lt;strong&gt;大素数&lt;/strong&gt;p，q&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;计算N=p*q，对应有ϕ(N)=(p−1)×(q−1)\phi(N)=(p-1)×(q-1)ϕ(N)=(p−1)×(q−1)&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;选择e使得 1&amp;lt;e&amp;lt;ϕ(N)1&amp;lt;e&amp;lt;\phi(N)1&amp;lt;e&amp;lt;ϕ(N)，且$gcd(e,\phi(N)) = 1 （使得e在（使得e在（使得e在\phi(N)$下存在    &lt;strong&gt;逆元&lt;/strong&gt;，也就是后面我们要求的d）&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;解下列方程求出d&lt;/p&gt;   &lt;p&gt;ed≡1 mod ϕ(N)ed ≡ 1 \ mod \ \phi(N)ed≡1 mod ϕ(N)  且 0&amp;lt;= d &amp;lt;= N&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;公布公钥KU={e，N}&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;保存私钥KR={d，N}&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#rsa&amp;#30340;&amp;#20351;&amp;#29992;"&gt;&lt;/a&gt; RSA的使用&lt;/h3&gt; &lt;ol&gt;  &lt;li&gt;发送方加密明文M   &lt;ul&gt;    &lt;li&gt;获取接收方的公钥KU={e,N}&lt;/li&gt;    &lt;li&gt;计算$C=M^{e} \ mod \ N $  where 0&amp;lt;= M &amp;lt; N&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;  &lt;li&gt;接收方解密密文C   &lt;br /&gt;用自己的私钥KR={d，N}，计算 M=Cd mod NM=C^{d} \ mod \ NM=Cd mod N&lt;/li&gt;&lt;/ol&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#20026;&amp;#20160;&amp;#20040;rsa&amp;#21487;&amp;#20197;&amp;#21152;&amp;#35299;&amp;#23494;"&gt;&lt;/a&gt; 为什么RSA可以加解密？&lt;/h3&gt; &lt;p&gt;由  &lt;strong&gt;欧拉定理&lt;/strong&gt;：&lt;/p&gt; &lt;blockquote&gt;  &lt;p&gt;欧拉定理：设n和a为正整数，且gcd(a,n) = 1，则 aϕ(n)≡1 (mod n)a^{\phi(n)} ≡ 1\ (mod \ n)aϕ(n)≡1 (mod n)&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;又在RSA ：ed≡1 mod ϕ(N)ed ≡ 1 \ mod \ \phi(N)ed≡1 mod ϕ(N)，等价于ed=kϕ(N)+1ed=k\phi(N)+1ed=kϕ(N)+1&lt;/p&gt; &lt;p&gt;所以$M^{ed} ≡ M \ mod \ N $&lt;/p&gt; &lt;p&gt;等价于 Mkϕ(N)+1≡M mod NM^{k\phi(N)+1} ≡ M \ mod \ NMkϕ(N)+1≡M mod N&lt;/p&gt; &lt;p&gt;在RSA中 N=p×q，p,q为素数，所以有ϕ(N)=(p−1)×(q−1)\phi(N)=(p-1)×(q-1)ϕ(N)=(p−1)×(q−1)&lt;/p&gt; &lt;p&gt;且 ed≡1 mod ϕ(N)ed ≡ 1 \ mod \ \phi(N)ed≡1 mod ϕ(N)&lt;/p&gt; &lt;p&gt;因此，有Cd≡(Me)d=Med≡M mod N=MC^{d}≡ (M^{e})^{d}=M^{ed} ≡ M \ mod \ N = MCd≡(Me)d=Med≡M mod N=M&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#rsa&amp;#23494;&amp;#38053;key&amp;#30340;&amp;#36873;&amp;#25321;"&gt;&lt;/a&gt; RSA密钥key的选择&lt;/h3&gt; &lt;p&gt;从安全性考虑，key的选择需要以下规则&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;p和q不能太接近&lt;/li&gt;  &lt;li&gt;p-1 和 q-1必须包含大的素因子&lt;/li&gt;  &lt;li&gt;gcd(q-1,p-1)必须小&lt;/li&gt;  &lt;li&gt;e和d不能太小，e可选65537&lt;/li&gt;  &lt;li&gt;d不能小于n1/4n^{1/4}n1/4&lt;/li&gt;&lt;/ol&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#rsa&amp;#30340;&amp;#23433;&amp;#20840;&amp;#24615;"&gt;&lt;/a&gt; RSA的安全性&lt;/h3&gt; &lt;ul&gt;  &lt;li&gt;   &lt;p&gt;强力穷举攻击：    &lt;strong&gt;穷举&lt;/strong&gt;所有可能的私钥&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;数学攻击：实质是对两个素数的乘积N的分解，由N求得ϕ(N)\phi(N)ϕ(N)是困难的，因此要将N分解为p和q两个质数，然后利用ϕ(N)=(p−1)∗(q−1)\phi(N)=(p-1)*(q-1)ϕ(N)=(p−1)∗(q−1)&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;时间攻击 ：不懂&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;选择密文攻击CCA&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;三种数学攻击方法&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;   &lt;p&gt;分解N=p*q，由此可算出ϕ(N)\phi(N)ϕ(N)，从而确定d&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;直接确定ϕ(N)\phi(N)ϕ(N)，然后找到d&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;直接确定d&lt;/p&gt;   &lt;p&gt;本质上都是要找到ϕ(N)\phi(N)ϕ(N)或直接找到d，由    &lt;code&gt;N确定$\phi(N)$可等价于因子分解&lt;/code&gt;。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#rsa&amp;#20844;&amp;#38053;&amp;#20307;&amp;#36136;&amp;#30340;&amp;#23433;&amp;#20840;&amp;#24615;&amp;#26159;&amp;#24314;&amp;#31435;&amp;#22312;&amp;#21738;&amp;#31181;&amp;#20107;&amp;#23454;&amp;#20043;&amp;#19978;"&gt;&lt;/a&gt; RSA公钥体质的安全性是建立在哪种事实之上？&lt;/h3&gt; &lt;p&gt;  &lt;strong&gt;因子分解&lt;/strong&gt;具有大素数因子的数仍然是一个难题。&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#36873;&amp;#25321;&amp;#23494;&amp;#25991;cca"&gt;&lt;/a&gt; 选择密文CCA&lt;/h3&gt; &lt;p&gt;选择密文攻击是假定攻击者有一台  &lt;strong&gt;解密机&lt;/strong&gt;，除了待解的密文，攻击者可以输入任意的密文，然后解密机就会给出对应的明文，因此攻击者可以构造特殊的密文，去求解出  &lt;strong&gt;待解的密文&lt;/strong&gt;。&lt;/p&gt; &lt;p&gt;给定待解密文C∗C^{*}C∗，求对应的明文M∗M^{*}M∗，并给出一个解密机O&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;攻击过程&lt;/strong&gt;：&lt;/p&gt; &lt;p&gt;构造密文C=2eC∗C=2^{e}C^{*}C=2eC∗&lt;/p&gt; &lt;p&gt;将C输入到解密机O，得到对应的明文M&lt;/p&gt; &lt;p&gt;M=(Cd mod N)=(2eC∗)d mod N=2edC∗d mod N=2∗m∗ mod N=2m∗M=(C^{d} \ mod \ N)=(2^{e}C^{*})^{d} \ mod \ N = 2^{ed}C^{*d} \ mod \ N = 2* m^{*} \ mod \ N = 2m^{*}M=(Cd mod N)=(2eC∗)d mod N=2edC∗d mod N=2∗m∗ mod N=2m∗&lt;/p&gt; &lt;p&gt;于是便可以求得m∗m^{*}m∗&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://luo41.top/#diffie-hellman-&amp;#23494;&amp;#38053;&amp;#20132;&amp;#25442;&amp;#21327;&amp;#35758;"&gt;&lt;/a&gt; Diffie-Hellman 密钥交换协议&lt;/h2&gt; &lt;p&gt;Diffie-Hellman需要原根(生成元)的定义&lt;/p&gt; &lt;p&gt;  &lt;code&gt;原根&lt;/code&gt;：如果a是素数p的原根，则数 a mod p,a2 mod pa^{2} \ mod \ pa2 mod p … ap−1 mod pa^{p-1} \ mod \ pap−1 mod p是不同的并且包含1到p-1的整数的  &lt;strong&gt;某种排列&lt;/strong&gt;。原根是数论的概念。&lt;/p&gt; &lt;p&gt;  &lt;code&gt;生成元&lt;/code&gt;：p为素数，g是Zp∗Z_{p}^{*}Zp∗​的生成元，当且仅当g的阶是p-1. 生成元是  &lt;strong&gt;群论的概念&lt;/strong&gt;。&lt;/p&gt; &lt;p&gt;Diffie-Hellman公钥体制&lt;/p&gt; &lt;p&gt;目标：Alice和Bob试图在公开网络上通过交互建立一个  &lt;strong&gt;秘密&lt;/strong&gt;&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#diffie-hellman-&amp;#23494;&amp;#38053;&amp;#20132;&amp;#25442;&amp;#31639;&amp;#27861;"&gt;&lt;/a&gt; Diffie-Hellman 密钥交换算法&lt;/h3&gt; &lt;p&gt;  &lt;code&gt;全局公开量：&lt;/code&gt;&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;大素数：p&lt;/li&gt;  &lt;li&gt;选g为Zp∗Z_{p}^{*}Zp∗​的生成元&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;  &lt;code&gt;Alice密钥的产生&lt;/code&gt;：&lt;/p&gt; &lt;p&gt;选择随机数x&amp;lt;p&lt;/p&gt; &lt;p&gt;计算公开量 X=gx mod pX=g^{x} \ mod \ pX=gx mod p&lt;/p&gt; &lt;p&gt;  &lt;code&gt;Bob密钥的产生&lt;/code&gt;：&lt;/p&gt; &lt;p&gt;选择随机数y&amp;lt;p&lt;/p&gt; &lt;p&gt;计算公开量Y=gy mod pY=g^{y}\ mod \ pY=gy mod p&lt;/p&gt; &lt;p&gt;  &lt;code&gt;协议过程&lt;/code&gt;：&lt;/p&gt; &lt;p&gt;Alice -----&amp;gt; Bob:X&lt;/p&gt; &lt;p&gt;Bob -------&amp;gt; Alice：Y&lt;/p&gt; &lt;p&gt;  &lt;code&gt;产生密钥&lt;/code&gt;：&lt;/p&gt; &lt;p&gt;Alice接受到了Y，计算KAB=Yx mod qK_{AB}=Y^{x} \ mod \ qKAB​=Yx mod q&lt;/p&gt; &lt;p&gt;Bob接受到了X，计算KAB=Xy mod qK_{AB}=X^{y}\ mod \ qKAB​=Xy mod q&lt;/p&gt; &lt;p&gt;KAB=gxy mod qK_{AB}=g^{xy}\ mod \ qKAB​=gxy mod q&lt;/p&gt; &lt;p&gt;这样双方就都计算出了密钥KABK_{AB}KAB​，成功完成了密钥的交换，且  &lt;strong&gt;各自的私钥x和y没有暴露&lt;/strong&gt;。&lt;/p&gt; &lt;p&gt;为什么没有暴露呢？因为传递的是X和Y，而Diffie-Hellman密钥的安全性正是建立在如下的事实上：  &lt;code&gt;求关于素数的模幂运算相对容易，而计算离散对数问题却非常困难，对于大素数，求离散对数问题被认为是计算上不可行的&lt;/code&gt;&lt;/p&gt; &lt;blockquote&gt;  &lt;p&gt;离散对数问题：给定y=gx mod py=g^{x} \ mod \ py=gx mod p，知道y,g,p，求x在计算上是不可行的，如果p是一个大素数&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;正因如此，双方才能在各自私钥不暴露的情况下完成密钥(KABK_{AB}KAB​)的交换。&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#diffie-hellman&amp;#31639;&amp;#27861;&amp;#20013;&amp;#23384;&amp;#22312;&amp;#30340;&amp;#38382;&amp;#39064;"&gt;&lt;/a&gt; Diffie-Hellman算法中存在的问题&lt;/h3&gt; &lt;p&gt;Diffie-Hellman算法中存在的问题就是  &lt;strong&gt;不能对抗中间人攻击&lt;/strong&gt;。&lt;/p&gt; &lt;p&gt;  &lt;code&gt;中间人攻击原理如下&lt;/code&gt;：&lt;/p&gt; &lt;img src="https://luo41.top/2022/01/20/ComSec%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%AE%89%E5%85%A8OverView/1.png"&gt;&lt;/img&gt; &lt;p&gt;那么如何对抗中间人攻击呢？之所以存在中间人攻击，是因为Diffie-Hellman没有对通信的参与方  &lt;strong&gt;进行认证&lt;/strong&gt;，所以引入  &lt;strong&gt;认证&lt;/strong&gt;机制可以抵抗中间人攻击，如何引入  &lt;strong&gt;认证机制&lt;/strong&gt;？就要用到我们前面提到的公钥密码的认证模式，具体的应用为  &lt;strong&gt;数字签名&lt;/strong&gt;，通过添加  &lt;strong&gt;数字签名&lt;/strong&gt;对通信的参与方进行认证，从而对抗中间人攻击。&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://luo41.top/#ecc&amp;#26925;&amp;#22278;&amp;#35745;&amp;#31639;"&gt;&lt;/a&gt; ECC椭圆计算&lt;/h2&gt; &lt;p&gt;只是一个只会在ZpZ_{p}Zp​上计算的做题家罢了。&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://luo41.top/#hash&amp;#19982;mac&amp;#37096;&amp;#20998;"&gt;&lt;/a&gt; Hash与MAC部分&lt;/h2&gt; &lt;p&gt;  &lt;code&gt;Hash函数&lt;/code&gt;：Hash函数不是加密解密函数，只是  &lt;strong&gt;保证消息的完整性&lt;/strong&gt;，在密码学中具有重要的理论和应用价值。&lt;/p&gt; &lt;p&gt;  &lt;code&gt;Hash函数的定义&lt;/code&gt;&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;Hash函数是将   &lt;strong&gt;任意长度&lt;/strong&gt;的报文   &lt;strong&gt;映射&lt;/strong&gt;成一个   &lt;strong&gt;较短的定长&lt;/strong&gt;输出报文的函数。&lt;/li&gt;  &lt;li&gt;如下形式 h=H(M)，M是边长的报文，h是定长的Hash值。&lt;/li&gt;  &lt;li&gt;Hash函数的目的是为文件，报文或其他的分组数据产生   &lt;strong&gt;数字指纹&lt;/strong&gt;，通过检查“数字指纹”判断消息有没有被篡改过，从而判断   &lt;strong&gt;消息的完整性&lt;/strong&gt;。&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;  &lt;code&gt;对Hash函数的要求：&lt;/code&gt;&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;能用于任何大小的数据分组&lt;/li&gt;  &lt;li&gt;H产生定长输出&lt;/li&gt;  &lt;li&gt;对任意给定的x，H(x)要相对   &lt;strong&gt;易于计算&lt;/strong&gt;，使得软硬件实现都变得可行。&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;  &lt;code&gt;安全性需求&lt;/code&gt;：&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;单向性&lt;/strong&gt;：对任意给定的码y，寻求x使得H(x)=y     &lt;strong&gt;在计算上不可行&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;弱抗碰撞性&lt;/strong&gt;：任意分组x，寻求不等于x的x’，使得H(x)=H(x’)    &lt;strong&gt;在计算上不可行&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;强抗碰撞性&lt;/strong&gt;：寻求任何的(x，x’)，使得H(x)=H(x’)    &lt;strong&gt;在计算上不可行&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;  &lt;code&gt;密码学Hash函数的应用&lt;/code&gt;&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;消息认证&lt;/strong&gt;：验证消息    &lt;strong&gt;完整性&lt;/strong&gt;的一种机制或服务。确保收到的数据确实和发送时一样，即没有发生第三方截获了消息，进行修改，插入，删除或重放。此外，通常还要求消息认证机制确保&amp;quot;    &lt;strong&gt;发送方声称的身份是真实有效的&lt;/strong&gt;&amp;quot;，Hash函数用于提供消息认证功能时，Hash值也称为    &lt;strong&gt;消息摘要&lt;/strong&gt;。更一般地，消息认证是通过    &lt;strong&gt;消息认证码MAC&lt;/strong&gt;实现的，MAC即    &lt;strong&gt;带密钥&lt;/strong&gt;的Hash。通信双方基于    &lt;strong&gt;共享的同一密钥&lt;/strong&gt;来认证彼此交互的消息，就会使用MAC。&lt;/p&gt;   &lt;p&gt;总体来看 MAC是    &lt;strong&gt;Hash函数和加密函数的集合&lt;/strong&gt;,对Hash值保密，防止中间人篡改后轻松地    &lt;strong&gt;伪造Hash值&lt;/strong&gt;。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;数字签名&lt;/strong&gt;：&lt;/p&gt;   &lt;p&gt;Hash函数另一个重要的应用是    &lt;strong&gt;数字签名&lt;/strong&gt;。数字签名的操作与MAC相似，但MAC防止是第三方的截获和篡改，而数字签名防止的是通信双方自己之间的欺骗(    &lt;strong&gt;…全得防着，太可怕了&lt;/strong&gt;)。在进行数字签名的过程中，使用用户的    &lt;strong&gt;私钥&lt;/strong&gt;加密消息的Hash值，其他任何知道该用户    &lt;strong&gt;公钥&lt;/strong&gt;的人都能够通过数字签名来    &lt;strong&gt;验证消息的完整性&lt;/strong&gt;。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt; &lt;h2&gt;  &lt;a href="https://luo41.top/#&amp;#35748;&amp;#35777;&amp;#25216;&amp;#26415;"&gt;&lt;/a&gt; 认证技术&lt;/h2&gt; &lt;p&gt;认证是防止  &lt;strong&gt;主动攻击&lt;/strong&gt;的重要技术，对开放系统的安全性起到重要作用。&lt;/p&gt; &lt;p&gt;认证的主要目的是：&lt;/p&gt; &lt;p&gt;一   &lt;strong&gt;实体认证&lt;/strong&gt;(  &lt;strong&gt;发送者非冒充&lt;/strong&gt;)&lt;/p&gt; &lt;p&gt;二   &lt;strong&gt;报文认证&lt;/strong&gt;(验证消息的完整性)&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#25253;&amp;#25991;&amp;#35748;&amp;#35777;mac"&gt;&lt;/a&gt; 报文认证MAC&lt;/h3&gt; &lt;p&gt;报文认证，简单而言，就是要求接收方可以确保发送方发来的数据具有  &lt;strong&gt;真实性&lt;/strong&gt;，没有被篡改或是伪造数据。&lt;/p&gt; &lt;p&gt;  &lt;code&gt;报文认证实例&lt;/code&gt;&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;在电子商务系统中，在电子政务系统中，我们需要确认某些电子文档不是伪造的。&lt;/li&gt;  &lt;li&gt;在实际应用中，我们考虑如何把非法软件与合法软件区分开来。&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;  &lt;code&gt;特点&lt;/code&gt;：第三方可仲裁，离线或者非实时。&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#23454;&amp;#20307;&amp;#35748;&amp;#35777;-&amp;#29992;&amp;#25143;&amp;#35748;&amp;#35777;"&gt;&lt;/a&gt; 实体认证 – 用户认证&lt;/h3&gt; &lt;ul&gt;  &lt;li&gt;确保正在参与通信者是一个非冒充用户，比如QQ,MSN等即时通信系统，邮件系统等&lt;/li&gt;  &lt;li&gt;实例：安全登录&lt;/li&gt;  &lt;li&gt;特性：实时，在线，多方参与…&lt;/li&gt;&lt;/ul&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#25253;&amp;#25991;&amp;#35748;&amp;#35777;"&gt;&lt;/a&gt; 报文认证&lt;/h3&gt; &lt;p&gt;Ailice与Bob共同拥有一个秘密sk，Alice用sk加密M给Bob，Bob解密，立即可知此报文是真假是假。但是这种方法无法防止Alice和Bob之间的相互欺骗，例如Alice否认自己发送过消息，因为Bob也可以伪造Alice给自己发消息，同时Bob也可以否认自己收过某条消息，问题的症状在于Alice与Bob共同拥有  &lt;strong&gt;私钥sk&lt;/strong&gt;,而后的数字签名就是来解决这个问题的。&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#mac&amp;#30340;&amp;#23433;&amp;#20840;&amp;#24615;&amp;#38656;&amp;#27714;"&gt;&lt;/a&gt; MAC的安全性需求&lt;/h3&gt; &lt;ul&gt;  &lt;li&gt;某个报文M的   &lt;strong&gt;MAC&lt;/strong&gt;必须要短小&lt;/li&gt;  &lt;li&gt;要有   &lt;strong&gt;认证性&lt;/strong&gt;，发送接收双方必须共享   &lt;strong&gt;秘密&lt;/strong&gt;，看了有些博客，好像也叫   &lt;strong&gt;初始信任&lt;/strong&gt;，有了初始信任才能有认证。&lt;/li&gt;  &lt;li&gt;Hash不足以保证安全&lt;/li&gt;  &lt;li&gt;避免“加密运算”&lt;/li&gt;&lt;/ul&gt; &lt;h2&gt;  &lt;a href="https://luo41.top/#&amp;#25968;&amp;#23383;&amp;#31614;&amp;#21517;"&gt;&lt;/a&gt; 数字签名&lt;/h2&gt; &lt;p&gt;签名提到了MAC报文认证，MAC理论的遗留问题：要求通信双方首先要共享  &lt;strong&gt;秘密&lt;/strong&gt;，也称为初始信任，之后才能有认证，双方共享sk，似乎是个很强的要求，但有了  &lt;strong&gt;公钥密码体制&lt;/strong&gt;(PKC)，这个问题可以得到解决。&lt;/p&gt; &lt;p&gt;  &lt;code&gt;数字签名是认证的重要工具&lt;/code&gt;。&lt;/p&gt; &lt;p&gt;  &lt;code&gt;为什么需要数字签名&lt;/code&gt;：&lt;/p&gt; &lt;p&gt;前面提到了报文认证是用来保护双方之间的数据交换不被第三方侵扰，但它并不能保证  &lt;strong&gt;双方自身的相互欺骗&lt;/strong&gt;。&lt;/p&gt; &lt;p&gt;实例：&lt;/p&gt; &lt;p&gt;假定A发送一个认证的信息给B，双方之间的争议可能有：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;B伪造一个不同的消息(   &lt;strong&gt;因为B也有sk&lt;/strong&gt;)，但声称是从A收到的&lt;/li&gt;  &lt;li&gt;A可以否认发过该消息(   &lt;strong&gt;因为A可以说是B自己给自己发的&lt;/strong&gt;)，B无法证明A确实发了该消息&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;问题的症状在于双方都有  &lt;strong&gt;sk&lt;/strong&gt;，都可以进行发消息和验证消息，问题在于发送和验证职责的分离。&lt;/p&gt; &lt;p&gt;  &lt;code&gt;数字签名的直观定义&lt;/code&gt;：&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;目标&lt;/strong&gt;：防止报文被伪造，用于保证数据的完整性&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;操作&lt;/strong&gt;：签名者对报文产生一串  &lt;strong&gt;公开且他人无法伪造的数据串&lt;/strong&gt;，公开是为了给他人验证，无法伪造是保证发送方无法否认发过该消息。&lt;/p&gt; &lt;p&gt;方法：PKC+Hash  PKC用私钥加密，公钥解密，提供认证性，Hash提供消息的完整性。&lt;/p&gt; &lt;h4&gt;  &lt;a href="https://luo41.top/#&amp;#25968;&amp;#23383;&amp;#31614;&amp;#21517;&amp;#24212;&amp;#20855;&amp;#26377;&amp;#30340;&amp;#24615;&amp;#36136;"&gt;&lt;/a&gt; 数字签名应具有的性质&lt;/h4&gt; &lt;ul&gt;  &lt;li&gt;必须能够验证签名者以及签名的日期，时间等&lt;/li&gt;  &lt;li&gt;必须能够认证签名的报文内容&lt;/li&gt;  &lt;li&gt;签名能够由第三方验证，以解决收发双发的争议&lt;/li&gt;&lt;/ul&gt; &lt;h2&gt;  &lt;a href="https://luo41.top/#&amp;#35748;&amp;#35777;&amp;#31995;&amp;#32479;"&gt;&lt;/a&gt; 认证系统&lt;/h2&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#35777;&amp;#20070;"&gt;&lt;/a&gt; 证书&lt;/h3&gt; &lt;p&gt;  &lt;code&gt;证书的定义：&lt;/code&gt; 公钥 &amp;lt;------&amp;gt; 用户&lt;/p&gt; &lt;p&gt;通俗将：数字证书就是把  &lt;code&gt;公钥与其所有者的身份&lt;/code&gt;进行  &lt;strong&gt;绑定&lt;/strong&gt;的文档。&lt;/p&gt; &lt;p&gt;而绑定的方法就是：  &lt;code&gt;权威机构(证书颁布机构)CA的数字签名&lt;/code&gt;。&lt;/p&gt; &lt;p&gt;  &lt;code&gt;两个实体&lt;/code&gt;：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;证书用户：使用证书的实体&lt;/li&gt;  &lt;li&gt;证书所有者：证书中的主体，证书中公钥的持有者&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;  &lt;code&gt;证书的格式&lt;/code&gt;&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;版本号&lt;/strong&gt; Version：区分证书的不同版本&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;证书序列号&lt;/strong&gt; Serial number 一个证书，在CA中    &lt;strong&gt;唯一标识证书&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;签名算法标识&lt;/strong&gt;：带参数的，用于给证书签名的算法&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;发行商名字&lt;/strong&gt; Issuer name 创建签名证书的签证机构CA的名字&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;有效期&lt;/strong&gt; 生效日期和终止日期&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;证书主体名&lt;/strong&gt; 获得证书的用户名，证明拥有相应密钥的主体是公钥的所有者&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;发行商唯一标识&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;证书主体唯一标识&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;…    &lt;strong&gt;拓展&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;签名&lt;/strong&gt;：用CA私钥对证书的    &lt;strong&gt;所有域&lt;/strong&gt;及这些域的Hash的值一起加密(    &lt;strong&gt;用私钥&lt;/strong&gt;)&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#pki-public-key-infrastructure-&amp;#20844;&amp;#38053;&amp;#22522;&amp;#30784;&amp;#35774;&amp;#26045;"&gt;&lt;/a&gt;   &lt;code&gt;PKI Public Key Infrastructure&lt;/code&gt; 公钥基础设施&lt;/h3&gt; &lt;p&gt;用来提供可靠易用的  &lt;strong&gt;公钥密码&lt;/strong&gt;操作的  &lt;strong&gt;系统的总称&lt;/strong&gt;。&lt;/p&gt; &lt;p&gt;主要目标：保障大型开放式网络环境下的网络和信息安全&lt;/p&gt; &lt;p&gt;  &lt;code&gt;PKI系统的主要组成&lt;/code&gt;：&lt;/p&gt; &lt;p&gt;  &lt;img alt="image-20220120144003640" src="https://luo41.top/2022/01/20/ComSec%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%AE%89%E5%85%A8OverView/C:%5CUsers%5CLenovo%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20220120144003640.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;code&gt;PKI系统主要组成&lt;/code&gt;：&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;   &lt;strong&gt;PKI策略&lt;/strong&gt; 定义和建立了一个组织信息安全方面的指导方针，同时也定义了密码系统使用的处理方法和原则&lt;/li&gt;  &lt;li&gt;   &lt;strong&gt;认证机构CA(Certificate Authority)系统&lt;/strong&gt;   &lt;br /&gt;PKI的信任基础，它管理了   &lt;strong&gt;公钥的整个生命周期&lt;/strong&gt;，其作用包括：审计用户身份的RA，发放证书，规定证书的有效期和通过发布证书废除列表确保必要时可以废除证书。&lt;/li&gt;  &lt;li&gt;   &lt;strong&gt;PKI应用&lt;/strong&gt;   &lt;br /&gt;为各种应用提供访问PKI的方式&lt;/li&gt;&lt;/ol&gt; &lt;h4&gt;  &lt;a href="https://luo41.top/#&amp;#35748;&amp;#35777;&amp;#26426;&amp;#26500;ca&amp;#31995;&amp;#32479;"&gt;&lt;/a&gt; 认证机构CA系统&lt;/h4&gt; &lt;p&gt;PKI的  &lt;strong&gt;核心是认证机构CA系统&lt;/strong&gt;。&lt;/p&gt; &lt;p&gt;CA系统：&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;权威的，可信任的，公正的第三方机构&lt;/li&gt;  &lt;li&gt;   &lt;strong&gt;认证中心&lt;/strong&gt;&lt;/li&gt;  &lt;li&gt;   &lt;strong&gt;PKI的核心&lt;/strong&gt;&lt;/li&gt;  &lt;li&gt;数字证书的   &lt;strong&gt;签发机构&lt;/strong&gt;&lt;/li&gt;  &lt;li&gt;负责证书的产生，存档，发放，查询，恢复以及作废管理&lt;/li&gt;  &lt;li&gt;一个典型的CA系统包括安全服务器，   &lt;strong&gt;注册机构RA&lt;/strong&gt;，CA服务器，LDAP目录服务器和数据库服务器等。&lt;/li&gt;&lt;/ol&gt; &lt;img src="https://luo41.top/2022/01/20/ComSec%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%AE%89%E5%85%A8OverView/2.png"&gt;&lt;/img&gt; &lt;p&gt;  &lt;code&gt;注册机构RA&lt;/code&gt;：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;   &lt;p&gt;RA是数字证书    &lt;strong&gt;注册审批&lt;/strong&gt;机构&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;RA提供用户和CA之间的一个接口，它获取并认证用户的身份，向CA    &lt;strong&gt;提出证书请求&lt;/strong&gt;，它主要完成    &lt;strong&gt;收集用户信息&lt;/strong&gt;和    &lt;strong&gt;确认用户身份的&lt;/strong&gt;功能&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;它接受用户的注册申请，审查用户的申请资格，并决定是否同意CA给其签发数字证书。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;注：    &lt;code&gt;注册机构并不给用户签发证书&lt;/code&gt;，而只是对用户进行资格审查。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;  &lt;code&gt;CA的任务&lt;/code&gt;：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;验证并标识证书申请者的身份&lt;/li&gt;  &lt;li&gt;   &lt;strong&gt;签发证书&lt;/strong&gt;&lt;/li&gt;  &lt;li&gt;证书资料信息的管理(   &lt;strong&gt;公钥证书序列号，CA标识&lt;/strong&gt;)&lt;/li&gt;  &lt;li&gt;   &lt;strong&gt;产生和发布撤销&lt;/strong&gt;证书列表&lt;/li&gt;  &lt;li&gt;证书的归档&lt;/li&gt;  &lt;li&gt;密钥归档与恢复&lt;/li&gt;  &lt;li&gt;历史数据归档&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;  &lt;code&gt;CA的职责&lt;/code&gt;：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;确保CA用于签名证书的非对称密钥的质量&lt;/li&gt;  &lt;li&gt;确保整个签证过程的安全性，   &lt;strong&gt;确保签名私钥的安全性&lt;/strong&gt;&lt;/li&gt;  &lt;li&gt;确保证书主体标识的唯一性，防止重名&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;  &lt;code&gt;CA的密钥&lt;/code&gt;：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;是整个证书机制得以运行的基础&lt;/li&gt;  &lt;li&gt;CA私钥由CA保管，必须确保   &lt;strong&gt;CA私钥的高度机密性&lt;/strong&gt;&lt;/li&gt;  &lt;li&gt;CA公钥在网上公开，必须保证   &lt;strong&gt;CA公钥的完整性&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;  &lt;code&gt;为什么需要PKI&lt;/code&gt;?&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;身份认证&lt;/strong&gt;的目的是要确定对方的身份是否是  &lt;strong&gt;真实&lt;/strong&gt;或  &lt;strong&gt;合法的&lt;/strong&gt;，为了达到这一目的，需要建立初始信任(双方已经共享了的  &lt;strong&gt;秘密&lt;/strong&gt;)，但在网络环境下，建立初始信任需要建立一条  &lt;strong&gt;安全通道&lt;/strong&gt;，这一任务在公钥密码提出之前是非常困难的，代价也很高。而公钥密码提出后，可以用公钥密码手段(  &lt;strong&gt;一个公钥，一个私钥，用公钥传递私钥&lt;/strong&gt;)传递  &lt;strong&gt;秘密&lt;/strong&gt;，如Diffie-Hellman。但  &lt;strong&gt;公钥的真实性&lt;/strong&gt;又难以保证，如何确定一个人和他所持有的公钥的关系，已经公钥是否在有效期内，签名算法是什么？所以  &lt;strong&gt;可信的第三方系统&lt;/strong&gt;被提了出来，如A与B想建立一条安全路径时，可以寻找共同信任的第三方，在可信第三方的帮助下实现A对B所拥有的  &lt;strong&gt;公钥的鉴别&lt;/strong&gt;，从而建立初始信任。&lt;/p&gt; &lt;p&gt;而PKI就是这样一个  &lt;strong&gt;第三方系统&lt;/strong&gt;，可以让可信第三方(  &lt;strong&gt;CA&lt;/strong&gt;)对用户的公钥  &lt;strong&gt;签署证书&lt;/strong&gt;，只要验证证书的合法性，就可以相信  &lt;strong&gt;公钥证书&lt;/strong&gt;中所描述的  &lt;strong&gt;关于公钥的信息&lt;/strong&gt;，如证书主体，有效期等证书域是可信的。&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://luo41.top/#&amp;#35748;&amp;#35777;&amp;#21327;&amp;#35758;"&gt;&lt;/a&gt; 认证协议&lt;/h2&gt; &lt;p&gt;实体认证的两种方式&lt;/p&gt; &lt;p&gt;  &lt;code&gt;弱认证&lt;/code&gt;：基于口令的认证&lt;/p&gt; &lt;p&gt;  &lt;code&gt;强认证&lt;/code&gt;：质询-应答协议&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#24369;&amp;#35748;&amp;#35777;"&gt;&lt;/a&gt; 弱认证&lt;/h3&gt; &lt;p&gt;用户提供一个口令，计算机验证该口令，如果输入的口令和该用户相对应的口令(  &lt;strong&gt;可能存放在数据库中&lt;/strong&gt;)对比不一致，则用户的身份得到认证，否则拒绝该口令，认证失败。&lt;/p&gt; &lt;p&gt;  &lt;code&gt;口令的脆弱性&lt;/code&gt;：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;   &lt;p&gt;字典攻击(    &lt;strong&gt;在线，离线&lt;/strong&gt;)：攻击者可以    &lt;strong&gt;穷举字典&lt;/strong&gt;的口令，猜测出特定用户的口令，但需要有两个条件：&lt;/p&gt;   &lt;ol&gt;    &lt;li&gt;口令在字典中&lt;/li&gt;    &lt;li&gt;可以判断选用的口令是否正确&lt;/li&gt;&lt;/ol&gt;   &lt;p&gt;因此要尽量避免    &lt;strong&gt;口令泄露&lt;/strong&gt;    &lt;br /&gt;    &lt;code&gt;口令又是如何泄露的呢？&lt;/code&gt;&lt;/p&gt;   &lt;ol&gt;    &lt;li&gt;     &lt;p&gt;网络明文传输&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;口令文件非法访问&lt;/p&gt;     &lt;p&gt;      &lt;code&gt;解决的办法&lt;/code&gt;：使用一个单向的hash函数将口令hash成      &lt;strong&gt;伪随机数&lt;/strong&gt;。对口令进行散列(      &lt;strong&gt;加盐&lt;/strong&gt;)&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;工作站劫持&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;用户误用&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;搭线窃听&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#24378;&amp;#35748;&amp;#35777;"&gt;&lt;/a&gt; 强认证&lt;/h3&gt; &lt;p&gt;这部分协议主要就是发送一个数，用  &lt;strong&gt;密钥&lt;/strong&gt;进行验证或解密，我个人还不是很熟悉。&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://luo41.top/#&amp;#23433;&amp;#20840;&amp;#31574;&amp;#30053;&amp;#35775;&amp;#38382;&amp;#25511;&amp;#21046;"&gt;&lt;/a&gt; 安全策略，访问控制&lt;/h2&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#23433;&amp;#20840;&amp;#31574;&amp;#30053;"&gt;&lt;/a&gt; 安全策略&lt;/h3&gt; &lt;p&gt;安全策略  &lt;strong&gt;为系统定义安全&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;安全策略将系统状态划分为两类&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;   &lt;strong&gt;授权状态&lt;/strong&gt; Authorized 系统允许进入的状态，安全的&lt;/li&gt;  &lt;li&gt;   &lt;strong&gt;非授权状态&lt;/strong&gt; Unauthorized 不安全的，如果系统进入这些状态，则违反了安全规定&lt;/li&gt;&lt;/ul&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#23433;&amp;#20840;&amp;#31995;&amp;#32479;"&gt;&lt;/a&gt; 安全系统&lt;/h3&gt; &lt;h4&gt;  &lt;a href="https://luo41.top/#&amp;#23450;&amp;#20041;&amp;#19968;"&gt;&lt;/a&gt; 定义一&lt;/h4&gt; &lt;p&gt;安全系统是一种  &lt;code&gt;始于已授权状态，但不能进入未授权状态&lt;/code&gt;的系统。&lt;/p&gt; &lt;h4&gt;  &lt;a href="https://luo41.top/#&amp;#23450;&amp;#20041;&amp;#20108;"&gt;&lt;/a&gt; 定义二&lt;/h4&gt; &lt;p&gt;如果系统进入了一个未授权状态，则称发生了一次安全破坏&lt;/p&gt; &lt;h4&gt;  &lt;a href="https://luo41.top/#&amp;#23450;&amp;#20041;&amp;#19977;"&gt;&lt;/a&gt; 定义三&lt;/h4&gt; &lt;p&gt;  &lt;strong&gt;保密性&lt;/strong&gt;：设X是实体的集合，并设I是信息，如果X中成员不能获取信息I，那么I关于X具有  &lt;strong&gt;保密性&lt;/strong&gt;。&lt;/p&gt; &lt;h4&gt;  &lt;a href="https://luo41.top/#&amp;#23450;&amp;#20041;&amp;#22235;"&gt;&lt;/a&gt; 定义四&lt;/h4&gt; &lt;p&gt;  &lt;strong&gt;完整性&lt;/strong&gt;：设X是实体的集合，并设I是某些信息或某种资源，如果X中所有成员都信任I，那么I关于X具有  &lt;strong&gt;完整性&lt;/strong&gt;。&lt;/p&gt; &lt;h4&gt;  &lt;a href="https://luo41.top/#&amp;#23450;&amp;#20041;&amp;#20116;"&gt;&lt;/a&gt; 定义五&lt;/h4&gt; &lt;p&gt;  &lt;strong&gt;可用性&lt;/strong&gt;：设X是实体的集合，并设I是一种资源，如果X中所有的成员都可以访问I，那么I关于X是具有  &lt;strong&gt;可用性&lt;/strong&gt;的。&lt;/p&gt; &lt;p&gt;以上  &lt;strong&gt;保密性&lt;/strong&gt;，  &lt;strong&gt;完整性&lt;/strong&gt;，  &lt;strong&gt;可用性&lt;/strong&gt;的定义都用到了集合的方法来表达，小结&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;   &lt;strong&gt;不可获取信息&lt;/strong&gt;(保密)&lt;/li&gt;  &lt;li&gt;   &lt;strong&gt;信任&lt;/strong&gt;(完整性)&lt;/li&gt;  &lt;li&gt;   &lt;strong&gt;访问的服务质量&lt;/strong&gt;(可用性)&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;与ISO的CIA定义的区别是：  &lt;code&gt;此处没有攻击者&lt;/code&gt;&lt;/p&gt; &lt;h4&gt;  &lt;a href="https://luo41.top/#&amp;#23450;&amp;#20041;&amp;#20845;"&gt;&lt;/a&gt; 定义六&lt;/h4&gt; &lt;p&gt;安全机制是实施安全策略的某些部分的实体或规程。&lt;/p&gt; &lt;h4&gt;  &lt;a href="https://luo41.top/#&amp;#23450;&amp;#20041;&amp;#19971;"&gt;&lt;/a&gt; 定义七&lt;/h4&gt; &lt;p&gt;安全模型是表达特定策略或策略集合的模型。&lt;/p&gt; &lt;h4&gt;  &lt;a href="https://luo41.top/#&amp;#20004;&amp;#20010;&amp;#32467;&amp;#35770;"&gt;&lt;/a&gt; 两个结论&lt;/h4&gt; &lt;ol&gt;  &lt;li&gt;计算机系统的安全性是不可判定问题&lt;/li&gt;  &lt;li&gt;判断一个程序是否为病毒是不可判定问题&lt;/li&gt;&lt;/ol&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#35775;&amp;#38382;&amp;#25511;&amp;#21046;"&gt;&lt;/a&gt; 访问控制&lt;/h3&gt; &lt;p&gt;  &lt;code&gt;&amp;quot;防止未经授权使用资源，包括防止以非授权方式使用资源&amp;quot;&lt;/code&gt;，是计算机安全的  &lt;strong&gt;核心元素&lt;/strong&gt;&lt;/p&gt; &lt;h4&gt;  &lt;a href="https://luo41.top/#&amp;#35775;&amp;#38382;&amp;#25511;&amp;#21046;&amp;#31574;&amp;#30053;"&gt;&lt;/a&gt; 访问控制策略&lt;/h4&gt; &lt;ol&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;自主访问控制&lt;/strong&gt; DAC&lt;/p&gt;   &lt;ul&gt;    &lt;li&gt;基于请求者的身份和访问规则&lt;/li&gt;    &lt;li&gt;实体可授权&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;强制访问控制&lt;/strong&gt; MAC&lt;/p&gt;   &lt;ul&gt;    &lt;li&gt;     &lt;p&gt;基于安全许可(      &lt;strong&gt;实体的能力&lt;/strong&gt;)和安全标记(      &lt;strong&gt;资源的敏感度&lt;/strong&gt;)&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;实体不可对另一实体授权&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;基于角色的访问控制&lt;/strong&gt; RBAC&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt; &lt;h4&gt;  &lt;a href="https://luo41.top/#&amp;#35775;&amp;#38382;&amp;#25511;&amp;#21046;&amp;#30340;&amp;#22522;&amp;#26412;&amp;#20803;&amp;#32032;"&gt;&lt;/a&gt; 访问控制的基本元素&lt;/h4&gt; &lt;ol&gt;  &lt;li&gt;   &lt;strong&gt;主体&lt;/strong&gt; - entity that can access objects   &lt;ul&gt;    &lt;li&gt;进程&lt;/li&gt;    &lt;li&gt;三类：owner，group，world&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;  &lt;li&gt;   &lt;strong&gt;客体&lt;/strong&gt; access controlled resource   &lt;ul&gt;    &lt;li&gt;资源&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;  &lt;li&gt;   &lt;strong&gt;访问权&lt;/strong&gt; way in which subject accessed an object   &lt;ul&gt;    &lt;li&gt;e.g read，write，execute，delete，create&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ol&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#33258;&amp;#20027;&amp;#35775;&amp;#38382;&amp;#25511;&amp;#21046;"&gt;&lt;/a&gt; 自主访问控制&lt;/h3&gt; &lt;ul&gt;  &lt;li&gt;访问控制矩阵   &lt;ul&gt;    &lt;li&gt;主体：     &lt;strong&gt;行&lt;/strong&gt;&lt;/li&gt;    &lt;li&gt;客体：     &lt;strong&gt;列&lt;/strong&gt;&lt;/li&gt;    &lt;li&gt;矩阵元素：     &lt;strong&gt;权限&lt;/strong&gt;(访问权)&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;但在一个系统中，访问控制矩阵会显得很大，因为其中有许多的矩阵元素是0(也就是主体和客体毫无关系的)，改进思路：  &lt;strong&gt;稀疏矩阵&lt;/strong&gt;，  &lt;strong&gt;可分解&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;  &lt;code&gt;访问控制列表ACL&lt;/code&gt;&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;概念上，ACL就像访问控制矩阵的列。&lt;/li&gt;  &lt;li&gt;针对每一个客体，列出   &lt;strong&gt;用户和权限&lt;/strong&gt;&lt;/li&gt;  &lt;li&gt;特点：简化，高效&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;  &lt;code&gt;能力表&lt;/code&gt;：又称能力权证，C-List&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;概念上能力表就像访问控制矩阵的行。&lt;/li&gt;  &lt;li&gt;针对用户，指示用户所能访问的   &lt;strong&gt;客体及其权限&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;  &lt;code&gt;ACL与C-List比较&lt;/code&gt;&lt;/p&gt; &lt;p&gt;给定主题，确定它能访问哪些客体，访问的权限是？&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;使用能力表更简单&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;给定客体，确定有哪些主体能够访问，访问的权限是？&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;使用访问控制表回答简单&lt;/li&gt;&lt;/ul&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#24378;&amp;#21046;&amp;#24615;&amp;#35775;&amp;#38382;&amp;#25511;&amp;#21046;"&gt;&lt;/a&gt; 强制性访问控制&lt;/h3&gt; &lt;ul&gt;  &lt;li&gt;基于安全许可(   &lt;strong&gt;实体的能力&lt;/strong&gt;)和安全标记(   &lt;strong&gt;资源的敏感度&lt;/strong&gt;)&lt;/li&gt;  &lt;li&gt;实体不可对另一实体授权&lt;/li&gt;&lt;/ul&gt; &lt;h4&gt;  &lt;a href="https://luo41.top/#blp&amp;#27169;&amp;#22411;"&gt;&lt;/a&gt; BLP模型&lt;/h4&gt; &lt;p&gt;按线性排列定义  &lt;strong&gt;安全许可&lt;/strong&gt;(clearance)，对应于军事安全中的安全许可&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;Top Secret (TS)：最高&lt;/li&gt;  &lt;li&gt;Secret (S)&lt;/li&gt;  &lt;li&gt;Confidential ©&lt;/li&gt;  &lt;li&gt;Unclassified (UC)：最低&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;主体有安全许可，客体有安全密级，有时候统称为  &lt;strong&gt;安全密级&lt;/strong&gt;&lt;/p&gt; &lt;img src="https://luo41.top/2022/01/20/ComSec%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%AE%89%E5%85%A8OverView/3.png"&gt;&lt;/img&gt; &lt;p&gt;BLP模型：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;   &lt;p&gt;防止主体读取安全密级比它的安全许可更高的客体。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;code&gt;简单安全条件&lt;/code&gt;，预备版本：S可以读O，当且仅当lo&amp;lt;=ls，且S对O具有自主性读权限&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;具有自主型读权限，意味着与自主访问控制部分相对应的S和O的访问控制矩阵条目中包含    &lt;strong&gt;一项读的权限&lt;/strong&gt;。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;*-属性(    &lt;strong&gt;星号属性&lt;/strong&gt;)，预备版本：S可以写O，当且仅当ls&amp;lt;=lo，且S对O具有自主型写权限。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;code&gt;基本安全定理&lt;/code&gt;，预备版本：设系统的的某一个    &lt;strong&gt;初始安全状态为&lt;/strong&gt;σ0\sigma_{0}σ0​，T是状态转换的集合。如果T的每个元素都遵守    &lt;strong&gt;简单安全条件&lt;/strong&gt;(预备版)和*-属性(预备版)，那么对于每个i&amp;gt;=0，状态σi\sigma_{i}σi​都是安全的。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;  &lt;code&gt;小结Bell-LaPadula模型：&lt;/code&gt;&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;简单安全条件：   &lt;strong&gt;不向上读&lt;/strong&gt;(no reads up)&lt;/li&gt;  &lt;li&gt;*-属性：   &lt;strong&gt;不向下写&lt;/strong&gt;(no writes down)&lt;/li&gt;  &lt;li&gt;基本安全定理，如果初始状态为安全的系统，且所有状态转换都满足简单安全条件和星号属性，则系统中的所有状态都是安全的。&lt;/li&gt;  &lt;li&gt;本质是   &lt;strong&gt;强制型访问控制&lt;/strong&gt;，但结合了自主型访问控制的内容。&lt;/li&gt;&lt;/ol&gt; &lt;h3&gt;  &lt;a href="https://luo41.top/#&amp;#22522;&amp;#20110;&amp;#35282;&amp;#33394;&amp;#30340;&amp;#35775;&amp;#38382;&amp;#25511;&amp;#21046;"&gt;&lt;/a&gt; 基于角色的访问控制&lt;/h3&gt; &lt;ul&gt;  &lt;li&gt;基于角色而非身份的控制&lt;/li&gt;  &lt;li&gt;基于角色授权，而不是单独给用户授权&lt;/li&gt;&lt;/ul&gt; &lt;img src="https://luo41.top/2022/01/20/ComSec%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%AE%89%E5%85%A8OverView/4.png"&gt;&lt;/img&gt; &lt;p&gt;参考资料：&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;   &lt;p&gt;bintou老师课件&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;《密码与编码学与网络安全》第七版&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>学习笔记 计算机安全</category>
      <guid isPermaLink="true">https://itindex.net/detail/62047-comsec-%E6%A6%82%E5%BF%B5</guid>
      <pubDate>Thu, 20 Jan 2022 20:11:16 CST</pubDate>
    </item>
    <item>
      <title>数学建模信息收集</title>
      <link>https://itindex.net/detail/61829-%E6%95%B0%E5%AD%A6%E5%BB%BA%E6%A8%A1-%E4%BF%A1%E6%81%AF</link>
      <description>&lt;blockquote&gt;  &lt;p&gt;其实我和大多数加入数学建模社团的人目的都一样，有些功利吧，想要通过参加数学建模竞赛整点综测分，但也有一些想要通过参加数学建模的比赛来提升数学和编程能力的想法。和其他大多数加入数学建模社团的人一样我对数学建模并不是特别了解，在我没有查阅资料之前我对数学建模只有一些模糊的认识通过数学来解决实际问题并利用编程推广证明模型（我自己的认识当然不太准确）。然后我利用了一个下午和晚上进行了信息检索收集和整理，废话不多说进入正文。&lt;/p&gt;&lt;/blockquote&gt; &lt;h2&gt;  &lt;a href="https://www.hesifan.top/#&amp;#24207;" title="&amp;#24207;"&gt;&lt;/a&gt;序&lt;/h2&gt; &lt;p&gt;本文全部资料全来自互联网，我仅对其进行整理总结。本文主要从下几个方面进行总结，什么是数学建模？参加数学建模竞赛的意义？有哪些常见的比赛？关于参赛组队相关问题？需要准备什么？其他问题的整理。由于整理收集的时间并不长，后续估计还会添加或者修改。&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#19968;&amp;#35272;&amp;#22270;" src="https://www.hesifan.top/npmimgbed/image-20211019094839613.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://www.hesifan.top/#&amp;#25968;&amp;#23398;&amp;#24314;&amp;#27169;&amp;#26159;&amp;#20160;&amp;#20040;&amp;#65311;" title="&amp;#25968;&amp;#23398;&amp;#24314;&amp;#27169;&amp;#26159;&amp;#20160;&amp;#20040;&amp;#65311;"&gt;&lt;/a&gt;数学建模是什么？&lt;/h2&gt; &lt;p&gt;先来看看百度度娘给的定义吧！数学建模，就是根据实际问题来建立数学模型，对数学模型来进行求解，然后根据结果去解决实际问题。不论是用数学方法在科技和生产领域解决哪类实际问题，还是与其它学科相结合形成交叉学科，首要的和关键的一步是建立研究对象的数学模型，并加以计算求解（通常借助计算机）；数学建模和计算机技术在知识经济时代的作用可谓是如虎添翼。引用自百度百科数学建模词条。&lt;/p&gt; &lt;p&gt;数学建模，简单来说就是使用数学符号对于某些事物进行抽象和模拟。数学建模，狭义上来讲是就是从实际问题中抽象出数学模型的过程，在广义上而言，数学建模又包括了狭义上的数学建模（我们可以理解为模型的设计过程）以及后续的计算求解与分析等。讲了一大堆，你看懂了吗？没有看懂，有关系吗？没有关系，我也没看懂（大雾）。&lt;/p&gt; &lt;p&gt;讲一句大家都听得懂的人话吧，数学建模用通俗易懂的话讲就是“1到3个人组队，从3~4个“应用题”中选出一个题，之后独立或在指导老师的指导下，在三天三夜（一般是这么长时间）的时间里，建立一个数学模型来解这道题，最后将你们的数学模型、解题思路、方法、过程以及最终结果以论文形式呈现出来（别担心，论文也有具体格式要求，按部就班的填充即可）。”&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://www.hesifan.top/#&amp;#21442;&amp;#21152;&amp;#25968;&amp;#23398;&amp;#24314;&amp;#27169;&amp;#31454;&amp;#36187;&amp;#30340;&amp;#24847;&amp;#20041;&amp;#65311;" title="&amp;#21442;&amp;#21152;&amp;#25968;&amp;#23398;&amp;#24314;&amp;#27169;&amp;#31454;&amp;#36187;&amp;#30340;&amp;#24847;&amp;#20041;&amp;#65311;"&gt;&lt;/a&gt;参加数学建模竞赛的意义？&lt;/h2&gt; &lt;p&gt;1.数学建模的获奖比率和比赛的频率高，只要掌握一定的方法，就可以获得很多奖，而比赛获奖对保研而言尤为重要&lt;/p&gt; &lt;p&gt;2.数学建模可以锻炼你的逻辑思维和团队协作能力，能让你结交一些“真朋友”。&lt;/p&gt; &lt;p&gt;3.获奖的论文稍加修改就可以向一些相关的期刊杂志进行投稿，而且中稿的可能性也会较大&lt;/p&gt; &lt;p&gt;4.建模当中使用的方法对研究生阶段写论文非常有帮助&lt;/p&gt; &lt;p&gt;5.越来越多的高校开始看重数学建模，对研究生复试也会有很大帮助。&lt;/p&gt; &lt;p&gt;6.有的企业在面试的过程中也会着重看你的建模比赛，所以对就业也有一定的帮助。&lt;/p&gt; &lt;p&gt;7.最重要的一点，数学建模性价比高，因为比赛就三天三夜，比赛所用的知识基本上就是你这三天所学习的知识，所以说，虽然过程“痛苦”一点，但是相比于创青春、三创赛之类的团队比赛而言，占用的时间还是少的多，而且获奖比率也会更高。真•一次参赛终身受益。&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://www.hesifan.top/#&amp;#25968;&amp;#23398;&amp;#24314;&amp;#27169;&amp;#30456;&amp;#20851;&amp;#36187;&amp;#20107;" title="&amp;#25968;&amp;#23398;&amp;#24314;&amp;#27169;&amp;#30456;&amp;#20851;&amp;#36187;&amp;#20107;"&gt;&lt;/a&gt;数学建模相关赛事&lt;/h2&gt; &lt;h3&gt;  &lt;a href="https://www.hesifan.top/#&amp;#22269;&amp;#23478;&amp;#32423;" title="&amp;#22269;&amp;#23478;&amp;#32423;"&gt;&lt;/a&gt;国家级&lt;/h3&gt; &lt;p&gt;​    1、   &lt;strong&gt;MCMICM&lt;/strong&gt; :美国大学生数学建模竞赛(每年2月份左右,变动大,四天,国内认可度大,国际认可度大)（在网上看到说由于19年的一个事件导致国内一些院校不再认可这个比赛）&lt;/p&gt; &lt;p&gt;​    2、  &lt;strong&gt;GUMCM&lt;/strong&gt;:全国大学生数学建模竞赛（每年九月中旬一个周五到周末三天72小时，国内认可度大，国际一般）&lt;/p&gt; &lt;p&gt;​    3、  &lt;strong&gt;GMCM&lt;/strong&gt; :研究生数学建模竞赛（本科生可以参加）(从9月24日上午8时开始,至9月28日中午12时结束国内认可度高国外一般）&lt;/p&gt; &lt;p&gt;​    4、  &lt;strong&gt;EMCM&lt;/strong&gt; :中国电机工程学(电工)杯数学建模竞赛(每年5月27日上午8时至5月30日上午8时,三天,国内认可度一般,国际一般)&lt;/p&gt; &lt;p&gt;​    5、  &lt;strong&gt;MathorCup&lt;/strong&gt;: 大学生数学建模挑战赛(每年5月27日上午8时至5月30日上午8时,三天,国内认可度低,国际认可度低)&lt;/p&gt; &lt;p&gt;​    6、   &lt;strong&gt;APMCM&lt;/strong&gt; :亚太地区大学生数学建模竞赛(每年11月17号上午八点到21号上午八点,四天,国内认可度低,国际认可度低)&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://www.hesifan.top/#&amp;#30465;&amp;#32423;&amp;#12289;&amp;#22320;&amp;#21306;&amp;#32423;" title="&amp;#30465;&amp;#32423;&amp;#12289;&amp;#22320;&amp;#21306;&amp;#32423;"&gt;&lt;/a&gt;省级、地区级&lt;/h3&gt; &lt;p&gt;1、  &lt;strong&gt;苏北赛&lt;/strong&gt;:(每年5月1号上午八点到5月4号上午八点,三天,国内认可度高,社会认可  &lt;br /&gt;度一般)  &lt;br /&gt;2、   &lt;strong&gt;TZMCM&lt;/strong&gt; :数学中国数学建模网络挑战赛(每年4月中旬,三天,国内认可度一般、  &lt;br /&gt;社会认可度高)  &lt;br /&gt;3、   &lt;strong&gt;CAMCM&lt;/strong&gt; :数学中国数学建模国际赛【俗称小美赛】(每年11月24号上午八点到11月28号上午八点,四天,国内认可度一般、社会认可度高)  &lt;br /&gt;4、  &lt;strong&gt;华东邀请赛&lt;/strong&gt;(每年5月19号到5月22号,三天,国内认可度一般、社会认可度一般).5、东北赛(每年4月18号到4月21号,三天,国内认可度一般、社会认可度一般)  &lt;br /&gt;6、  &lt;strong&gt;华中赛&lt;/strong&gt;(每年04月30日上午8点到5月3号上午八点,三天,国内认可度低、社会认可度低)&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://www.hesifan.top/#&amp;#26102;&amp;#38388;&amp;#19968;&amp;#35272;&amp;#34920;" title="&amp;#26102;&amp;#38388;&amp;#19968;&amp;#35272;&amp;#34920;"&gt;&lt;/a&gt;时间一览表&lt;/h3&gt; &lt;p&gt;（3）时间一览表  &lt;br /&gt;2月中旬: MCM / CM :美国大学生数学建模竞赛（★）&lt;/p&gt; &lt;p&gt;4月中旬: TZMCM :数学中国数学建模网络挑战赛&lt;/p&gt; &lt;p&gt;4月18号:东北三省大学生数学建模联赛&lt;/p&gt; &lt;p&gt;4月30号:华中地区大学生数学建模联赛&lt;/p&gt; &lt;p&gt;5月1号:苏北地区大学生数学建模联赛（★）&lt;/p&gt; &lt;p&gt;5月27号: EMCM :中国电机工程学(电工)杯数学建模竞赛（★）&lt;/p&gt; &lt;p&gt;5月27号: MathorCup 大学生数学建模挑战赛&lt;/p&gt; &lt;p&gt;9月中旬: CUMCM :全国大学生数学建模竞赛（★）&lt;/p&gt; &lt;p&gt;9月24号: GMCM :研究生数学建模竞赛&lt;/p&gt; &lt;p&gt;11月17号: APMCM :亚太地区大学生数学建模竞赛（★）&lt;/p&gt; &lt;p&gt;11月24号: CAMCM :数学中国数学建模国际赛&lt;/p&gt; &lt;blockquote&gt;  &lt;p&gt;★标注意味着推荐参加的比赛&lt;/p&gt;&lt;/blockquote&gt; &lt;h2&gt;  &lt;a href="https://www.hesifan.top/#&amp;#20851;&amp;#20110;&amp;#21442;&amp;#36187;&amp;#32452;&amp;#38431;&amp;#30456;&amp;#20851;&amp;#38382;&amp;#39064;&amp;#65311;" title="&amp;#20851;&amp;#20110;&amp;#21442;&amp;#36187;&amp;#32452;&amp;#38431;&amp;#30456;&amp;#20851;&amp;#38382;&amp;#39064;&amp;#65311;"&gt;&lt;/a&gt;关于参赛组队相关问题？&lt;/h2&gt; &lt;h3&gt;  &lt;a href="https://www.hesifan.top/#&amp;#20851;&amp;#20110;&amp;#32452;&amp;#38431;&amp;#30340;&amp;#19968;&amp;#20123;&amp;#35823;&amp;#21306;" title="&amp;#20851;&amp;#20110;&amp;#32452;&amp;#38431;&amp;#30340;&amp;#19968;&amp;#20123;&amp;#35823;&amp;#21306;"&gt;&lt;/a&gt;关于组队的一些误区&lt;/h3&gt; &lt;hr&gt;&lt;/hr&gt; &lt;p&gt;Q1：我现在才大一或大二，没什么人脉，要不等大三再参加吧！&lt;/p&gt; &lt;hr&gt;&lt;/hr&gt; &lt;p&gt;A1:这也是一个很可怕的想法，为什么这么说呢？&lt;/p&gt; &lt;p&gt;众所周知，数学建模一般是需要三个人组队参加，而这个所谓的“队”是怎么组建起来的呢？&lt;/p&gt; &lt;p&gt;因为你很难一开始就能组好一个队，所以最简单粗暴的方法，就是尽可能的找那些建模获奖或者参加了很多次建模的人组队，而这种队伍一般在大二时就会确定下来，而一旦确定下来之后，不出什么意外，队员就不会再改变了。&lt;/p&gt; &lt;p&gt;所以等你大三甚至大二下学期再去组队参加，你还是很难找到所谓的“大神”，获奖的可能性可想而知。所以递递建议，从大一就开始参加数学建模比赛。&lt;/p&gt; &lt;hr&gt;&lt;/hr&gt; &lt;p&gt;Q2:我是文科生，数学不好，所以不适合参加数学建模。&lt;/p&gt; &lt;hr&gt;&lt;/hr&gt; &lt;p&gt;A2:其实说实话，数学建模虽然带着“数学”二字，但是与数学还真没有非常大的关系。&lt;/p&gt; &lt;p&gt;很多时候，你需要解答的问题里面可能连一个数字都没有，而且你目前所学的数学知识（如果你不是非数学类学生）在建模的过程中基本上都用不到。&lt;/p&gt; &lt;p&gt;所以说，即便你是文科生，就算你高数不及格，也照样不影响你参加数学建模，更何况递递本身就在经管学院（偏文科）。&lt;/p&gt; &lt;hr&gt;&lt;/hr&gt; &lt;p&gt;Q3.其他学院的人我认识不多，要不我就从我们专业随便找两个人参加吧。&lt;/p&gt; &lt;hr&gt;&lt;/hr&gt; &lt;p&gt;A3:这应该是很多建模小白最初的想法，不能说错，但确实有点不妥。&lt;/p&gt; &lt;p&gt;千万不要随随便便找俩人就组队，不光建模，像其他的团队比赛也是，找人组队之前一定要有目的性，不是说关系好就可以一块参加比赛，因为比赛需要的是能力而不是关系。  &lt;br /&gt;人员配置是最重要的底牌，请尽量找到合适的队友。不要以亲疏远近为第一出发点，请尽量找有一定基础的同学。数学建模是一个“现学现卖”的过程，如果是从零开始，请务必注意其学习能力和学习态度。&lt;/p&gt; &lt;hr&gt;&lt;/hr&gt; &lt;h3&gt;  &lt;a href="https://www.hesifan.top/#&amp;#32452;&amp;#24314;&amp;#38431;&amp;#20237;" title="&amp;#32452;&amp;#24314;&amp;#38431;&amp;#20237;"&gt;&lt;/a&gt;组建队伍&lt;/h3&gt; &lt;p&gt;建模需要什么能力，你就应该知道如何组队了。&lt;/p&gt; &lt;p&gt;简单来说，要想顺利完成数学建模，一般需要三种能力，分别是：构建模型、计算机编程、论文写作。&lt;/p&gt; &lt;p&gt;而这三种能力所对应的专业学院大家应该就清楚了，最直接的就是数学学院、计算机学院、经管或文法学院(偏文科学院)。&lt;/p&gt; &lt;p&gt;所以理论上来讲，最好的队伍就应该有这三个学院的同学，但现实中却很难组建一支这种队伍，因为你连本学院认识的人都不多，更何况其他学院的呢。&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://www.hesifan.top/#&amp;#20851;&amp;#20110;&amp;#22242;&amp;#38431;&amp;#26500;&amp;#25104;&amp;#30340;&amp;#24515;&amp;#24471;" title="&amp;#20851;&amp;#20110;&amp;#22242;&amp;#38431;&amp;#26500;&amp;#25104;&amp;#30340;&amp;#24515;&amp;#24471;"&gt;&lt;/a&gt;关于团队构成的心得&lt;/h3&gt; &lt;p&gt;团队是极其重要的一个点，我觉得科学合理的团队能够让你事半功倍。首先得是目标类似的，比如都想保研加分或者都想冲击国奖等等，其次就是分工合理、专业互补，数学建模团队一般是由建模、编程和写作组成，先给自己定位，然后可以按着需求去找队友就好啦。&lt;/p&gt; &lt;p&gt;建模的人要懂撰写论文，要先把自己撰写好的”建模部分的论文”用格式工整的语言写好，然后再发给撰写论文的人，不然让写论文的人再改会很麻烦。&lt;/p&gt; &lt;p&gt;写论文的人要懂建模，知道这些公式这些模型怎么写成数学文字才能更好得让人理解。&lt;/p&gt; &lt;p&gt;编程的人要懂建模，建模的要了解编程，这样能在进行辅助计算时节省时间。&lt;/p&gt; &lt;p&gt;所以虽然有所分工，但是实际上小队成员对这三项技能（建模，编程，论文）除了专攻的方向其他也都要有所涉猎才能更有利于合作。&lt;/p&gt; &lt;blockquote&gt;  &lt;p&gt;一定要挑选好队友，否则比赛的三天可能就是你一个人建模一个人编程一个人论文，最大可能就是一个人难以坚持最后未完赛弃赛，当然如果你是大腿那当我没说。&lt;/p&gt;&lt;/blockquote&gt; &lt;h2&gt;  &lt;a href="https://www.hesifan.top/#&amp;#38656;&amp;#35201;&amp;#20934;&amp;#22791;&amp;#20160;&amp;#20040;&amp;#65311;" title="&amp;#38656;&amp;#35201;&amp;#20934;&amp;#22791;&amp;#20160;&amp;#20040;&amp;#65311;"&gt;&lt;/a&gt;需要准备什么？&lt;/h2&gt; &lt;p&gt;首先数学建模一个可以分工合作的比赛，一个小组一般由建模手编程手和写作手，三个部分所组成，虽然是分工合作，但是如果大家都对建模有一些基础认识可以达到事半功倍的效果。建模手一般也需要会一些编程知识半个编程手。  &lt;br /&gt;建模（编程）、写作、 编程。&lt;/p&gt; &lt;blockquote&gt;  &lt;p&gt;建模的人一般应该有比较扎实的数理基础，思维活跃，并且同时会编程为最佳。   &lt;br /&gt; 写作的人应该思维缜密，语言逻辑性强，要能看懂建模人建立的模型，&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;在理解模型的基础上进行写作。我个人认为  &lt;strong&gt;写作是最辛苦的&lt;/strong&gt;。&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://www.hesifan.top/#1&amp;#24314;&amp;#27169;&amp;#22522;&amp;#30784;" title="1&amp;#24314;&amp;#27169;&amp;#22522;&amp;#30784;"&gt;&lt;/a&gt;1建模基础&lt;/h3&gt; &lt;p&gt;了解建模，可以通过B站中国mooc来了解学习B站推荐教程清风数学建模，&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://www.hesifan.top/#2-&amp;#32534;&amp;#31243;&amp;#22522;&amp;#30784;" title="2.&amp;#32534;&amp;#31243;&amp;#22522;&amp;#30784;"&gt;&lt;/a&gt;2.编程基础&lt;/h3&gt; &lt;p&gt;编程基础一般就涉及到模型的计算了，Matlab是必须要掌握的语言，各种技术以及资料都相对比较齐全些。而R，SAS，SPSS这三个统计学语言三选一就足够了，但建议选择前两个，SPSS软件属于傻瓜操作。比赛的时候若有需要完全可以现学现卖。参考书籍《数学建模算法与应用（司守奎）》&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://www.hesifan.top/#3-&amp;#35770;&amp;#25991;" title="3.&amp;#35770;&amp;#25991;"&gt;&lt;/a&gt;3.论文&lt;/h3&gt; &lt;p&gt;1.多看获奖论文&lt;/p&gt; &lt;p&gt;2.平时就要制定论文框架以备比赛用&lt;/p&gt; &lt;p&gt;3.不要想着到时候比赛自己只负责论文&lt;/p&gt; &lt;p&gt;4.学习一下latex&lt;/p&gt; &lt;p&gt;5.熟练引用格式&lt;/p&gt; &lt;p&gt;6.练习摘要，练习摘要，练习摘要！&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://www.hesifan.top/#&amp;#20854;&amp;#20182;&amp;#38382;&amp;#39064;&amp;#25972;&amp;#29702;" title="&amp;#20854;&amp;#20182;&amp;#38382;&amp;#39064;&amp;#25972;&amp;#29702;"&gt;&lt;/a&gt;其他问题整理&lt;/h2&gt; &lt;hr&gt;&lt;/hr&gt; &lt;p&gt;Q1： 写作手是不是只管写作不管建模？建模手、编程手是不是不用管写作？ &lt;/p&gt; &lt;hr&gt;&lt;/hr&gt; &lt;p&gt;数学建模的比赛时间非常紧，国赛是三天，美赛是不到五天，因此建模论文没办法确保结论的准确性和推理过程的科学性，在几天时间完成正常科研大半年的工作是不现实的。所以，要明确一点，建模比赛的目的在于体会建模的乐趣，培养建模的兴趣，锻炼建模的技能。因此，建模论文的写作必须“迅猛”，写作手的工作要与建模手、编程手的工作紧密结合在一起，否则就达不到学习的目的。&lt;/p&gt; &lt;p&gt;写作不是写作手一个人的事情，而是所有队员都要参与的事情。理由很简单，如果写作手没有参与建模的讨论和建立的过程，如果建模手没能将建模的过程清晰地表达出来，如果编程手不能对算法的思想和代码的逻辑描述清楚，论文基本上完成不了。所以，每个队员都要进行写作，边做边写，整个建模的全部步骤都要保留痕迹，即使是错误的、无效的工作也尽量写一点东西，对建模结论的得出或许是无用的，但是对文章却很可能成为亮点。&lt;/p&gt; &lt;hr&gt;&lt;/hr&gt; &lt;p&gt;Q2：要不要先把问题讨论清楚然后在进行写作？&lt;/p&gt; &lt;hr&gt;&lt;/hr&gt; &lt;p&gt;A2：不要，因为我的经验告诉我，讨论过程很可能花去两天半的时间。所以，从确定选题以后，就要加紧工作，不要耗费过多时间在讨论细节问题上，不要追求在每个细节的处理上都做到完美，用最短的时间选择出合适的方法，然后马上进行建模的工作，在建模的过程中发现不妥再进行修正。这样做是避免纸上谈兵，只有在实践中才能发现问题、完善思路。即使做了很多无用功，也是不要紧的，耗费的时间不会比嘴头上讨论的时间多，这些无用功也是可以写到论文里的。&lt;/p&gt; &lt;hr&gt;&lt;/hr&gt; &lt;p&gt;Q3：在建模手、编程手都束手无措时，写作手是不是就没事干了？&lt;/p&gt; &lt;hr&gt;&lt;/hr&gt; &lt;p&gt;A3：并不是。写作手是队里从头到尾最忙的一个人，建不了模不代表写不了作。&lt;/p&gt; &lt;p&gt;比如说，一开始的选题，需要考虑不同题目要解决什么问题、属于哪个学科、所需要的知识基础、重点难点、可能用到的模型、算法、大体上的解决思路。选题过程中要对数据的特点进行分析，这时会涉及一些简单的数据处理，形成数据透视表等初等资料。这些都是可以写进论文的。&lt;/p&gt; &lt;p&gt;数学建模中若分成算手，写手，编程手，这三人的具体分工应该是什么？  &lt;br /&gt;算手：建模型的。&lt;/p&gt; &lt;p&gt;写手：写论文的。&lt;/p&gt; &lt;p&gt;编程手：写代码的。&lt;/p&gt; &lt;p&gt;这三个任务几乎就是建模的主要构成因素，如此分工听起来非常合理，但实际操作起来，特别鸡肋。&lt;/p&gt; &lt;p&gt;简单来说，写手一开始闲得蛋疼，最后累哭。算手正好倒过来。编程手如果顺利，就会一直很闲，不顺的话又会耽误整个建模进度。&lt;/p&gt; &lt;p&gt;所以还是并联操作吧，比如我们小队当初就是一人写一个模型的（正好建了三个模型）。&lt;/p&gt; &lt;p&gt;并串联结合就更好了，比如开始的时候模型（思路）可以一起想，最后论文（翻译）可以一起写。遇到问题也好互相帮助。&lt;/p&gt; &lt;p&gt;而且Abstract交给一个人完成肯定欠妥啊。&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>学习 总结 数学建模</category>
      <guid isPermaLink="true">https://itindex.net/detail/61829-%E6%95%B0%E5%AD%A6%E5%BB%BA%E6%A8%A1-%E4%BF%A1%E6%81%AF</guid>
      <pubDate>Tue, 19 Oct 2021 07:26:00 CST</pubDate>
    </item>
    <item>
      <title>Mybaits缓存机制</title>
      <link>https://itindex.net/detail/61717-mybaits-%E7%BC%93%E5%AD%98</link>
      <description>&lt;h1&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#21069;&amp;#35328;" title="&amp;#21069;&amp;#35328;"&gt;&lt;/a&gt;前言&lt;/h1&gt; &lt;p&gt;我们知道  &lt;code&gt;Mybatis&lt;/code&gt;作为常见的  &lt;code&gt;Java&lt;/code&gt;数据库访问层的  &lt;code&gt;ORM&lt;/code&gt;框架，其缓存分为一级缓存和二级缓存。&lt;/p&gt; &lt;p&gt;大多数情况下，我们使用的都是  &lt;code&gt;Mybatis&lt;/code&gt;缓存的默认配置，但是  &lt;code&gt;Mybatis&lt;/code&gt;缓存机制有一些不足之处，在使用中容易引起脏数据问题，形成一些潜在隐患。&lt;/p&gt; &lt;p&gt;今天，我们就来看下  &lt;code&gt;Mybatis&lt;/code&gt;的缓存机制，了解其底层的一些原理，来方便我们排查、解决以后可能出现的由  &lt;code&gt;Mybatis&lt;/code&gt;缓存引起的问题。&lt;/p&gt; &lt;h1&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#27491;&amp;#25991;" title="&amp;#27491;&amp;#25991;"&gt;&lt;/a&gt;正文&lt;/h1&gt; &lt;h2&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#32531;&amp;#23384;&amp;#23454;&amp;#29616;" title="&amp;#32531;&amp;#23384;&amp;#23454;&amp;#29616;"&gt;&lt;/a&gt;缓存实现&lt;/h2&gt; &lt;p&gt;我们通过  &lt;code&gt;Mybatis&lt;/code&gt;提供的  &lt;code&gt;jar&lt;/code&gt;包来看下缓存的整体结构，如下：&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;可以看到其缓存相关类位于  &lt;code&gt;cache&lt;/code&gt;包下，其中有一个  &lt;code&gt;Cache&lt;/code&gt;接口，有一个默认的实现类   &lt;code&gt;PerpetualCache&lt;/code&gt;，它是用  &lt;code&gt;HashMap&lt;/code&gt;实现的。&lt;/p&gt; &lt;p&gt;其他的实现类使用了装饰器模式，这些装饰器可以额外实现很多的功能，如回收策略、日志记录、定时刷新等等。使用装饰器模式的好处就是这些类可以互相组装，提供丰富的缓存操控能力。&lt;/p&gt; &lt;p&gt;这些缓存实现类可以总结如下表：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;th&gt;缓存实现类&lt;/th&gt;   &lt;th&gt;描述&lt;/th&gt;   &lt;th&gt;作用&lt;/th&gt;   &lt;th&gt;装饰条件&lt;/th&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;PerpetualCache&lt;/td&gt;   &lt;td&gt;基本缓存&lt;/td&gt;   &lt;td&gt;默认就是    &lt;code&gt;PerpetualCache&lt;/code&gt;，当然我们也可以自定义具备基本功能的缓存实现类，如    &lt;code&gt;RedisCache&lt;/code&gt;、    &lt;code&gt;ESCache&lt;/code&gt;等，来借助三方工具实现缓存&lt;/td&gt;   &lt;td&gt;无&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;LruCache&lt;/td&gt;   &lt;td&gt;LRU策略的缓存&lt;/td&gt;   &lt;td&gt;当缓存达到上限的时候，删除最近最少使用的对象（Least Recently Used）。默认上限    &lt;code&gt;1024&lt;/code&gt;，其底层是使用的    &lt;code&gt;LinkedHashMap&lt;/code&gt;实现的&lt;/td&gt;   &lt;td&gt;    &lt;code&gt;eviction=&amp;quot;LRU&amp;quot;&lt;/code&gt;（默认配置）&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;FifoCache&lt;/td&gt;   &lt;td&gt;FIFO策略的缓存&lt;/td&gt;   &lt;td&gt;当缓存对象达到上限时，首先删除最先入队的对象。默认上限1024，底层使用    &lt;code&gt;LinkedList&lt;/code&gt;实现。&lt;/td&gt;   &lt;td&gt;    &lt;code&gt;eviction=&amp;quot;FIFO&amp;quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;SoftCache&lt;/td&gt;   &lt;td&gt;软引用清理策略的缓存&lt;/td&gt;   &lt;td&gt;通过软引用来实现缓存，当    &lt;code&gt;JVM&lt;/code&gt;内存不足时，会自动清理掉这些缓存，基于    &lt;code&gt;SoftReference&lt;/code&gt;&lt;/td&gt;   &lt;td&gt;    &lt;code&gt;eviction=&amp;quot;SOFT&amp;quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;WeakCache&lt;/td&gt;   &lt;td&gt;弱引用清理策略的缓存&lt;/td&gt;   &lt;td&gt;通过弱引用来实现缓存，当    &lt;code&gt;JVM&lt;/code&gt;内存不足时，会自动清理掉这些缓存，基于    &lt;code&gt;WeakReference&lt;/code&gt;&lt;/td&gt;   &lt;td&gt;    &lt;code&gt;eviction=&amp;quot;WEAK&amp;quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;LoggingCache&lt;/td&gt;   &lt;td&gt;带日志功能的缓存&lt;/td&gt;   &lt;td&gt;记录部分缓存处理情况，如：输出缓存的命中率&lt;/td&gt;   &lt;td&gt;基本&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;SynchronizedCache&lt;/td&gt;   &lt;td&gt;同步缓存&lt;/td&gt;   &lt;td&gt;基于    &lt;code&gt;synchronized&lt;/code&gt;关键字实现，解决并发问题&lt;/td&gt;   &lt;td&gt;基本&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;BlockingCache&lt;/td&gt;   &lt;td&gt;阻塞缓存&lt;/td&gt;   &lt;td&gt;通过对     &lt;code&gt;get/set&lt;/code&gt;方法里加锁，保证只有一个线程操作缓存，基于    &lt;code&gt;ReentrantLock&lt;/code&gt;实现&lt;/td&gt;   &lt;td&gt;    &lt;code&gt;blocking=true&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;SerializedCache&lt;/td&gt;   &lt;td&gt;支持序列化的缓存&lt;/td&gt;   &lt;td&gt;将对象序列化后存入缓存，取出时反序列化&lt;/td&gt;   &lt;td&gt;    &lt;code&gt;readOnly=false&lt;/code&gt;（默认配置）&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;ScheduledCache&lt;/td&gt;   &lt;td&gt;定时调度的缓存&lt;/td&gt;   &lt;td&gt;在进行    &lt;code&gt;get/set/remove/getSize&lt;/code&gt;等操作前，判断缓存时间是否超过了设置的最长缓存时间，默认一小时，如果是则清空缓存。&lt;/td&gt;   &lt;td&gt;    &lt;code&gt;flushinterval&lt;/code&gt;不为空&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;TransactionalCache&lt;/td&gt;   &lt;td&gt;事务缓存&lt;/td&gt;   &lt;td&gt;在二级缓存中使用，可以一次存入多个缓存，移除多个缓存&lt;/td&gt;   &lt;td&gt;    &lt;code&gt;TransactionalCacheManager&lt;/code&gt;中用    &lt;code&gt;Map&lt;/code&gt;维护对应关系&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;对缓存实现结构有了大致了解后，我们分别来看下一级缓存和二级缓存。&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#19968;&amp;#32423;&amp;#32531;&amp;#23384;" title="&amp;#19968;&amp;#32423;&amp;#32531;&amp;#23384;"&gt;&lt;/a&gt;一级缓存&lt;/h2&gt; &lt;h3&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#20171;&amp;#32461;" title="&amp;#20171;&amp;#32461;"&gt;&lt;/a&gt;介绍&lt;/h3&gt; &lt;p&gt;在应用程序中，我们有可能在一次数据库会话中，执行多次查询条件完全相同的  &lt;code&gt;SQL&lt;/code&gt;，  &lt;code&gt;Mybatis&lt;/code&gt;提供了一级缓存的方案优化此场景，如果是相同的  &lt;code&gt;SQL&lt;/code&gt;语句，会优先命中一级缓存，避免直接对数据库进行查询，提高性能。其过程具体如下：&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;每个  &lt;code&gt;SqlSession&lt;/code&gt;中持有  &lt;code&gt;Executor&lt;/code&gt;，每个  &lt;code&gt;Executor&lt;/code&gt;中有一个  &lt;code&gt;Local Cache&lt;/code&gt;。当用户发起查询时，  &lt;code&gt;Mybaits&lt;/code&gt;根据当前执行的语句生成  &lt;code&gt;MappedStatement&lt;/code&gt;，在  &lt;code&gt;Local Cache&lt;/code&gt;中进行查询，如果缓存命中的话，直接返回结果给用户，如果缓存未命中，查询数据库，并将结果写入  &lt;code&gt;Local Cache&lt;/code&gt;，最后返回结果给用户。&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#37197;&amp;#32622;" title="&amp;#37197;&amp;#32622;"&gt;&lt;/a&gt;配置&lt;/h3&gt; &lt;p&gt;如何使用  &lt;code&gt;Mybatis&lt;/code&gt;的一级缓存呢？&lt;/p&gt; &lt;p&gt;我们只需在  &lt;code&gt;Mybatis&lt;/code&gt;配置文件  &lt;code&gt;mybatis-config.xml&lt;/code&gt;中，添加如下语句，就可以使用一级缓存了。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;&amp;lt;setting name=&amp;quot;localCacheScope&amp;quot; value=&amp;quot;SESSION&amp;quot;/&amp;gt;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;对于集成  &lt;code&gt;Mybaits&lt;/code&gt;的  &lt;code&gt;springboot&lt;/code&gt;项目，也可以直接在  &lt;code&gt;properties&lt;/code&gt;或者  &lt;code&gt;yml&lt;/code&gt; 文件里直接进行配置，如下：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;mybatis.configuration.local-cache-scope=session     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;  &lt;code&gt;value&lt;/code&gt;共有两个选项，  &lt;code&gt;SESSION&lt;/code&gt;和  &lt;code&gt;STATEMENT&lt;/code&gt;，默认是  &lt;code&gt;SESSION&lt;/code&gt;级别，即在一个  &lt;code&gt;Mybatis&lt;/code&gt;会话中执行的所有语句，都会共享这个缓存。另一种是  &lt;code&gt;STATEMENT&lt;/code&gt;级别，可以理解为缓存只对当前执行的这一个  &lt;code&gt;Statement&lt;/code&gt;有效。&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#27979;&amp;#35797;" title="&amp;#27979;&amp;#35797;"&gt;&lt;/a&gt;测试&lt;/h3&gt; &lt;p&gt;我们接下来通过实验来了解  &lt;code&gt;Mybaits&lt;/code&gt;一级缓存的效果。&lt;/p&gt; &lt;p&gt;我们首先创建表  &lt;code&gt;student&lt;/code&gt;，并向表中添加一些数据。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;CREATE TABLE `student` (     &lt;br /&gt;  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT &amp;apos;主键&amp;apos;,     &lt;br /&gt;  `stu_no` varchar(20) NOT NULL COMMENT &amp;apos;学号&amp;apos;,     &lt;br /&gt;  `stu_sex` char(1) DEFAULT NULL COMMENT &amp;apos;学生性别&amp;apos;,     &lt;br /&gt;  `stu_birthday` date DEFAULT NULL COMMENT &amp;apos;学生生日&amp;apos;,     &lt;br /&gt;  `stu_class` char(2) DEFAULT NULL COMMENT &amp;apos;学生班级&amp;apos;,     &lt;br /&gt;  `stu_name` varchar(50) DEFAULT NULL COMMENT &amp;apos;学生姓名&amp;apos;,     &lt;br /&gt;  PRIMARY KEY (`id`)     &lt;br /&gt;) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;     &lt;br /&gt;     &lt;br /&gt;INSERT INTO `student` (`id`, `stu_no`, `stu_sex`, `stu_birthday`, `stu_class`, `stu_name`) VALUES (&amp;apos;1&amp;apos;, &amp;apos;1&amp;apos;, &amp;apos;1&amp;apos;, &amp;apos;2004-03-03&amp;apos;, &amp;apos;1&amp;apos;, &amp;apos;张三&amp;apos;);     &lt;br /&gt;INSERT INTO `student` (`id`, `stu_no`, `stu_sex`, `stu_birthday`, `stu_class`, `stu_name`) VALUES (&amp;apos;2&amp;apos;, &amp;apos;2&amp;apos;, &amp;apos;0&amp;apos;, &amp;apos;2005-06-02&amp;apos;, &amp;apos;2&amp;apos;, &amp;apos;李四&amp;apos;);     &lt;br /&gt;INSERT INTO `student` (`id`, `stu_no`, `stu_sex`, `stu_birthday`, `stu_class`, `stu_name`) VALUES (&amp;apos;3&amp;apos;, &amp;apos;3&amp;apos;, &amp;apos;1&amp;apos;, &amp;apos;2003-01-07&amp;apos;, &amp;apos;3&amp;apos;, &amp;apos;王五&amp;apos;);     &lt;br /&gt;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;同时提供  &lt;code&gt;Mapper&lt;/code&gt;及  &lt;code&gt;pojo&lt;/code&gt;类，过程略。&lt;/p&gt; &lt;h4&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#27979;&amp;#35797;&amp;#19968;" title="&amp;#27979;&amp;#35797;&amp;#19968;"&gt;&lt;/a&gt;测试一&lt;/h4&gt; &lt;p&gt;我们通过单元测试，测试如下代码：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;15     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@RunWith(SpringRunner.class)     &lt;br /&gt;@SpringBootTest     &lt;br /&gt;public class DemoApplicationTests {     &lt;br /&gt;    @Autowired     &lt;br /&gt;    private SqlSessionFactory sqlSessionFactory;     &lt;br /&gt;     &lt;br /&gt;    @Test     &lt;br /&gt;    public void cacheTest() {     &lt;br /&gt;        SqlSession sqlSession = sqlSessionFactory.openSession(true);     &lt;br /&gt;        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);     &lt;br /&gt;        System.out.println(studentMapper.selectByPrimaryKey(1));     &lt;br /&gt;        System.out.println(studentMapper.selectByPrimaryKey(1));     &lt;br /&gt;        System.out.println(studentMapper.selectByPrimaryKey(1));     &lt;br /&gt;    }     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;记得开启日志的   &lt;code&gt;DEBUG&lt;/code&gt;级别。&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;可以看到三次查询，实际只有第一次真正查询了数据库，后续的查询使用的是一级缓存。&lt;/p&gt; &lt;h4&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#27979;&amp;#35797;&amp;#20108;" title="&amp;#27979;&amp;#35797;&amp;#20108;"&gt;&lt;/a&gt;测试二&lt;/h4&gt; &lt;p&gt;上述代码中，我们增加对数据库的修改操作，验证在一次数据库会话中，如果对数据库发生了修改操作，一级缓存是否失效。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@Test     &lt;br /&gt;public void cacheTest1() {     &lt;br /&gt;    SqlSession sqlSession = sqlSessionFactory.openSession(true);     &lt;br /&gt;    StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);     &lt;br /&gt;    studentMapper.selectByPrimaryKey(1);     &lt;br /&gt;    StudentModel studentModel = new StudentModel();     &lt;br /&gt;    studentModel.setStuName(&amp;quot;赵六&amp;quot;);     &lt;br /&gt;    studentModel.setStuNo(&amp;quot;4&amp;quot;);     &lt;br /&gt;    studentModel.setStuClass(&amp;quot;1&amp;quot;);     &lt;br /&gt;    studentModel.setStuSex(&amp;quot;1&amp;quot;);     &lt;br /&gt;    studentMapper.insert(studentModel);     &lt;br /&gt;    studentMapper.selectByPrimaryKey(1);     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;输出如下日志：&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;可以看到，在修改操作后执行相同的查询，  &lt;strong&gt;一级缓存失效&lt;/strong&gt;，会再次查询数据库。&lt;/p&gt; &lt;h4&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#27979;&amp;#35797;&amp;#19977;" title="&amp;#27979;&amp;#35797;&amp;#19977;"&gt;&lt;/a&gt;测试三&lt;/h4&gt; &lt;p&gt;开启两个  &lt;code&gt;SqlSession&lt;/code&gt;，在  &lt;code&gt;sqlSession1&lt;/code&gt;中查询数据，使一级缓存生效，在  &lt;code&gt;sqlSession2&lt;/code&gt;中更新数据库，验证一级缓存只在数据库会话内部共享。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;15     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@Test     &lt;br /&gt;public void cacheTest3() {     &lt;br /&gt;    SqlSession sqlSession1 = sqlSessionFactory.openSession(true);     &lt;br /&gt;    SqlSession sqlSession2 = sqlSessionFactory.openSession(true);     &lt;br /&gt;     &lt;br /&gt;    StudentMapper studentMapper1 = sqlSession1.getMapper(StudentMapper.class);     &lt;br /&gt;    StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);     &lt;br /&gt;    System.out.println(&amp;quot;studentMapper1-&amp;gt;&amp;quot;+studentMapper1.selectByPrimaryKey(1));     &lt;br /&gt;    System.out.println(&amp;quot;studentMapper1-&amp;gt;&amp;quot;+studentMapper1.selectByPrimaryKey(1));     &lt;br /&gt;     &lt;br /&gt;    studentMapper2.updateStudentName(&amp;quot;张二&amp;quot;,1);     &lt;br /&gt;     &lt;br /&gt;    System.out.println(&amp;quot;studentMapper1-&amp;gt;&amp;quot;+studentMapper1.selectByPrimaryKey(1));     &lt;br /&gt;    System.out.println(&amp;quot;studentMapper2-&amp;gt;&amp;quot;+studentMapper2.selectByPrimaryKey(1));     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;日志如下：&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;可以看到，  &lt;code&gt;sqlSession2&lt;/code&gt;更新了  &lt;code&gt;id&lt;/code&gt;为1的学生的姓名，从  &lt;code&gt;张三&lt;/code&gt;变为  &lt;code&gt;张二&lt;/code&gt;，但在  &lt;code&gt;session1&lt;/code&gt;之后的查询中，  &lt;code&gt;id&lt;/code&gt;为1的学生名字还是  &lt;code&gt;张三&lt;/code&gt;，出现了脏数据，也说明了一级缓存只在数据库会话内部共享。&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#24037;&amp;#20316;&amp;#27969;&amp;#31243;&amp;#21450;&amp;#28304;&amp;#30721;" title="&amp;#24037;&amp;#20316;&amp;#27969;&amp;#31243;&amp;#21450;&amp;#28304;&amp;#30721;"&gt;&lt;/a&gt;工作流程及源码&lt;/h3&gt; &lt;h4&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#24037;&amp;#20316;&amp;#27969;&amp;#31243;" title="&amp;#24037;&amp;#20316;&amp;#27969;&amp;#31243;"&gt;&lt;/a&gt;工作流程&lt;/h4&gt; &lt;p&gt;一级缓存执行的时序图可以用下图来表示：&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;h4&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#28304;&amp;#30721;&amp;#20998;&amp;#26512;" title="&amp;#28304;&amp;#30721;&amp;#20998;&amp;#26512;"&gt;&lt;/a&gt;源码分析&lt;/h4&gt; &lt;p&gt;下面我们来看下  &lt;code&gt;Mybaits&lt;/code&gt;查询相关的核心类和一级缓存源码，这对我们后面的二级缓存学习也有帮助。&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;SqlSession&lt;/strong&gt;：对外提供了用户和数据库之间交互需要的方法，隐藏了底层细节。默认实现类是  &lt;code&gt;DefaultSqlSession&lt;/code&gt;。&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;Executor&lt;/strong&gt;：  &lt;code&gt;SqlSession&lt;/code&gt;向用户提供操作数据库的方法，但和数据库操作有关的职责都会委托给  &lt;code&gt;Executor&lt;/code&gt;。&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;如下图所示，  &lt;code&gt;Executor&lt;/code&gt;有若干个实现类，为  &lt;code&gt;Executor&lt;/code&gt;赋予了不同的能力。&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;在一级缓存源码中，我们主要来看下  &lt;code&gt;BaseExecutor&lt;/code&gt;的内部实现。&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;BaseExecutor&lt;/strong&gt;：是一个实现了  &lt;code&gt;Executor&lt;/code&gt;接口的抽象类，定义了若干抽象方法，在执行的时候，把具体的操作委托给子类进行执行。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;     &lt;br /&gt;     &lt;br /&gt;protected abstract List&amp;lt;BatchResult&amp;gt; doFlushStatements(boolean isRollback) throws SQLException;     &lt;br /&gt;     &lt;br /&gt;protected abstract &amp;lt;E&amp;gt; List&amp;lt;E&amp;gt; doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException;     &lt;br /&gt;     &lt;br /&gt;protected abstract &amp;lt;E&amp;gt; Cursor&amp;lt;E&amp;gt; doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;在   &lt;code&gt;BaseExecutor&lt;/code&gt;中，我们可以看到   &lt;code&gt;Local Cache&lt;/code&gt;是其内部的一个成员变量，如下：&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;也就到了我们最开始所说的部分，  &lt;code&gt;Cache&lt;/code&gt;接口及其实现类。&lt;/p&gt; &lt;p&gt;总体流程如下：&lt;/p&gt; &lt;p&gt;为了执行和数据库交互，首先需要初始化  &lt;code&gt;SqlSession&lt;/code&gt;，通过  &lt;code&gt;DefaultSqlSessionFactory&lt;/code&gt;开启  &lt;code&gt;SqlSession&lt;/code&gt;：&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;可以看到会用  &lt;code&gt;Configuration&lt;/code&gt;类创建一个全新的  &lt;code&gt;Executor&lt;/code&gt;，作为  &lt;code&gt;DefaultSqlSession&lt;/code&gt; 的参数。&lt;/p&gt; &lt;p&gt;创建  &lt;code&gt;Executor&lt;/code&gt;的代码如下：&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;code&gt;SqlSession&lt;/code&gt;创建完毕后，根据  &lt;code&gt;Statement&lt;/code&gt;的不同类型，会进入  &lt;code&gt;SqlSession&lt;/code&gt;的不同方法中，如果是  &lt;code&gt;select&lt;/code&gt;语句，最后会执行到  &lt;code&gt;SqlSession&lt;/code&gt;的  &lt;code&gt;selectList&lt;/code&gt;方法：&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;code&gt;SqlSession&lt;/code&gt;把具体的查询职责委托给了  &lt;code&gt;Executor&lt;/code&gt;。如果只开启了一级缓存的话，会进入  &lt;code&gt;BaseExecutor&lt;/code&gt;的  &lt;code&gt;query&lt;/code&gt;方法。如下：&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;上述代码中，会先根据传入的参数生成  &lt;code&gt;CacheKey&lt;/code&gt;，我们来看下：&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;上述代码中，将  &lt;code&gt;MappedStatement&lt;/code&gt;的  &lt;code&gt;id&lt;/code&gt;、  &lt;code&gt;SQL&lt;/code&gt;的  &lt;code&gt;offset&lt;/code&gt;及  &lt;code&gt;limit&lt;/code&gt;、  &lt;code&gt;SQL&lt;/code&gt;本身以及  &lt;code&gt;SQL&lt;/code&gt;中的参数传入了  &lt;code&gt;CacheKey&lt;/code&gt;这个类。&lt;/p&gt; &lt;p&gt;其  &lt;code&gt;update&lt;/code&gt;方法如下：&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;它重写了  &lt;code&gt;equals&lt;/code&gt;方法，如下：&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;我们可以看到除去  &lt;code&gt;hashcode&lt;/code&gt;、  &lt;code&gt;checksum&lt;/code&gt;、  &lt;code&gt;count&lt;/code&gt;的比较外，只要  &lt;code&gt;updatelist&lt;/code&gt;中的元素一一对应相等，那么就认为  &lt;code&gt;CacheKey&lt;/code&gt;相等。&lt;/p&gt; &lt;p&gt;也就是只要两条  &lt;code&gt;SQL&lt;/code&gt;的下面五个值相同，即可以认为是相同  &lt;code&gt;SQL&lt;/code&gt;。&lt;/p&gt; &lt;blockquote&gt;  &lt;p&gt;Statement Id + Offset + Limit + Sql + Params&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;  &lt;code&gt;BaseExecutor&lt;/code&gt;的  &lt;code&gt;query&lt;/code&gt;继续往下走，如下：&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;可以看到如果一级缓存里没有，就会去数据库查询。&lt;/p&gt; &lt;p&gt;在  &lt;code&gt;queryFromDatabase&lt;/code&gt;中，会对  &lt;code&gt;localCache&lt;/code&gt;进行写入。&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;在上面  &lt;code&gt;query&lt;/code&gt;方法的最后，可以看到会判断一级缓存的级别，如果是  &lt;code&gt;STATEMENT&lt;/code&gt;级别，就会清空缓存，也就是  &lt;code&gt;STATEMENT&lt;/code&gt;级别无法共享一级缓存。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {     &lt;br /&gt;        // issue #482     &lt;br /&gt;        clearLocalCache();     &lt;br /&gt; }     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;最后，我们再来看下  &lt;code&gt;insert/update/delete&lt;/code&gt;操作，缓存会刷新的原因。&lt;/p&gt; &lt;p&gt;  &lt;code&gt;SqlSession&lt;/code&gt;找到  &lt;code&gt;insert/delete&lt;/code&gt;方法，都会走  &lt;code&gt;update&lt;/code&gt;流程。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@Override     &lt;br /&gt;public int insert(String statement, Object parameter) {     &lt;br /&gt;  return update(statement, parameter);     &lt;br /&gt;}     &lt;br /&gt;@Override     &lt;br /&gt;public int delete(String statement, Object parameter) {     &lt;br /&gt;  return update(statement, parameter);     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;我们来看下  &lt;code&gt;update&lt;/code&gt;方法，它也是委托给了  &lt;code&gt;Executor&lt;/code&gt;执行。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@Override     &lt;br /&gt;public int update(String statement, Object parameter) {     &lt;br /&gt;  try {     &lt;br /&gt;    dirty = true;     &lt;br /&gt;    MappedStatement ms = configuration.getMappedStatement(statement);     &lt;br /&gt;    return executor.update(ms, wrapCollection(parameter));     &lt;br /&gt;  } catch (Exception e) {     &lt;br /&gt;    throw ExceptionFactory.wrapException(&amp;quot;Error updating database.  Cause: &amp;quot; + e, e);     &lt;br /&gt;  } finally {     &lt;br /&gt;    ErrorContext.instance().reset();     &lt;br /&gt;  }     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;找到  &lt;code&gt;BaseExecutor&lt;/code&gt;实现，我们可以看到它每次执行都会清空  &lt;code&gt;Local Cache&lt;/code&gt;。&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;上面就是  &lt;code&gt;Mybaits&lt;/code&gt;的一级缓存的工作流程原理。&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#24635;&amp;#32467;" title="&amp;#24635;&amp;#32467;"&gt;&lt;/a&gt;总结&lt;/h3&gt; &lt;p&gt;我们来总结下一级缓存的部分内容。&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;   &lt;code&gt;Mybatis&lt;/code&gt;一级缓存的生命周期和   &lt;code&gt;SqlSession&lt;/code&gt;一致。&lt;/li&gt;  &lt;li&gt;   &lt;code&gt;Mybatis&lt;/code&gt;一级缓存结构简单，只是个没有容量限定的   &lt;code&gt;HashMap&lt;/code&gt;，在缓存功能上有所缺陷。&lt;/li&gt;  &lt;li&gt;   &lt;code&gt;Mybatis&lt;/code&gt;的一级缓存最大范围是   &lt;code&gt;SqlSession&lt;/code&gt;内部，有多个   &lt;code&gt;SqlSession&lt;/code&gt;或者分布式环境下，数据库写操作可能会引起脏数据问题，建议设定缓存级别为   &lt;code&gt;STATEMENT&lt;/code&gt;。&lt;/li&gt;&lt;/ul&gt; &lt;h2&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#20108;&amp;#32423;&amp;#32531;&amp;#23384;" title="&amp;#20108;&amp;#32423;&amp;#32531;&amp;#23384;"&gt;&lt;/a&gt;二级缓存&lt;/h2&gt; &lt;h3&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#20171;&amp;#32461;-1" title="&amp;#20171;&amp;#32461;"&gt;&lt;/a&gt;介绍&lt;/h3&gt; &lt;p&gt;上面我们说的一级缓存，其最大共享范围就是一个  &lt;code&gt;SqlSession&lt;/code&gt;的内部，如果多个  &lt;code&gt;SqlSession&lt;/code&gt;需要共享缓存，则需要使用二级缓存。&lt;/p&gt; &lt;p&gt;开启二级缓存后，会使用  &lt;code&gt;CachingExecutor&lt;/code&gt; 装饰  &lt;code&gt;Executor&lt;/code&gt;，我们可以在  &lt;code&gt;newExecutor&lt;/code&gt;方法逻辑里发现这段代码：&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;进入一级缓存的查询流程前，先在  &lt;code&gt;CachingExecutor&lt;/code&gt; 进行二级缓存的查询，具体工作流程如下。&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;二级缓存开启后，同一个  &lt;code&gt;namespace&lt;/code&gt;下所有操作语句，都影响着同一个  &lt;code&gt;Cache&lt;/code&gt;，即二级缓存被多个  &lt;code&gt;SqlSession&lt;/code&gt;共享，是一个全局变量。&lt;/p&gt; &lt;p&gt;当开启缓存后，数据的查询执行流程为：  &lt;strong&gt;二级缓存 -&amp;gt; 一级缓存 -&amp;gt; 数据库&lt;/strong&gt;。&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#37197;&amp;#32622;-1" title="&amp;#37197;&amp;#32622;"&gt;&lt;/a&gt;配置&lt;/h3&gt; &lt;p&gt;要正确使用二级缓存，需要完成如下配置。&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;   &lt;p&gt;    &lt;code&gt;Mybatis&lt;/code&gt;配置文件中开启二级缓存。&lt;/p&gt;   &lt;table&gt;    &lt;tr&gt;     &lt;td&gt;      &lt;pre&gt;1       &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;     &lt;td&gt;      &lt;pre&gt;&amp;lt;setting name=&amp;quot;cacheEnabled&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;       &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;   &lt;p&gt;或者如果使用的是    &lt;code&gt;springboot&lt;/code&gt;集成，需要如下配置。&lt;/p&gt;   &lt;table&gt;    &lt;tr&gt;     &lt;td&gt;      &lt;pre&gt;1       &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;     &lt;td&gt;      &lt;pre&gt;mybatis.configuration.cache-enabled=true       &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;在    &lt;code&gt;Mybatis&lt;/code&gt;的映射    &lt;code&gt;XML&lt;/code&gt;中配置    &lt;code&gt;cache&lt;/code&gt;或者    &lt;code&gt;cache-ref&lt;/code&gt;。&lt;/p&gt;   &lt;p&gt;    &lt;code&gt;cache&lt;/code&gt;标签用于声明这个    &lt;code&gt;namespace&lt;/code&gt;使用二级缓存，并且可以自定义配置。&lt;/p&gt;   &lt;table&gt;    &lt;tr&gt;     &lt;td&gt;      &lt;pre&gt;1       &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;     &lt;td&gt;      &lt;pre&gt;&amp;lt;cache/&amp;gt;       &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;   &lt;ul&gt;    &lt;li&gt;     &lt;strong&gt;type&lt;/strong&gt;：     &lt;code&gt;cache&lt;/code&gt;使用的类型，默认是     &lt;code&gt;PerpetualCache&lt;/code&gt;。&lt;/li&gt;    &lt;li&gt;     &lt;strong&gt;eviction&lt;/strong&gt;：定义回收的策略，常见的有     &lt;code&gt;FIFO&lt;/code&gt;、     &lt;code&gt;LRU&lt;/code&gt;。&lt;/li&gt;    &lt;li&gt;     &lt;strong&gt;flushInterval&lt;/strong&gt;：配置一定时间自动刷新缓存，单位是毫秒。&lt;/li&gt;    &lt;li&gt;     &lt;strong&gt;size&lt;/strong&gt;：最多缓存的对象个数。&lt;/li&gt;    &lt;li&gt;     &lt;strong&gt;readOnly&lt;/strong&gt;：是否只读，若配置可读写，则需要对应的实体类能够序列化。&lt;/li&gt;    &lt;li&gt;     &lt;strong&gt;blocking&lt;/strong&gt;：若缓存中找不到对应的     &lt;code&gt;key&lt;/code&gt;，是否会一直     &lt;code&gt;blocking&lt;/code&gt;，直到有对应的数据进入缓存。&lt;/li&gt;&lt;/ul&gt;   &lt;p&gt;    &lt;code&gt;cache-ref&lt;/code&gt;代表引用别的命名空间的    &lt;code&gt;Cache&lt;/code&gt;配置，两个命名空间的操作使用的是同一个    &lt;code&gt;Cache&lt;/code&gt;。&lt;/p&gt;   &lt;table&gt;    &lt;tr&gt;     &lt;td&gt;      &lt;pre&gt;1       &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;     &lt;td&gt;      &lt;pre&gt;&amp;lt;cache-ref namespace=&amp;quot;com.zwt.demo.mapper.StudentMapper&amp;quot;/&amp;gt;       &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/li&gt;&lt;/ul&gt; &lt;h3&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#27979;&amp;#35797;-1" title="&amp;#27979;&amp;#35797;"&gt;&lt;/a&gt;测试&lt;/h3&gt; &lt;p&gt;我们来看几个二级缓存的测试，来了解下二级缓存的一些特点。&lt;/p&gt; &lt;h4&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#27979;&amp;#35797;&amp;#19968;-1" title="&amp;#27979;&amp;#35797;&amp;#19968;"&gt;&lt;/a&gt;测试一&lt;/h4&gt; &lt;p&gt;测试二级缓存效果，不提交事务，  &lt;code&gt;sqlSession1&lt;/code&gt;查询完数据后，  &lt;code&gt;sqlSession2&lt;/code&gt;相同的查询是否会从缓存中获取数据。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@Test     &lt;br /&gt;public void cacheTest4() {     &lt;br /&gt;    SqlSession sqlSession1 = sqlSessionFactory.openSession(true);     &lt;br /&gt;    SqlSession sqlSession2 = sqlSessionFactory.openSession(true);     &lt;br /&gt;     &lt;br /&gt;    StudentMapper studentMapper1 = sqlSession1.getMapper(StudentMapper.class);     &lt;br /&gt;    StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);     &lt;br /&gt;    System.out.println(&amp;quot;studentMapper1-&amp;gt;&amp;quot;+studentMapper1.selectByPrimaryKey(1));     &lt;br /&gt;    System.out.println(&amp;quot;studentMapper2-&amp;gt;&amp;quot;+studentMapper2.selectByPrimaryKey(1));     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;可以看到，当  &lt;code&gt;sqlSession&lt;/code&gt;没有调用  &lt;code&gt;commit()&lt;/code&gt;方法时，二级缓存并没有起到作用。&lt;/p&gt; &lt;h4&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#27979;&amp;#35797;&amp;#20108;-1" title="&amp;#27979;&amp;#35797;&amp;#20108;"&gt;&lt;/a&gt;测试二&lt;/h4&gt; &lt;p&gt;这回我们测试当提交事务后，二级缓存是否可以起到作用。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@Test     &lt;br /&gt;public void cacheTest5() {     &lt;br /&gt;    SqlSession sqlSession1 = sqlSessionFactory.openSession(true);     &lt;br /&gt;    SqlSession sqlSession2 = sqlSessionFactory.openSession(true);     &lt;br /&gt;     &lt;br /&gt;    StudentMapper studentMapper1 = sqlSession1.getMapper(StudentMapper.class);     &lt;br /&gt;    StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);     &lt;br /&gt;    System.out.println(&amp;quot;studentMapper1-&amp;gt;&amp;quot;+studentMapper1.selectByPrimaryKey(1));     &lt;br /&gt;    sqlSession1.commit();     &lt;br /&gt;    System.out.println(&amp;quot;studentMapper2-&amp;gt;&amp;quot;+studentMapper2.selectByPrimaryKey(1));     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;可以看到只进行了一次查询，  &lt;code&gt;sqlSession2&lt;/code&gt;的查询使用了缓存，缓存命中率为0.5。&lt;/p&gt; &lt;p&gt;PS：为什么是0.5？ 因为进行了两次查询，第一次未命中，查询数据库，第二次命中缓存，故命中率是 1/2 = 0.5。&lt;/p&gt; &lt;h4&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#27979;&amp;#35797;&amp;#19977;-1" title="&amp;#27979;&amp;#35797;&amp;#19977;"&gt;&lt;/a&gt;测试三&lt;/h4&gt; &lt;p&gt;测试  &lt;code&gt;update&lt;/code&gt;操作是否会刷新该  &lt;code&gt;namespace&lt;/code&gt;下的二级缓存。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;15     &lt;br /&gt;16     &lt;br /&gt;17     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@Test     &lt;br /&gt;public void cacheTest6() {     &lt;br /&gt;    SqlSession sqlSession1 = sqlSessionFactory.openSession(true);     &lt;br /&gt;    SqlSession sqlSession2 = sqlSessionFactory.openSession(true);     &lt;br /&gt;    SqlSession sqlSession3 = sqlSessionFactory.openSession(true);     &lt;br /&gt;     &lt;br /&gt;    StudentMapper studentMapper1 = sqlSession1.getMapper(StudentMapper.class);     &lt;br /&gt;    StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);     &lt;br /&gt;    StudentMapper studentMapper3 = sqlSession3.getMapper(StudentMapper.class);     &lt;br /&gt;     &lt;br /&gt;    System.out.println(&amp;quot;studentMapper1-&amp;gt;&amp;quot;+studentMapper1.selectByPrimaryKey(1));     &lt;br /&gt;    sqlSession1.commit();     &lt;br /&gt;    System.out.println(&amp;quot;studentMapper2-&amp;gt;&amp;quot;+studentMapper2.selectByPrimaryKey(1));     &lt;br /&gt;    studentMapper3.updateStudentName(&amp;quot;张二二&amp;quot;,1);     &lt;br /&gt;    sqlSession3.commit();     &lt;br /&gt;    System.out.println(&amp;quot;studentMapper2-&amp;gt;&amp;quot;+studentMapper2.selectByPrimaryKey(1));     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;可以看到，  &lt;code&gt;sqlSession3&lt;/code&gt;更新数据库，提交事务后，  &lt;code&gt;sqlSession2&lt;/code&gt;后面的查询走了数据库，没有走  &lt;code&gt;Cache&lt;/code&gt;。&lt;/p&gt; &lt;h4&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#27979;&amp;#35797;&amp;#22235;" title="&amp;#27979;&amp;#35797;&amp;#22235;"&gt;&lt;/a&gt;测试四&lt;/h4&gt; &lt;p&gt;验证  &lt;code&gt;Mybatis&lt;/code&gt;二级缓存不适用用映射文件中存在多表查询的情况。&lt;/p&gt; &lt;p&gt;通常我们会为每个单表创建单独的映射文件，由于  &lt;code&gt;Mybatis&lt;/code&gt;的二级缓存是基于  &lt;code&gt;namespace&lt;/code&gt;的，多表查询语句所在的  &lt;code&gt;namespace&lt;/code&gt;无法感应到其他  &lt;code&gt;namespace&lt;/code&gt;中的语句对多表查询中涉及的表进行的修改，引发脏数据问题。&lt;/p&gt; &lt;p&gt;这儿我们在引入一张表，  &lt;code&gt;score&lt;/code&gt;学生分数表，并创建几条数据。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;CREATE TABLE `score` (     &lt;br /&gt;  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT &amp;apos;主键&amp;apos;,     &lt;br /&gt;  `stu_no` varchar(20) NOT NULL COMMENT &amp;apos;学号&amp;apos;,     &lt;br /&gt;  `cou_no` varchar(20) NOT NULL COMMENT &amp;apos;课程号&amp;apos;,     &lt;br /&gt;  `score` decimal(10,0) DEFAULT NULL COMMENT &amp;apos;分数&amp;apos;,     &lt;br /&gt;  PRIMARY KEY (`id`),     &lt;br /&gt;  UNIQUE KEY `uq_stu_cou` (`stu_no`,`cou_no`) USING BTREE     &lt;br /&gt;) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;     &lt;br /&gt;     &lt;br /&gt;INSERT INTO `score` (`id`, `stu_no`, `cou_no`, `score`) VALUES (&amp;apos;1&amp;apos;, &amp;apos;1&amp;apos;, &amp;apos;A&amp;apos;, &amp;apos;80&amp;apos;);     &lt;br /&gt;INSERT INTO `score` (`id`, `stu_no`, `cou_no`, `score`) VALUES (&amp;apos;2&amp;apos;, &amp;apos;2&amp;apos;, &amp;apos;B&amp;apos;, &amp;apos;90&amp;apos;);     &lt;br /&gt;INSERT INTO `score` (`id`, `stu_no`, `cou_no`, `score`) VALUES (&amp;apos;3&amp;apos;, &amp;apos;2&amp;apos;, &amp;apos;A&amp;apos;, &amp;apos;70&amp;apos;);     &lt;br /&gt;INSERT INTO `score` (`id`, `stu_no`, `cou_no`, `score`) VALUES (&amp;apos;4&amp;apos;, &amp;apos;3&amp;apos;, &amp;apos;A&amp;apos;, &amp;apos;60&amp;apos;);     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;涉及到的   &lt;code&gt;ScoreMapper&lt;/code&gt;不再附上。&lt;/p&gt; &lt;p&gt;我们假设在  &lt;code&gt;StudentMapper&lt;/code&gt;里有个根据学生  &lt;code&gt;id&lt;/code&gt;和课程号查询学生分数的方法，  &lt;code&gt;ScoreMapper&lt;/code&gt;里有个更新学生分数的方法。&lt;/p&gt; &lt;p&gt;  &lt;code&gt;StudentMapper&lt;/code&gt;中获取学生分数：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;int selectStudentScore(@Param(&amp;quot;stuNo&amp;quot;) String stuNo,@Param(&amp;quot;couNo&amp;quot;) String couNo);     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;&amp;lt;select id=&amp;quot;selectStudentScore&amp;quot; resultType=&amp;quot;java.lang.Integer&amp;quot;&amp;gt;     &lt;br /&gt;  select c.score from student s left join score c on s.stu_no = c.stu_no where s.stu_no = #{stuNo} and c.cou_no = #{couNo};     &lt;br /&gt;&amp;lt;/select&amp;gt;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;  &lt;code&gt;ScoreMapper&lt;/code&gt;更新分数：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;int updateScore(@Param(&amp;quot;stuNo&amp;quot;) String stuNo,@Param(&amp;quot;couNo&amp;quot;) String couNo,@Param(&amp;quot;score&amp;quot;)Integer score);     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;&amp;lt;update id=&amp;quot;updateScore&amp;quot;&amp;gt;     &lt;br /&gt;   update score set score = #{score} where stu_no = #{stuNo} and cou_no = #{couNo}     &lt;br /&gt;&amp;lt;/update&amp;gt;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;代码如下：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;15     &lt;br /&gt;16     &lt;br /&gt;17     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@Test     &lt;br /&gt;public void cacheTest7() {     &lt;br /&gt;    SqlSession sqlSession1 = sqlSessionFactory.openSession(true);     &lt;br /&gt;    SqlSession sqlSession2 = sqlSessionFactory.openSession(true);     &lt;br /&gt;    SqlSession sqlSession3 = sqlSessionFactory.openSession(true);     &lt;br /&gt;     &lt;br /&gt;    StudentMapper studentMapper1 = sqlSession1.getMapper(StudentMapper.class);     &lt;br /&gt;    StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);     &lt;br /&gt;    ScoreMapper scoreMapper = sqlSession3.getMapper(ScoreMapper.class);     &lt;br /&gt;     &lt;br /&gt;    System.out.println(&amp;quot;studentMapper1-&amp;gt;&amp;quot;+studentMapper1.selectStudentScore(&amp;quot;1&amp;quot;,&amp;quot;A&amp;quot;));     &lt;br /&gt;    sqlSession1.close();     &lt;br /&gt;    System.out.println(&amp;quot;studentMapper2-&amp;gt;&amp;quot;+studentMapper2.selectStudentScore(&amp;quot;1&amp;quot;,&amp;quot;A&amp;quot;));     &lt;br /&gt;    scoreMapper.updateScore(&amp;quot;1&amp;quot;,&amp;quot;A&amp;quot;,99);     &lt;br /&gt;    sqlSession3.commit();     &lt;br /&gt;    System.out.println(&amp;quot;studentMapper2-&amp;gt;&amp;quot;+studentMapper2.selectStudentScore(&amp;quot;1&amp;quot;,&amp;quot;A&amp;quot;));     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;可以看到，  &lt;code&gt;sqlSession1&lt;/code&gt; 的   &lt;code&gt;studentMapper1&lt;/code&gt; 查询数据库后，二级缓存生效。其保存在  &lt;code&gt;StudentMapper&lt;/code&gt; 的  &lt;code&gt;namespace&lt;/code&gt;的  &lt;code&gt;cache&lt;/code&gt;中。当  &lt;code&gt;sqlSession3&lt;/code&gt;的  &lt;code&gt;scoreMapper&lt;/code&gt;更新数据时，其方法不属于  &lt;code&gt;StudentMapper&lt;/code&gt; 的  &lt;code&gt;namespace&lt;/code&gt;，无法感应到缓存变化，因此读取了脏数据。&lt;/p&gt; &lt;h4&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#27979;&amp;#35797;&amp;#20116;" title="&amp;#27979;&amp;#35797;&amp;#20116;"&gt;&lt;/a&gt;测试五&lt;/h4&gt; &lt;p&gt;为了解决测试四出现的问题，我们可以使用  &lt;code&gt;cache-ref&lt;/code&gt;，让  &lt;code&gt;ScoreMapper&lt;/code&gt;引用  &lt;code&gt;StudentMapper&lt;/code&gt;的命名空间，这样两个映射文件对应的  &lt;code&gt;SQL&lt;/code&gt;操作就会使用同一块缓存。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;......     &lt;br /&gt;&amp;lt;mapper namespace=&amp;quot;com.zwt.demo.mapper.ScoreMapper&amp;quot; &amp;gt;     &lt;br /&gt;......     &lt;br /&gt;    &amp;lt;cache-ref namespace=&amp;quot;com.zwt.demo.mapper.StudentMapper&amp;quot;&amp;gt;&amp;lt;/cache-ref&amp;gt;     &lt;br /&gt;&amp;lt;/mapper&amp;gt;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;继续执行测试四代码：&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;可以看到，更新后，会重新进行查询，得到99的结果。&lt;/p&gt; &lt;p&gt;PS：不过这样做的后果是，缓存的粒度变粗了，多个  &lt;code&gt;Mapper namespace&lt;/code&gt;下的所有操作都会对缓存的使用造成影响。&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#28304;&amp;#30721;&amp;#20998;&amp;#26512;-1" title="&amp;#28304;&amp;#30721;&amp;#20998;&amp;#26512;"&gt;&lt;/a&gt;源码分析&lt;/h3&gt; &lt;p&gt;  &lt;code&gt;Mybatis&lt;/code&gt;二级缓存的工作流程和上面提到的一级缓存类似，只是在一级缓存处理前，用  &lt;code&gt;CachingExecutor&lt;/code&gt;装饰了  &lt;code&gt;BaseExecutor&lt;/code&gt;的子类，在委托具体职责给  &lt;code&gt;delegate&lt;/code&gt;之前，实现了二级缓存的查询和写入功能。&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;我们从  &lt;code&gt;CachingExecutor&lt;/code&gt;的  &lt;code&gt;query&lt;/code&gt;方法看起，来了解下二级缓存的一些内容。&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;code&gt;CachingExecutor&lt;/code&gt;的  &lt;code&gt;query&lt;/code&gt;方法，首先会从  &lt;code&gt;MappedStatement&lt;/code&gt;中获得在配置初始化时赋予的  &lt;code&gt;Cache&lt;/code&gt;。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;Cache cache = ms.getCache();     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;其也是装饰器模式的使用，装饰链如下：&lt;/p&gt; &lt;blockquote&gt;  &lt;table&gt;   &lt;tr&gt;    &lt;td&gt;     &lt;pre&gt;1      &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;    &lt;td&gt;     &lt;pre&gt;SynchronizedCache -&amp;gt; LoggingCache -&amp;gt; SerializedCache -&amp;gt; LruCache -&amp;gt; PerpetualCache      &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/blockquote&gt; &lt;p&gt;这些缓存的具体能力我们上面都有讲到。&lt;/p&gt; &lt;p&gt;然后判断是否需要刷新缓存。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;flushCacheIfRequired(ms);     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;默认设置中  &lt;code&gt;SELECT&lt;/code&gt;语句不会刷新，  &lt;code&gt;INSERT/UPDATE/DELETE&lt;/code&gt;语句会刷新缓存。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;private void flushCacheIfRequired(MappedStatement ms) {     &lt;br /&gt;    Cache cache = ms.getCache();     &lt;br /&gt;    if (cache != null &amp;amp;&amp;amp; ms.isFlushCacheRequired()) {           &lt;br /&gt;      tcm.clear(cache);     &lt;br /&gt;    }     &lt;br /&gt;  }     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;上述代码中我们可以看到  &lt;code&gt;tcm&lt;/code&gt;，它是  &lt;code&gt;TransactionalCacheManager&lt;/code&gt;。&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;底层是由  &lt;code&gt;TransactionalCache&lt;/code&gt; 实现的，作用就是如果事务提交，对缓存的操作才会生效，如果事务回滚或者不提交事务，则不会对缓存产生影响。&lt;/p&gt; &lt;p&gt;在  &lt;code&gt;TransactionalCache&lt;/code&gt;中的  &lt;code&gt;clear&lt;/code&gt;方法，清空了需要在提交时加入缓存的列表，同时设定提交时清空缓存。有以下代码。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@Override     &lt;br /&gt;public void clear() {     &lt;br /&gt;  clearOnCommit = true;     &lt;br /&gt;  entriesToAddOnCommit.clear();     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;返回到  &lt;code&gt;CachingExecutor&lt;/code&gt;，我们继续向下看，  &lt;code&gt;ensureNoOutParams&lt;/code&gt; 方法用来处理存储过程的，暂时不用考虑。&lt;/p&gt; &lt;p&gt;之后会尝试从  &lt;code&gt;tcm&lt;/code&gt;中获取缓存列表。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;List&amp;lt;E&amp;gt; list = (List&amp;lt;E&amp;gt;) tcm.getObject(cache, key);     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;  &lt;code&gt;getObject&lt;/code&gt;方法中，可以看到未命中会把  &lt;code&gt;key&lt;/code&gt;加入Miss集合，这个主要是为了统计命中率。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@Override     &lt;br /&gt;public Object getObject(Object key) {     &lt;br /&gt;    // issue #116     &lt;br /&gt;    Object object = delegate.getObject(key);     &lt;br /&gt;    if (object == null) {     &lt;br /&gt;        entriesMissedInCache.add(key);     &lt;br /&gt;    }     &lt;br /&gt;    // issue #146     &lt;br /&gt;    if (clearOnCommit) {     &lt;br /&gt;        return null;     &lt;br /&gt;    } else {     &lt;br /&gt;        return object;     &lt;br /&gt;    }     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;返回到  &lt;code&gt;CachingExecutor&lt;/code&gt;，我们继续向下看，如果查询到数据，则调用  &lt;code&gt;putObject&lt;/code&gt;方法，向缓存中放入值。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;if (list == null) {     &lt;br /&gt;    list = delegate.&amp;lt;E&amp;gt; query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);     &lt;br /&gt;    tcm.putObject(cache, key, list); // issue #578 and #116     &lt;br /&gt; }     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;  &lt;code&gt;putObject&lt;/code&gt;方法最终调用  &lt;code&gt;tcm.put&lt;/code&gt;方法。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@Override     &lt;br /&gt;public void putObject(Object key, Object object) {     &lt;br /&gt;  entriesToAddOnCommit.put(key, object);     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;这儿是将数据放入待提交的  &lt;code&gt;Map&lt;/code&gt;中，而不是直接操作缓存。&lt;/p&gt; &lt;p&gt;上面代码我们可以看到，如果不调用  &lt;code&gt;commit&lt;/code&gt;方法的话，由于  &lt;code&gt;TransactionalCache&lt;/code&gt; 的作用，不会对二级缓存造成直接影响。因此我们来看看  &lt;code&gt;SqlSession&lt;/code&gt;的  &lt;code&gt;commit&lt;/code&gt;方法中做了什么。&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;因为我们使用的是  &lt;code&gt;CachingExecutor&lt;/code&gt;，因此我们来看下其  &lt;code&gt;commit&lt;/code&gt;方法。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@Override     &lt;br /&gt;public void commit(boolean required) throws SQLException {     &lt;br /&gt;  delegate.commit(required);     &lt;br /&gt;  tcm.commit();     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;会把具体  &lt;code&gt;commit&lt;/code&gt;的职责委托给包装的  &lt;code&gt;Executor&lt;/code&gt;。而后  &lt;code&gt;tcm.commit&lt;/code&gt;方法如下：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;public void commit() {     &lt;br /&gt;    for (TransactionalCache txCache : transactionalCaches.values()) {     &lt;br /&gt;        txCache.commit();     &lt;br /&gt;    }     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;最终调用了  &lt;code&gt;TransactionalCache&lt;/code&gt;。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;public void commit() {     &lt;br /&gt;    if (clearOnCommit) {     &lt;br /&gt;        delegate.clear();     &lt;br /&gt;    }     &lt;br /&gt;    flushPendingEntries();     &lt;br /&gt;    reset();     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;可以看到真正的清理  &lt;code&gt;Cache&lt;/code&gt;实在这里进行的。具体的清理职责委托给了包装的  &lt;code&gt;Cache&lt;/code&gt;类。&lt;/p&gt; &lt;p&gt;之后进入到  &lt;code&gt;flushPendingEntries()&lt;/code&gt;方法，代码如下：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;private void flushPendingEntries() {     &lt;br /&gt;    for (Map.Entry&amp;lt;Object, Object&amp;gt; entry : entriesToAddOnCommit.entrySet()) {     &lt;br /&gt;        delegate.putObject(entry.getKey(), entry.getValue());     &lt;br /&gt;    }     &lt;br /&gt;    for (Object entry : entriesMissedInCache) {     &lt;br /&gt;        if (!entriesToAddOnCommit.containsKey(entry)) {     &lt;br /&gt;            delegate.putObject(entry, null);     &lt;br /&gt;        }     &lt;br /&gt;    }     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;可以看到在   &lt;code&gt;flushPendingEntries&lt;/code&gt;中，会将待提交的  &lt;code&gt;Map&lt;/code&gt;进行循环处理，委托给包装的  &lt;code&gt;Cache&lt;/code&gt;类，进行  &lt;code&gt;putObject&lt;/code&gt;操作。&lt;/p&gt; &lt;p&gt;后续的查询操作会重复这套流程。&lt;/p&gt; &lt;p&gt;如果是  &lt;code&gt;INSERT/UPDATE/DELETE&lt;/code&gt;语句的话，会统一进入  &lt;code&gt;CachingExecutor&lt;/code&gt;的  &lt;code&gt;update&lt;/code&gt;方法。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@Override     &lt;br /&gt;public int update(MappedStatement ms, Object parameterObject) throws SQLException {     &lt;br /&gt;    flushCacheIfRequired(ms);     &lt;br /&gt;    return delegate.update(ms, parameterObject);     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;  &lt;code&gt;flushCacheIfRequired&lt;/code&gt;我们上面有提到过。&lt;/p&gt; &lt;p&gt;在二级缓存执行流程后就会进入一级缓存的执行流程，这儿我们就不过多介绍了。&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#24635;&amp;#32467;-1" title="&amp;#24635;&amp;#32467;"&gt;&lt;/a&gt;总结&lt;/h3&gt; &lt;ul&gt;  &lt;li&gt;   &lt;code&gt;Mybatis&lt;/code&gt;的二级缓存相对于一级缓存来说，实现了   &lt;code&gt;SqlSession&lt;/code&gt;之间缓存数据的共享，同时粒度更加细致，能够到   &lt;code&gt;namespace&lt;/code&gt;级别，通过   &lt;code&gt;Cache&lt;/code&gt;接口实现不同的组合，对   &lt;code&gt;Cache&lt;/code&gt;的可控性也更强。&lt;/li&gt;  &lt;li&gt;二级缓存开启状态下，   &lt;code&gt;Mybatis&lt;/code&gt;在多表查询时，很有可能会出现脏数据，有设计上的缺陷，安全使用二级缓存条件比较苛刻。&lt;/li&gt;  &lt;li&gt;在分布式环境下，由于默认的   &lt;code&gt;Mybatis Cache&lt;/code&gt;实现都是基于本地的，因此也很可能出现脏数据问题，此时可以使用集中式缓存将   &lt;code&gt;Mybaits&lt;/code&gt;的   &lt;code&gt;Cache&lt;/code&gt;接口实现，但会有一定的开发成本，直接使用   &lt;code&gt;Redis&lt;/code&gt;等分布式缓存成本可能更低，安全性也更好。&lt;/li&gt;&lt;/ul&gt; &lt;h1&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#33258;&amp;#23450;&amp;#20041;&amp;#32531;&amp;#23384;&amp;#23454;&amp;#29616;" title="&amp;#33258;&amp;#23450;&amp;#20041;&amp;#32531;&amp;#23384;&amp;#23454;&amp;#29616;"&gt;&lt;/a&gt;自定义缓存实现&lt;/h1&gt; &lt;p&gt;我们在正文的缓存实现里说到，我们可以借助一些缓存工具来实现  &lt;code&gt;Mybatis&lt;/code&gt;的自定义缓存，这儿我们使用  &lt;code&gt;Redis&lt;/code&gt;来举例。&lt;/p&gt; &lt;p&gt;首先需要引入  &lt;code&gt;Redis&lt;/code&gt;，我的项目配置如下：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;15     &lt;br /&gt;16     &lt;br /&gt;17     &lt;br /&gt;18     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver     &lt;br /&gt;spring.datasource.url=jdbc:mysql://localhost:3306/test1?characterEncoding=utf8&amp;amp;useSSL=false&amp;amp;serverTimezone=GMT%2B8     &lt;br /&gt;spring.datasource.username=root     &lt;br /&gt;spring.datasource.password=123456     &lt;br /&gt;     &lt;br /&gt;mybatis.mapper-locations=classpath:sqlmap/mapper/*.xml     &lt;br /&gt;mybatis.type-aliases-package=com.zwt.demo.model     &lt;br /&gt;mybatis.configuration.local-cache-scope=session     &lt;br /&gt;mybatis.configuration.cache-enabled=true     &lt;br /&gt;     &lt;br /&gt;spring.redis.host=127.0.0.1     &lt;br /&gt;spring.redis.database=0     &lt;br /&gt;spring.redis.port=6379     &lt;br /&gt;spring.redis.timeout=3600ms     &lt;br /&gt;spring.redis.jedis.pool.max-active=8     &lt;br /&gt;spring.redis.jedis.pool.max-wait=-1ms     &lt;br /&gt;spring.redis.jedis.pool.max-idle=8     &lt;br /&gt;spring.redis.jedis.pool.min-idle=0     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;其项目结构如下：&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;可以看到在  &lt;code&gt;cache&lt;/code&gt;包下的内容就是我对于  &lt;code&gt;Mybatis&lt;/code&gt;二级缓存的自定义实现。&lt;/p&gt; &lt;p&gt;主要有两个类：  &lt;code&gt;RedisCache&lt;/code&gt;和  &lt;code&gt;RedisCacheHelp&lt;/code&gt;，  &lt;code&gt;RedisCache1&lt;/code&gt;是  &lt;code&gt;RedisCache&lt;/code&gt;的另一种实现。&lt;/p&gt; &lt;p&gt;我们来看下，首先看下  &lt;code&gt;RedisCache&lt;/code&gt;，代码如下：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;15     &lt;br /&gt;16     &lt;br /&gt;17     &lt;br /&gt;18     &lt;br /&gt;19     &lt;br /&gt;20     &lt;br /&gt;21     &lt;br /&gt;22     &lt;br /&gt;23     &lt;br /&gt;24     &lt;br /&gt;25     &lt;br /&gt;26     &lt;br /&gt;27     &lt;br /&gt;28     &lt;br /&gt;29     &lt;br /&gt;30     &lt;br /&gt;31     &lt;br /&gt;32     &lt;br /&gt;33     &lt;br /&gt;34     &lt;br /&gt;35     &lt;br /&gt;36     &lt;br /&gt;37     &lt;br /&gt;38     &lt;br /&gt;39     &lt;br /&gt;40     &lt;br /&gt;41     &lt;br /&gt;42     &lt;br /&gt;43     &lt;br /&gt;44     &lt;br /&gt;45     &lt;br /&gt;46     &lt;br /&gt;47     &lt;br /&gt;48     &lt;br /&gt;49     &lt;br /&gt;50     &lt;br /&gt;51     &lt;br /&gt;52     &lt;br /&gt;53     &lt;br /&gt;54     &lt;br /&gt;55     &lt;br /&gt;56     &lt;br /&gt;57     &lt;br /&gt;58     &lt;br /&gt;59     &lt;br /&gt;60     &lt;br /&gt;61     &lt;br /&gt;62     &lt;br /&gt;63     &lt;br /&gt;64     &lt;br /&gt;65     &lt;br /&gt;66     &lt;br /&gt;67     &lt;br /&gt;68     &lt;br /&gt;69     &lt;br /&gt;70     &lt;br /&gt;71     &lt;br /&gt;72     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;public class RedisCache implements Cache{     &lt;br /&gt;     &lt;br /&gt;    private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);     &lt;br /&gt;     &lt;br /&gt;    private static RedisTemplate&amp;lt;String,Object&amp;gt; redisTemplate;     &lt;br /&gt;     &lt;br /&gt;    private final String id;     &lt;br /&gt;     &lt;br /&gt;    private final String HASH_KEY = &amp;quot;mybatis_redis_cache&amp;quot;;     &lt;br /&gt;     &lt;br /&gt;    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();     &lt;br /&gt;     &lt;br /&gt;    @Override     &lt;br /&gt;    public ReadWriteLock getReadWriteLock(){     &lt;br /&gt;        return this.readWriteLock;     &lt;br /&gt;    }     &lt;br /&gt;     &lt;br /&gt;    public static void setRedisTemplate(RedisTemplate redisTemplate){     &lt;br /&gt;        RedisCache.redisTemplate = redisTemplate;     &lt;br /&gt;    }     &lt;br /&gt;     &lt;br /&gt;    public RedisCache(final String id){     &lt;br /&gt;        if (id == null) {     &lt;br /&gt;            throw new IllegalArgumentException(&amp;quot;需要指定id&amp;quot;);     &lt;br /&gt;        }     &lt;br /&gt;        this.id = id;     &lt;br /&gt;    }     &lt;br /&gt;     &lt;br /&gt;    @Override     &lt;br /&gt;    public String getId(){     &lt;br /&gt;        return this.id;     &lt;br /&gt;    }     &lt;br /&gt;     &lt;br /&gt;    @Override     &lt;br /&gt;    public void putObject(Object key, Object value){     &lt;br /&gt;        logger.info(&amp;quot;MybatisRedisCache -&amp;gt; putObject: key=&amp;quot; + key + &amp;quot;,value=&amp;quot; + value);     &lt;br /&gt;        if(null!=value) {     &lt;br /&gt;            redisTemplate.opsForHash().put(HASH_KEY,key.toString(),value);     &lt;br /&gt;        }     &lt;br /&gt;    }     &lt;br /&gt;     &lt;br /&gt;    @Override     &lt;br /&gt;    public Object getObject(Object key) {     &lt;br /&gt;        logger.info(&amp;quot;MybatisRedisCache -&amp;gt; getObject: key=&amp;quot;+key);     &lt;br /&gt;        if(null != key) {     &lt;br /&gt;            return redisTemplate.opsForHash().get(HASH_KEY,key.toString());     &lt;br /&gt;        }     &lt;br /&gt;        return null;     &lt;br /&gt;    }     &lt;br /&gt;     &lt;br /&gt;    @Override     &lt;br /&gt;    public Object removeObject(Object key){     &lt;br /&gt;        logger.info(&amp;quot;MybatisRedisCache -&amp;gt; removeObject: key=&amp;quot;+key);     &lt;br /&gt;        if(null != key) {     &lt;br /&gt;            return redisTemplate.opsForHash().delete(HASH_KEY,key);     &lt;br /&gt;        }     &lt;br /&gt;        return null;     &lt;br /&gt;    }     &lt;br /&gt;     &lt;br /&gt;    @Override     &lt;br /&gt;    public void clear() {     &lt;br /&gt;        Long size = redisTemplate.opsForHash().size(HASH_KEY);     &lt;br /&gt;        redisTemplate.delete(HASH_KEY);     &lt;br /&gt;        logger.info(&amp;quot;MybatisRedisCache -&amp;gt; clear: 清除了&amp;quot; + size + &amp;quot;个对象&amp;quot;);     &lt;br /&gt;    }     &lt;br /&gt;     &lt;br /&gt;    @Override     &lt;br /&gt;    public int getSize() {     &lt;br /&gt;        Long size = redisTemplate.opsForHash().size(HASH_KEY);     &lt;br /&gt;        return size == null ? 0 : size.intValue();     &lt;br /&gt;    }     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;代码非常好理解，就是实现  &lt;code&gt;org.apache.ibatis.cache.Cache&lt;/code&gt; 接口，通过  &lt;code&gt;Redis&lt;/code&gt;操作实现缓存的操作，这儿我们使用到了  &lt;code&gt;Hash&lt;/code&gt;表。&lt;/p&gt; &lt;p&gt;关于如何是上述代码中的  &lt;code&gt;redisTemplate&lt;/code&gt;生效，就要用到了  &lt;code&gt;RedisCacheHelp&lt;/code&gt;类，这个类代码如下，主要是实现  &lt;code&gt;redisTemplate&lt;/code&gt;的注入。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@Component     &lt;br /&gt;public class RedisCacheHelp {     &lt;br /&gt;    @Autowired     &lt;br /&gt;    public void setRedisTemplate(RedisTemplate redisTemplate) {     &lt;br /&gt;        RedisCache.setRedisTemplate(redisTemplate);     &lt;br /&gt;        //RedisCache1.setRedisTemplate(redisTemplate);     &lt;br /&gt;    }     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;上述代码弄好后，我们还需要在具体的  &lt;code&gt;xml&lt;/code&gt;（如StudentMapper.xml）指定缓存类型为我们的  &lt;code&gt;RedisCache&lt;/code&gt;，如下：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;&amp;lt;cache type=&amp;quot;com.zwt.demo.cache.RedisCache&amp;quot;&amp;gt;&amp;lt;/cache&amp;gt;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;以上配置完成，我们就可以使用我们自定义的二级缓存了。&lt;/p&gt; &lt;p&gt;我们运行一下测试代码，比如上面的这一个测试类：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;15     &lt;br /&gt;16     &lt;br /&gt;17     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@Test     &lt;br /&gt;public void cacheTest6() {     &lt;br /&gt;    SqlSession sqlSession1 = sqlSessionFactory.openSession(true);     &lt;br /&gt;    SqlSession sqlSession2 = sqlSessionFactory.openSession(true);     &lt;br /&gt;    SqlSession sqlSession3 = sqlSessionFactory.openSession(true);     &lt;br /&gt;     &lt;br /&gt;    StudentMapper studentMapper1 = sqlSession1.getMapper(StudentMapper.class);     &lt;br /&gt;    StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);     &lt;br /&gt;    StudentMapper studentMapper3 = sqlSession3.getMapper(StudentMapper.class);     &lt;br /&gt;     &lt;br /&gt;    System.out.println(&amp;quot;studentMapper1-&amp;gt;&amp;quot;+studentMapper1.selectByPrimaryKey(1));     &lt;br /&gt;    sqlSession1.commit();     &lt;br /&gt;    System.out.println(&amp;quot;studentMapper2-&amp;gt;&amp;quot;+studentMapper2.selectByPrimaryKey(1));     &lt;br /&gt;    studentMapper3.updateStudentName(&amp;quot;张二二&amp;quot;,1);     &lt;br /&gt;    sqlSession3.commit();     &lt;br /&gt;    System.out.println(&amp;quot;studentMapper2-&amp;gt;&amp;quot;+studentMapper2.selectByPrimaryKey(1));     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;可以看到如下输出：&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;说明已经使用了我们自定义的缓存策略。&lt;/p&gt; &lt;p&gt;大家可以看到我写了  &lt;code&gt;RedisCache&lt;/code&gt;和  &lt;code&gt;RedisCache1&lt;/code&gt;两个类，是这样的，我们可以看到  &lt;code&gt;RedisCache&lt;/code&gt;使用的是  &lt;code&gt;Redis Hash&lt;/code&gt;表结构来实现的二级缓存，其增加、删除、获取、统计数量、清空等操作十分方便，但有一个问题，就是  &lt;code&gt;Hash&lt;/code&gt;表没有办法设置其内部的单个  &lt;code&gt;key&lt;/code&gt;的过期时间，只能指定  &lt;code&gt;Hash&lt;/code&gt;表过期时间。&lt;/p&gt; &lt;p&gt;如果我们使用不当，可能会出现一些问题。&lt;/p&gt; &lt;p&gt;  &lt;code&gt;RedisCache1&lt;/code&gt;的代码如下：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;15     &lt;br /&gt;16     &lt;br /&gt;17     &lt;br /&gt;18     &lt;br /&gt;19     &lt;br /&gt;20     &lt;br /&gt;21     &lt;br /&gt;22     &lt;br /&gt;23     &lt;br /&gt;24     &lt;br /&gt;25     &lt;br /&gt;26     &lt;br /&gt;27     &lt;br /&gt;28     &lt;br /&gt;29     &lt;br /&gt;30     &lt;br /&gt;31     &lt;br /&gt;32     &lt;br /&gt;33     &lt;br /&gt;34     &lt;br /&gt;35     &lt;br /&gt;36     &lt;br /&gt;37     &lt;br /&gt;38     &lt;br /&gt;39     &lt;br /&gt;40     &lt;br /&gt;41     &lt;br /&gt;42     &lt;br /&gt;43     &lt;br /&gt;44     &lt;br /&gt;45     &lt;br /&gt;46     &lt;br /&gt;47     &lt;br /&gt;48     &lt;br /&gt;49     &lt;br /&gt;50     &lt;br /&gt;51     &lt;br /&gt;52     &lt;br /&gt;53     &lt;br /&gt;54     &lt;br /&gt;55     &lt;br /&gt;56     &lt;br /&gt;57     &lt;br /&gt;58     &lt;br /&gt;59     &lt;br /&gt;60     &lt;br /&gt;61     &lt;br /&gt;62     &lt;br /&gt;63     &lt;br /&gt;64     &lt;br /&gt;65     &lt;br /&gt;66     &lt;br /&gt;67     &lt;br /&gt;68     &lt;br /&gt;69     &lt;br /&gt;70     &lt;br /&gt;71     &lt;br /&gt;72     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;public class RedisCache1 implements Cache{     &lt;br /&gt;     &lt;br /&gt;    private static final Logger logger = LoggerFactory.getLogger(RedisCache1.class);     &lt;br /&gt;     &lt;br /&gt;    private static RedisTemplate&amp;lt;String,Object&amp;gt; redisTemplate;     &lt;br /&gt;     &lt;br /&gt;    private final String id;     &lt;br /&gt;     &lt;br /&gt;    private final String KEY_ALL_PREFIX = &amp;quot;mybatis_redis_cache_%s&amp;quot;;     &lt;br /&gt;     &lt;br /&gt;    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();     &lt;br /&gt;     &lt;br /&gt;    @Override     &lt;br /&gt;    public ReadWriteLock getReadWriteLock(){     &lt;br /&gt;        return this.readWriteLock;     &lt;br /&gt;    }     &lt;br /&gt;     &lt;br /&gt;    public static void setRedisTemplate(RedisTemplate redisTemplate){     &lt;br /&gt;        RedisCache1.redisTemplate = redisTemplate;     &lt;br /&gt;    }     &lt;br /&gt;     &lt;br /&gt;    public RedisCache1(final String id){     &lt;br /&gt;        if (id == null) {     &lt;br /&gt;            throw new IllegalArgumentException(&amp;quot;需要指定id&amp;quot;);     &lt;br /&gt;        }     &lt;br /&gt;        this.id = id;     &lt;br /&gt;    }     &lt;br /&gt;     &lt;br /&gt;    @Override     &lt;br /&gt;    public String getId(){     &lt;br /&gt;        return this.id;     &lt;br /&gt;    }     &lt;br /&gt;     &lt;br /&gt;    @Override     &lt;br /&gt;    public void putObject(Object key, Object value){     &lt;br /&gt;        logger.info(&amp;quot;MybatisRedisCache -&amp;gt; putObject: key=&amp;quot; + key + &amp;quot;,value=&amp;quot; + value);     &lt;br /&gt;        if(null!=value) {     &lt;br /&gt;            redisTemplate.opsForValue().set(String.format(KEY_ALL_PREFIX,key.toString()),value,60, TimeUnit.SECONDS);     &lt;br /&gt;        }     &lt;br /&gt;    }     &lt;br /&gt;     &lt;br /&gt;    @Override     &lt;br /&gt;    public Object getObject(Object key) {     &lt;br /&gt;        logger.info(&amp;quot;MybatisRedisCache -&amp;gt; getObject: key=&amp;quot;+key);     &lt;br /&gt;        if(null != key) {     &lt;br /&gt;            return redisTemplate.opsForValue().get(String.format(KEY_ALL_PREFIX,key.toString()));     &lt;br /&gt;        }     &lt;br /&gt;        return null;     &lt;br /&gt;    }     &lt;br /&gt;     &lt;br /&gt;    @Override     &lt;br /&gt;    public Object removeObject(Object key){     &lt;br /&gt;        logger.info(&amp;quot;MybatisRedisCache -&amp;gt; removeObject: key=&amp;quot;+key);     &lt;br /&gt;        if(null != key) {     &lt;br /&gt;            return redisTemplate.delete(String.format(KEY_ALL_PREFIX,key.toString()));     &lt;br /&gt;        }     &lt;br /&gt;        return null;     &lt;br /&gt;    }     &lt;br /&gt;     &lt;br /&gt;    @Override     &lt;br /&gt;    public void clear() {     &lt;br /&gt;        Set&amp;lt;String&amp;gt; keys = redisTemplate.keys(String.format(KEY_ALL_PREFIX,&amp;quot;*&amp;quot;));     &lt;br /&gt;        redisTemplate.delete(keys);     &lt;br /&gt;        logger.info(&amp;quot;MybatisRedisCache -&amp;gt; clear: 清除了&amp;quot; + keys.size() + &amp;quot;个对象&amp;quot;);     &lt;br /&gt;    }     &lt;br /&gt;     &lt;br /&gt;    @Override     &lt;br /&gt;    public int getSize() {     &lt;br /&gt;        Set&amp;lt;String&amp;gt; keys = redisTemplate.keys(String.format(KEY_ALL_PREFIX,&amp;quot;*&amp;quot;));     &lt;br /&gt;        return keys.size();     &lt;br /&gt;    }     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;可以看到这个类主要是利用  &lt;code&gt;Redis key&lt;/code&gt;前缀来处理缓存内容，其  &lt;code&gt;K,V&lt;/code&gt;都是单个存储的，其增加、删除、获取是非常方便的，而且可以指定  &lt;code&gt;key&lt;/code&gt;的过期时间，防止出现问题，但其清空、统计数量操作需要查出全部前缀数据的  &lt;code&gt;Set&lt;/code&gt;集合，在进行处理。&lt;/p&gt; &lt;p&gt;以上就是我们关于实现自定义  &lt;code&gt;Mybatis&lt;/code&gt;缓存的一些代码。&lt;/p&gt; &lt;blockquote&gt;  &lt;p&gt;这儿需要说的一点是：我们上述这种使用Redis实现   &lt;code&gt;Mybatis&lt;/code&gt;二级缓存的方式，在分布式环境下，可以将各个单机缓存归一，避免了一些查库操作，但实际上我们不使用二级缓存，只使用Redis也是可以解决的。&lt;/p&gt;  &lt;p&gt;另外这种形式的二级缓存，出现（测试四）映射文件中存在多表查询的情况，如果不使用cache-ref，也会有脏数据问题。&lt;/p&gt;&lt;/blockquote&gt; &lt;h1&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#24635;&amp;#32467;-2" title="&amp;#24635;&amp;#32467;"&gt;&lt;/a&gt;总结&lt;/h1&gt; &lt;p&gt;本文通过对  &lt;code&gt;Mybaits&lt;/code&gt;一二级缓存的介绍，并从实验及源码角度分析了  &lt;code&gt;Mybaits&lt;/code&gt;缓存的特点、机制及可能产生的问题，并对缓存机制做了一定的总结。&lt;/p&gt; &lt;p&gt;而后我们又借助  &lt;code&gt;Redis&lt;/code&gt;实现了一个自定义的二级缓存处理器，加深对  &lt;code&gt;Mybatis&lt;/code&gt;缓存的理解。&lt;/p&gt; &lt;p&gt;在实际生产中，我们一般建议关闭  &lt;code&gt;Mybatis&lt;/code&gt;缓存特性，单纯将其作为一个  &lt;code&gt;ORM&lt;/code&gt;框架来使用。&lt;/p&gt; &lt;h1&gt;  &lt;a href="https://www.sakuratears.top/#&amp;#21442;&amp;#32771;&amp;#36164;&amp;#26009;" title="&amp;#21442;&amp;#32771;&amp;#36164;&amp;#26009;"&gt;&lt;/a&gt;参考资料&lt;/h1&gt; &lt;ul&gt;  &lt;li&gt;   &lt;a href="https://tech.meituan.com/2018/01/19/mybatis-cache.html"&gt;聊聊Mybatis缓存机制&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;   &lt;a href="https://www.cnblogs.com/wuzhenzhao/p/11103043.html"&gt;mybatis缓存机制&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>学习 Mybatis 一级缓存 二级缓存</category>
      <guid isPermaLink="true">https://itindex.net/detail/61717-mybaits-%E7%BC%93%E5%AD%98</guid>
      <pubDate>Mon, 02 Aug 2021 22:38:00 CST</pubDate>
    </item>
    <item>
      <title>风险控制：信用评分卡模型</title>
      <link>https://itindex.net/detail/61198-%E9%A3%8E%E9%99%A9%E6%8E%A7%E5%88%B6-%E4%BF%A1%E7%94%A8%E8%AF%84%E5%88%86-%E6%A8%A1%E5%9E%8B</link>
      <description>&lt;h2&gt;什么是信用评分卡模型？&lt;/h2&gt;
 &lt;p&gt;评分卡模型又叫做信用评分卡模型，最早由美国信用评分巨头FICO公司于20世纪60年代推出，在信用风险评估以及金融风险控制领域中广泛使用。银行利用评分卡模型对客户的信用历史数据的多个特征进行打分，得到不同等级的信用评分，从而判断客户的优质程度，据此决定是否准予授信以及授信的额度和利率。相较资深从业人员依靠自身的经验设置的专家规则，评分卡模型的使用具有很明显的优点：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;判断快速&lt;/strong&gt;：系统只需要按照评分卡逐项打分，最后通过相应的公式计算出总分，即可准确判断出是否为客户授信以及额度和利率。&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;客观透明&lt;/strong&gt;：评分卡模型的标准是统一的，无论是客户还是风险审核人员，都可以通过评分卡一眼看出评分结果和评判依据。&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;应用范围广&lt;/strong&gt;：由于评分卡的评分项是客观计算，其得出的分数具有广泛的参考性和适用性。例如，生活中常见的支付宝芝麻信用分，就是依据评分卡模型计算得出。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" height="263" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/credit-score.png" width="750"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;评分卡模型在银行不同的业务阶段体现的方式和功能也不一样。按照借贷用户的借贷时间，评分卡模型可以划分为以下三种：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;贷前：申请评分卡（Application score card），又称为A卡
   &lt;ul&gt;
    &lt;li&gt;更准确地评估申请人的未来表现(违约率)，降低坏帐率&lt;/li&gt;
    &lt;li&gt;加快(自动化)审批流程, 降低营运成本&lt;/li&gt;
    &lt;li&gt;增加审批决策的客观性和一致性，提高客户满意度&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;贷中：行为评分卡（Behavior score card），又称为B卡
   &lt;ul&gt;
    &lt;li&gt;更好的客户管理策略, 提高赢利&lt;/li&gt;
    &lt;li&gt;减少好客户的流失&lt;/li&gt;
    &lt;li&gt;对可能拖欠的客户，提早预警&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;贷后：催收评分卡（Collection score card），又称为C卡
   &lt;ul&gt;
    &lt;li&gt;优化催收策略，提高欠帐的回收率&lt;/li&gt;
    &lt;li&gt;减少不必要的催收行为，降低营运成本&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;评分卡模型示例：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="432" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/credit-score-example.png" width="429"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;一个用户总的评分等于基准分加上对客户各个属性的评分。举个例子某客户年龄为27岁，性别为男，婚姻状况为已婚，学历为本科，月收入为10000，那么他的评分为：223+8+4+8+8+13=264&lt;/p&gt;
 &lt;h2&gt;如何搭信用评分卡模型？&lt;/h2&gt;
 &lt;p&gt;有了上面的评分卡示例，接下来需要考虑的是如何生成类似上面的表格：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;变量特征是如何选取的？&lt;/li&gt;
  &lt;li&gt;特征的变量范围是如何进行划分的？&lt;/li&gt;
  &lt;li&gt;每个字段的分值是如何设定的？&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;变量选择&lt;/h3&gt;
 &lt;p&gt;变量筛选的主要目的：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;剔除跟目标变量不太相关的特征&lt;/li&gt;
  &lt;li&gt;消除由于线性相关的变量，避免特征冗余&lt;/li&gt;
  &lt;li&gt;减轻后期验证、部署、监控的负担&lt;/li&gt;
  &lt;li&gt;保证变量的可解释性&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;单变量筛选&lt;/h4&gt;
 &lt;p&gt;单变量的筛选基于变量预测能力，常用方法：&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;基于IV值的变量筛选&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;WOE的取值范围是[-∞,+∞]，当分箱中好坏客户比例等于整体好坏客户比例时，WOE为0。&lt;/li&gt;
  &lt;li&gt;对于变量的一个分箱，这个分组的好坏客户比例与整体好坏客户比例相差越大，IV值越大，否则，IV值越小。&lt;/li&gt;
  &lt;li&gt;IV值的取值范围是[0,+∞)，当分箱中只包含好客户或坏客户时，IV = +∞，当分箱中好坏客户比例等于整体好坏客户比例时，IV为0。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;在评分卡建模流程中，WOE（Weight of Evidence）常用于特征变换，IV（Information Value）则用来衡量特征的预测能力。&lt;/p&gt;
 &lt;p&gt;WOE（Weight of Evidence）叫做证据权重，WOE在业务中常有哪些应用呢？&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;处理缺失值：当数据源没有100%覆盖时，那就会存在缺失值，此时可以把null单独作为一个分箱。这点在分数据源建模时非常有用，可以有效将覆盖率哪怕只有20%的数据源利用起来。&lt;/li&gt;
  &lt;li&gt;处理异常值：当数据中存在离群点时，可以把其通过分箱离散化处理，从而提高变量的鲁棒性（抗干扰能力）。例如，age若出现200这种异常值，可分入“age &amp;gt; 60”这个分箱里，排除影响。&lt;/li&gt;
  &lt;li&gt;业务解释性：我们习惯于线性判断变量的作用，当x越来越大，y就越来越大。但实际x与y之间经常存在着非线性关系，此时可经过WOE变换。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;$$WOE_i = ln(\frac{Bad_i}{Bad_T}/\frac{Good_i}{Good_T}) = ln(\frac{Bad_i}{Bad_T})-ln(\frac{Good_i}{Good_T})$$&lt;/p&gt;
 &lt;p&gt;IV（Information Value）是与WOE密切相关的一个指标，常用来评估变量的预测能力。因而可用来快速筛选变量。在应用实践中，其评价标准如下：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="137" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/IV.png" width="252"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;而IV的计算公式定义如下，其可认为是WOE的加权和：&lt;/p&gt;
 &lt;p&gt;$$IV_i = (\frac{Bad_i}{Bad_T}-\frac{Good_i}{Good_T}) * WOE_i$$&lt;/p&gt;
 &lt;p&gt;$$IV = \sum_{i=1}^{n}IV_i$$&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;基于stepwise的变量筛选&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;基于stepwise的变量筛选方法也是评分卡中变量筛选最常用的方法之一。具体包括三种筛选变量的方式：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;前向选择forward：逐步将变量一个一个放入模型，并计算相应的指标，如果指标值符合条件，则保留，然后再放入下一个变量，直到没有符合条件的变量纳入或者所有的变量都可纳入模型。&lt;/li&gt;
  &lt;li&gt;后向选择backward：一开始将所有变量纳入模型，然后挨个移除不符合条件的变量，持续此过程，直到留下所有最优的变量为止。&lt;/li&gt;
  &lt;li&gt;逐步选择stepwise：该算法是向前选择和向后选择的结合，逐步放入最优的变量、移除最差的变量。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;基于特征重要度的变量筛选&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;基于特征重要度的变量筛选方法是目前机器学习最热门的方法之一，其原理主要是通过随机森林和GBDT等集成模型选取特征的重要度。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;基于LASSO正则化的变量筛选&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;L1正则化通常称为Lasso正则化，它是在代价函数上增加了一个L1范数。&lt;/p&gt;
 &lt;p&gt;随着机器学习的发展，变量选择的方法也在增加。信用风险模型中典型的变量选择方法：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="701" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/features.png" width="695"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;更多方法请参考：  &lt;a href="https://www.biaodianfu.com/feature-selection.html"&gt;机器学习之特征选择方法&lt;/a&gt;&lt;/p&gt;
 &lt;h4&gt;变量相关性分析&lt;/h4&gt;
 &lt;p&gt;常用分析方法：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;变量两两相关性分析&lt;/li&gt;
  &lt;li&gt;变量的多重共线性分析&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;为什么要进行相关性分析？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;设想建立一个具有两变量$X_1$和$X_2$的线性模型，真实模型是$Y=X_1+X_2$。如果$X_1$和$X_2$线性相关（比如说$X_1\approx 2X_2$），那么拟合模型$Y=3X_2$, $Y=2X_1-X_2$或$Y=51X_1-99X_2$的效果都一样好，理想状态下，系数权重会有无数种取法，使系数权重变得无法解释，导致变量的每个分段的得分也有无数种取法（后面我们会发现变量中不同分段的评分会用到变量的系数）。&lt;/p&gt;
 &lt;p&gt;当两个变量具有高相关性时，保留IV值大。&lt;/p&gt;
 &lt;h3&gt;变量分箱&lt;/h3&gt;
 &lt;p&gt;评分卡模型通过对变量进行分箱来实现变量的分段。那么什么是分箱呢？以下为分箱的定义：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;对连续变量进行分段离散化&lt;/li&gt;
  &lt;li&gt;将多状态的离散变量进行合并，减少离散变量的状态数&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;常见的分箱类型有以下几种：&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;无监督分箱&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;等频分箱：把自变量按从小到大的顺序排列，根据自变量的个数等分为k部分，每部分作为一个分箱。&lt;/li&gt;
  &lt;li&gt;等距分箱：把自变量按从小到大的顺序排列，将自变量的取值范围分为k个等距的区间，每个区间作为一个分箱。&lt;/li&gt;
  &lt;li&gt;聚类分箱：用k-means聚类法将自变量聚为k类，但在聚类过程中需要保证分箱的有序性。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;由于无监督分箱仅仅考虑了各个变量自身的数据结构，并没有考虑自变量与目标变量之间的关系，因此无监督分箱不一定会带来模型性能的提升。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;有监督分箱&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;包括 Split 分箱和 Merge 分箱&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Split 分箱是一种自上而下(即基于分裂)的数据分段方法。Split 分箱和决策树比较相似，切分点的选择指标主要有 Entropy，Gini 指数和 IV 值等。&lt;/li&gt;
  &lt;li&gt;Merge 分箱，是一种自底向上(即基于合并)的数据离散化方法。Merge 分箱常见的类型为Chimerge分箱。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;ChiMerge 分箱&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;ChiMerge 分箱是目前最流行的分箱方式之一，其基本思想是如果两个相邻的区间具有类似的类分布，则这两个区间合并；否则，它们应保持分开。Chimerge通常采用卡方值来衡量两相邻区间的类分布情况。&lt;/p&gt;
 &lt;p&gt;ChiMerge的具体算法如下：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;输入：分箱的最大区间数n&lt;/li&gt;
  &lt;li&gt;初始化&lt;/li&gt;
&lt;/ol&gt;
 &lt;ul&gt;
  &lt;li&gt;连续值按升序排列，离散值先转化为坏客户的比率，然后再按升序排列&lt;/li&gt;
  &lt;li&gt;为了减少计算量，对于状态数大于某一阈值 (建议为100) 的变量，利用等频分箱进行粗分箱&lt;/li&gt;
  &lt;li&gt;若有缺失值，则缺失值单独作为一个分箱&lt;/li&gt;
&lt;/ul&gt;
 &lt;ol start="3"&gt;
  &lt;li&gt;合并区间&lt;/li&gt;
&lt;/ol&gt;
 &lt;ul&gt;
  &lt;li&gt;计算每一对相邻区间的卡方值&lt;/li&gt;
  &lt;li&gt;将卡方值最小的一对区间合并&lt;/li&gt;
  &lt;li&gt;重复以上两个步骤，直到分箱数量不大于n&lt;/li&gt;
&lt;/ul&gt;
 &lt;ol start="4"&gt;
  &lt;li&gt;分箱后处理&lt;/li&gt;
&lt;/ol&gt;
 &lt;ul&gt;
  &lt;li&gt;对于坏客户比例为 0 或 1 的分箱进行合并 (一个分箱内不能全为好客户或者全为坏客户)。&lt;/li&gt;
  &lt;li&gt;对于分箱后某一箱样本占比超过 95% 的箱子进行删除。&lt;/li&gt;
  &lt;li&gt;检查缺失分箱的坏客户比例是否和非缺失分箱相等，如果相等，进行合并。&lt;/li&gt;
&lt;/ul&gt;
 &lt;ol start="5"&gt;
  &lt;li&gt;输出：分箱后的数据和分箱区间。&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;总结一下特征分箱的优势：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;特征分箱可以有效处理特征中的缺失值和异常值。&lt;/li&gt;
  &lt;li&gt;特征分箱后，数据和模型会更稳定。&lt;/li&gt;
  &lt;li&gt;特征分箱可以简化逻辑回归模型，降低模型过拟合的风险，提高模型的泛化能力。&lt;/li&gt;
  &lt;li&gt;将所有特征统一变换为类别型变量。&lt;/li&gt;
  &lt;li&gt;分箱后变量才可以使用标准的评分卡格式，即对不同的分段进行评分。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;为了创建一个对过度拟合具有弹性的健壮模型，每个箱应该包含来自总账户的足够数量的观察结果（大多数从业者建议的最小值为5%），如果最大箱占据了总样本量的90%以上，那么弃用该变量。&lt;/p&gt;
 &lt;h3&gt;WOE编码&lt;/h3&gt;
 &lt;p&gt;在风控用到的数据里，我们会用到两种变量：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Numerical Variable，数值变量。例如逾期金额，天数&lt;/li&gt;
  &lt;li&gt;Categorical Variable，类别变量。例如客户职业&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;在制作评分卡过程中，我们还需要把数值变量变成类别变量，例如客户年龄段，我们可以划分为[20及以下],[21-30],[31-40],[41-50],[51-60],[61-70],[70以上]七个类别，这时候我们就把数值变成了类别。这种把数值变成类别的技巧叫做分箱（binning）。&lt;/p&gt;
 &lt;p&gt;但是当把所有变量都变成类别后，这时候你也许有这个疑惑：怎么去训练一个模型呢？例如逻辑回归，只能用数值作为特征输入。怎么把类别变成数值呢？&lt;/p&gt;
 &lt;p&gt;你这时候想到的可能是one-hot encoding，但还是有问题，对于逻辑回归来说，one-hot encoding输出的矩阵太稀疏了，很难让逻辑回归有很好的效果。这时候，我们可以试试把类别或者分箱转化成响应的数值。这个分数必须和必须有这个特性：分数越大，代表这个变量给bad label的贡献度越大，这个贡献度，视运算符号不同，可以是正向，也可以是负向，但我们期望它们之间有个线性关系。这时候我们需要引入WOE编码。&lt;/p&gt;
 &lt;p&gt;在变量筛选中对WOE已经有了简单的介绍。这里以实例进行介绍：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="154" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/woe.png" width="951"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;我们观察Bad Rate 和WOE的关系，可以看到WOE越大，Bad Rate越高，也就是说，通过WOE变换，特征值不仅仅代表一个分类，还代表了这个分类的权重。&lt;/p&gt;
 &lt;p&gt;对于类别变量进行WOE编码很好理解，为什么数值变量需要在分箱以后再进行WOE编码？分箱+WOE编码主要要解决的问题是把非线性的特征转化为线性。&lt;/p&gt;
 &lt;p&gt;例如在风控场景里，我们可能用到客户的年龄做特征。我们知道肯定不是年龄越大风险越高，或者年龄越大风险越低，一定是有个年龄段的风险是比其他年龄段高些。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="643" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/woe-1.png" width="406"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;总结下WOE编码的优势：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;可提升模型的预测效果&lt;/li&gt;
  &lt;li&gt;将自变量规范到同一尺度上&lt;/li&gt;
  &lt;li&gt;WOE能反映自变量取值的贡献情况&lt;/li&gt;
  &lt;li&gt;有利于对变量的每个分箱进行评分&lt;/li&gt;
  &lt;li&gt;转化为连续变量之后，便于分析变量与变量之间的相关性&lt;/li&gt;
  &lt;li&gt;与独热向量编码相比，可以保证变量的完整性，同时避免稀疏矩阵和维度灾难&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;模型训练&lt;/h3&gt;
 &lt;p&gt;Logistic回归是信用评分中用于解决二元分类问题的常用技术。逻辑回归通过sigmoid函数$ y = \frac{1}{1+e^{-z}}$ 将线性回归模型$z=\boldsymbol{w}^T\boldsymbol{x}+b$产生的预测值转换为一个接近0或1的拟合值：&lt;/p&gt;
 &lt;p&gt;$$h(x)=\frac{1}{1+e^{-z}}=\frac{1}{1+e^{-(\boldsymbol{w}^T\boldsymbol{x}+b)}}$$&lt;/p&gt;
 &lt;p&gt;上式的$h(x)$可视为事件发生的概率$p(y=1|\boldsymbol{x})$，变换后得到：$\ln\frac{p}{1-p}=z=\boldsymbol{w}^T\boldsymbol{x}+b$&lt;/p&gt;
 &lt;p&gt;其中，$p/(1-p)$为比率(odds)，即违约概率与正常概率的比值。$\ln{p/(1-p)}$为logit函数，即比率的自然对数。因此，逻辑回归实际上是用比率的自然对数作为因变量的线性回归模型。&lt;/p&gt;
 &lt;p&gt;在模型拟合之前，变量选择的再一次迭代对于检查新的WOE变换变量是否仍然是良好的模型候选变量是有价值的。优选的候选变量是具有较高信息值（通常在0.1和0.5之间）的变量，与因变量具有线性关系，在所有类别中具有良好的覆盖率，具有正态分布，包含显著的总体贡献，并且与业务相关。&lt;/p&gt;
 &lt;p&gt;由逻辑回归的基本原理，我们将客户违约的概率表示为p，则正常的概率为1-p。因此，可以得到：&lt;/p&gt;
 &lt;p&gt;$$Odds = \frac{p}{1-p}$$&lt;/p&gt;
 &lt;p&gt;此时，客户违约的概率p可表示为：&lt;/p&gt;
 &lt;p&gt;$$p = \frac{Odds}{1+Odds}$$&lt;/p&gt;
 &lt;p&gt;评分卡设定的分值刻度可以通过将分值表示为比率对数的线性表达式来定义，即可表示为下式：&lt;/p&gt;
 &lt;p&gt;$$Score = A – B\log(Odds)$$&lt;/p&gt;
 &lt;p&gt;其中，A和B是常数。式中的负号可以使得违约概率越低，得分越高。通常情况下，这是分值的理想变动方向，即高分值代表低风险，低分值代表高风险。 逻辑回归模型计算比率如下所示：&lt;/p&gt;
 &lt;p&gt;$$ log(Odds)=\beta _0 + \beta _1x_1+…+\beta _nx_n$$&lt;/p&gt;
 &lt;p&gt;其中，用建模参数拟合模型可以得到模型参数$\beta _0,\beta _1,…,\beta _n$。 式中的常数A、B的值可以通过将两个已知或假设的分值带入计算得到。通常情况下，需要设定两个假设：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;给某个特定的比率设定特定的预期分值&lt;/li&gt;
  &lt;li&gt;确定比率翻番的分数（PDO） 根据以上的分析，我们首先假设比率为x的特定点的分值为P。则比率为2x的点的分值应该为P+PDO。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;代入式中，可以得到如下两个等式：&lt;/p&gt;
 &lt;p&gt;$$P = A – B\log(x)$$&lt;/p&gt;
 &lt;p&gt;$$P – PDO = A – B\log(2x)$$&lt;/p&gt;
 &lt;p&gt;假设设定评分卡刻度使得比率为1:20（违约正常比）时的分值为50分，PDO为10分，代入式中求得：B=14.43，A=6.78 则分值的计算公式可表示为：&lt;/p&gt;
 &lt;p&gt;$$Score = 6.78 -14.43\log(Odds)$$&lt;/p&gt;
 &lt;p&gt;评分卡刻度参数A和B确定以后，就可以计算比率和违约概率，以及对应的分值了。通常将常数A称为补偿，常数B称为刻度。 则评分卡的分值可表达为：&lt;/p&gt;
 &lt;p&gt;$$Score = A – B\{\beta _0+\beta _1x_1+…+\beta _nx_n\}$$&lt;/p&gt;
 &lt;p&gt;式中：变量$x_1,…,x_n$是出现在最终模型中的自变量，即为入模指标。由于此时所有变量都用WOE转换进行了转换，可以将这些自变量中的每一个都写$(\beta _i\omega _{ij})\delta _{ij}$的形式：&lt;/p&gt;
 &lt;p&gt;$$Score = A-B\{\beta _0+(\beta _1\omega _{11})\delta _{11}+(\beta _1\omega _{12})\delta _{12}+…+(\beta _2\omega _{21})\delta _{21}+…\}$$&lt;/p&gt;
 &lt;p&gt;式中：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;$\omega _{ij}$为第i行第j个变量的WOE，为已知变量&lt;/li&gt;
  &lt;li&gt;$\beta _i$为逻辑回归方程中的系数，为已知变量&lt;/li&gt;
  &lt;li&gt;$\delta _{ij}$为二元变量，表示变量i是否取第j个值。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;上式可重新表示为：&lt;/p&gt;
 &lt;p&gt;$$Score = (A-B\beta _0)-(B\beta _1\omega _{11})\delta _{11}-(B\beta _1\omega _12)\delta _{12}-…-(B\beta _x\omega _{x1}-…$$&lt;/p&gt;
 &lt;p&gt;此式即为最终评分卡公式。如果$x_1…x_n$变量取不同行并计算其WOE值，式中表示的标准评分卡格式，如表3.20所示：$(A-B\beta _0)$；由于分值分配公式中的负号，模型参数$\beta _0,\beta _1,…,\beta _n$也应该是负值；变量$x_i$的第j行的分值取决于以下三个数值：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="331" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/Logistic.png" width="573"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h4&gt;拒绝推断&lt;/h4&gt;
 &lt;p&gt;申请评分卡的模型开发过程中使用的数据实际上并不是从申请总体样本中随机选择的，而仅仅是从过去已经被接受的客户样本中选择的。因此，开发申请评分卡时将对被拒绝客户的状态进行推断并纳入模型开发数据集中，即拒绝推断过程。拒绝推断的常用方法包括：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;简单赋值法：人为指定被拒绝账户的标签
   &lt;ul&gt;
    &lt;li&gt;忽略被拒绝申请&lt;/li&gt;
    &lt;li&gt;所有被拒申请赋值为违约标签&lt;/li&gt;
    &lt;li&gt;按比例赋值，使得其坏客户率是通过样本的2~5倍以上&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;强化法：通过外推法确定拒绝账户的标签
   &lt;ul&gt;
    &lt;li&gt;简单强化法：使用通过客户开发的模型对被拒绝客户评分，将其中低分段赋予违约标签。使得拒绝客户的坏客户率为通过的2~5倍以上&lt;/li&gt;
    &lt;li&gt;模糊强化法：通过模型计算得到正常和违约概率。&lt;/li&gt;
    &lt;li&gt;打包强化法：先用开发的评分卡对被拒客户评分，然后指定每个分值区间的违约客户数量。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;模型表现&lt;/h4&gt;
 &lt;p&gt;模型评估是模型构建过程的最后一步。它由三个不同的阶段组成：评估，验证和接受。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;评估准确性&lt;/strong&gt; – 我是否构建了正确的模型？ – 是第一个要求测试模型的问题。评估的关键指标是统计测量，包括模型准确性，复杂性，错误率，模型拟合统计，变量统计，显著性值和优势比。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;验证稳健性&lt;/strong&gt; – 我是否构建了正确的模型？ – 从分类准确性和统计评估转向排名能力和业务评估时，是下一个要问的问题。&lt;/p&gt;
 &lt;p&gt;验证度量的选择取决于模型分类器的类型。二元分类问题最常见的指标是增益图，提升图，ROC曲线和Kolmogorov-Smirnov图。ROC曲线是可视化模型性能的最常用工具。它是一个多用途工具，用于：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;冠军挑战者选择最佳表现模式的方法&lt;/li&gt;
  &lt;li&gt;在看不见的数据上测试模型性能并将其与训练数据进行比较&lt;/li&gt;
  &lt;li&gt;选择最佳阈值，最大化真阳性率，同时最小化假阳性率&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;通过绘制灵敏度与不同阈值的误报概率（误报率）来创建ROC曲线。评估不同阈值下的性能指标是ROC曲线的理想特征。根据业务策略，不同类型的业务问题将具有不同的阈值。&lt;/p&gt;
 &lt;p&gt;ROC曲线下面积（AUC）是指示分类器预测能力的有用度量。在信用风险中，0.75或更高的AUC是行业认可的标准和模型验收的先决条件。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;接受有用性&lt;/strong&gt; – 模型是否会被接受？ – 是最后一个问题，以便测试该模型是否对商业前景有价值。这是数据科学家必须将模型结果回放给业务并“捍卫”其模型的关键阶段。关键评估标准是模型的商业利益，因此，效益分析是呈现结果的核心部分。数据科学家应该尽一切努力以简洁的方式呈现结果，因此结果和发现很容易理解。如果不能实现这一点，可能会导致模型拒绝，从而导致项目失败。&lt;/p&gt;
 &lt;p&gt;模型一旦对齐，下一步就是将模型调整到业务所需的比例。这称为缩放。缩放作为一种测量工具，可以在不同的评分卡中提供分数的一致性和标准化。最低和最高分数值以及分数范围有助于风险解释，并应向业务部门报告。通常，业务要求是对多个评分卡使用相同的分数范围，因此它们都具有相同的风险解释。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;信用风控策略&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;模型开发之后需要基于建模样本确定风控策略。一个好的风控策略应具备：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;增加客户数量&lt;/li&gt;
  &lt;li&gt;减少风险损失&lt;/li&gt;
  &lt;li&gt;最大化利润&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;基于开发的评分卡，我们可以获得建模样本的审批决策表。结合审批决策表与损失或者利润目标，制定常用风控策略：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;评分临界值：实现通过率、坏客户率、或利润损失率等业务目标&lt;/li&gt;
  &lt;li&gt;通过交叉决策矩阵实现风险定价，实现差异化的利率、额度等：
   &lt;ul&gt;
    &lt;li&gt;风险评分与利润损失比&lt;/li&gt;
    &lt;li&gt;风险评分与债务收入比&lt;/li&gt;
    &lt;li&gt;风险评分与流失倾向评分&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;一个好的模型一般应具有以下特征：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;在进行数据描述时变量应该有意义。通常，某些变在特定客群的不同风险模型中重复出现。例如，信用卡行为评分卡模型中，授信使用率经常出现；申请评分卡模型中收入水平、职业和历史信贷产品拥有情况比人口统计变量重要。&lt;/li&gt;
  &lt;li&gt;变量的预测力或贡献度，应该在模型的变量之间分布。&lt;/li&gt;
  &lt;li&gt;模型中不应该包含太多变量。通常，包含的变量不超过9~20个(最优10~12个)。变量太多可能导致过拟合，变量太少往往区分度不够。&lt;/li&gt;
  &lt;li&gt;最终模型的变量应该能够确保包含稳健一致的数据，并在后续实施阶段能够准确获取。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;评分卡模型搭建实战&lt;/h2&gt;
 &lt;p&gt;数据来源：  &lt;a href="https://www.kaggle.com/c/GiveMeSomeCredit/"&gt;Give Me Some Credit比赛&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;数据字段说明：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td width="281"&gt;列名&lt;/td&gt;
   &lt;td width="441"&gt;字段说明&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;SeriousDlqin2yrs&lt;/td&gt;
   &lt;td width="441"&gt;两年内是否有严重违约（好坏用户判断）&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;RevolvingUtilizationOfUnsecuredLines&lt;/td&gt;
   &lt;td width="441"&gt;可用信贷额度比例，信用卡和个人信用额度（不动产和汽车贷款等分期付款债务除外）的总余额除以信用额度之和&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;age&lt;/td&gt;
   &lt;td width="441"&gt;借款人年龄&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;NumberOfTime30-59DaysPastDueNotWorse&lt;/td&gt;
   &lt;td width="441"&gt;两年内35-59天逾期次数&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;DebtRatio&lt;/td&gt;
   &lt;td width="441"&gt;借款人负债比率（每月债务支付、赡养费、生活费之和除以月收入）&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;MonthlyIncome&lt;/td&gt;
   &lt;td width="441"&gt;借款人月收入&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;NumberOfOpenCreditLinesAndLoans&lt;/td&gt;
   &lt;td width="441"&gt;开放式信贷和贷款数量&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;NumberOfTimes90DaysLate&lt;/td&gt;
   &lt;td width="441"&gt;两年内90天或高于90天逾期的次数&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;NumberRealEstateLoansOrLines&lt;/td&gt;
   &lt;td width="441"&gt;不动产贷款或额度数量&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;NumberOfTime60-89DaysPastDueNotWorse&lt;/td&gt;
   &lt;td width="441"&gt;两年内60-89天逾期次数&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;NumberOfDependents&lt;/td&gt;
   &lt;td width="441"&gt;借款人家属数量（不包括本人在内）&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;h3&gt;  &lt;strong&gt;探索数据&lt;/strong&gt;&lt;/h3&gt;
 &lt;pre&gt;import pandas as pd
from dataprep.eda import plot
import warnings
warnings.filterwarnings(&amp;apos;ignore&amp;apos;)

train_data = pd.read_csv(&amp;apos;cs-training.csv&amp;apos;, index_col=0)
train_data.columns = [&amp;apos;严重违约&amp;apos;, &amp;apos;可用额度比例&amp;apos;,&amp;apos;年龄&amp;apos;, &amp;apos;35-69天逾期次数&amp;apos;, &amp;apos;负债比例&amp;apos;,&amp;apos;月收入&amp;apos;,&amp;apos;普通贷款数量&amp;apos;,&amp;apos;高于90天逾期次数&amp;apos;,&amp;apos;不动产贷款数量&amp;apos;,&amp;apos;60-89天逾期次数&amp;apos;,&amp;apos;家属数量&amp;apos;]
train_data = train_data[[&amp;apos;年龄&amp;apos;,&amp;apos;家属数量&amp;apos;,&amp;apos;月收入&amp;apos;,&amp;apos;负债比例&amp;apos;,&amp;apos;可用额度比例&amp;apos;,&amp;apos;普通贷款数量&amp;apos;,&amp;apos;不动产贷款数量&amp;apos;,&amp;apos;35-69天逾期次数&amp;apos;,&amp;apos;60-89天逾期次数&amp;apos;,&amp;apos;高于90天逾期次数&amp;apos;,&amp;apos;严重违约&amp;apos;]]


# 手工探索数据
print(train_data.shape)
print(train_data.info())
print(train_data.isnull().sum())
print(train_data.describe().T)
print(train_data[&amp;apos;严重违约&amp;apos;].value_counts())
print(train_data[&amp;apos;严重违约&amp;apos;].sum()/train_data[&amp;apos;严重违约&amp;apos;].count())

# 使用EDA工具探索数据
plot(train_data)
&lt;/pre&gt;
 &lt;h3&gt;  &lt;strong&gt;数据预处理&lt;/strong&gt;&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;1）缺失值处理&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;常见方法：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;直接删除含有缺失值的样本&lt;/li&gt;
  &lt;li&gt;根据样本之间的相似性填补缺失值&lt;/li&gt;
  &lt;li&gt;根据变量之间的相关关系填补缺失值&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;存在缺失的特征：月收入、家属人数&lt;/p&gt;
 &lt;p&gt;这里假设一个人的月收入和家属人数和自身的 其他个人特征有关联，这里根据变量之间的相关关系采用随机森林法填补。&lt;/p&gt;
 &lt;pre&gt;from sklearn.ensemble import RandomForestRegressor

def fill_income_missing(data, to_fill):
    df = data.copy()
    columns = [*df.columns]
    columns.remove(to_fill)
    
    # 移除有缺失值的列
    columns.remove(&amp;apos;家属数量&amp;apos;)
    X = df.loc[:, columns]
    y = df.loc[:, to_fill]
    X_train = X.loc[df[to_fill].notnull()]
    y_train = y.loc[df[to_fill].notnull()]
    X_pred = X.loc[df[to_fill].isnull()]
    rfr = RandomForestRegressor(random_state=22, n_estimators=200, max_depth=3, n_jobs=-1)
    rfr.fit(X_train, y_train)
    y_pred = rfr.predict(X_pred).round()
    df.loc[df[to_fill].isnull(), to_fill] = y_pred
    return df

def fill_dependents_missing(data, to_fill):
    df = data.copy()
    columns = [*df.columns]
    columns.remove(to_fill)
    
    X = df.loc[:, columns]
    y = df.loc[:, to_fill]
    X_train = X.loc[df[to_fill].notnull()]
    y_train = y.loc[df[to_fill].notnull()]
    X_pred = X.loc[df[to_fill].isnull()]
    rfr = RandomForestRegressor(random_state=22, n_estimators=200, max_depth=3, n_jobs=-1)
    rfr.fit(X_train, y_train)
    y_pred = rfr.predict(X_pred).round()
    df.loc[df[to_fill].isnull(), to_fill] = y_pred
return df

train_data = fill_income_missing(train_data, &amp;apos;月收入&amp;apos;)
train_data = fill_dependents_missing(train_data, &amp;apos;家属数量&amp;apos;)
print(train_data.isnull().sum())
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;2) 异常值处理&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;a. 删除年龄为0的数据&lt;/p&gt;
 &lt;pre&gt;train_data = train_data.loc[train_data[&amp;apos;年龄&amp;apos;] &amp;gt; 0]&lt;/pre&gt;
 &lt;p&gt;b. 去除逾期次数中的异常数据&lt;/p&gt;
 &lt;pre&gt;import matplotlib.pyplot as plt

columns = [&amp;apos;35-69天逾期次数&amp;apos;,&amp;apos;60-89天逾期次数&amp;apos;,&amp;apos;高于90天逾期次数&amp;apos;]
train_data.loc[:, columns].plot.box(vert=False)

train_data = train_data[(train_data[&amp;apos;35-69天逾期次数&amp;apos;] &amp;lt; 90) &amp;amp; (train_data[&amp;apos;60-89天逾期次数&amp;apos;] &amp;lt; 90)  &amp;amp; (train_data[&amp;apos;高于90天逾期次数&amp;apos;] &amp;lt; 90)]
&lt;/pre&gt;
 &lt;h3&gt;  &lt;strong&gt;信用卡模型训练&lt;/strong&gt;&lt;/h3&gt;
 &lt;p&gt;这里直接使用  &lt;a href="https://github.com/Lantianzz/Scorecard-Bundle"&gt;Scorecard-Bundle&lt;/a&gt;这个Python包进行训练。Scorecard-Bundle是一个基于Python的高级评分卡建模API，实施方便且符合Scikit-Learn的调用习惯，包含的类均遵守Scikit-Learn的fit-transform-predict习惯。Scorecard-Bundle包括基于ChiMerge的特征离散化、WOE编码、基于信息值（IV）和共线性的特征评估、基于逻辑回归的评分卡模型、以及针对二元分类任务的模型评估。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;1） 特征离散化（ChiMerge）&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;from scorecardbundle.feature_discretization import ChiMerge as cm
from scorecardbundle.feature_discretization import FeatureIntervalAdjustment as fia
from scorecardbundle.feature_encoding import WOE as woe
from scorecardbundle.feature_selection import FeatureSelection as fs
from scorecardbundle.model_training import LogisticRegressionScoreCard as lrsc
from scorecardbundle.model_evaluation import ModelEvaluation as me

X = train_data.iloc[:, :-1]
y = train_data.iloc[:, -1]

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0, test_size=0.25)

trans_cm = cm.ChiMerge(max_intervals=10, min_intervals=5, output_dataframe=True)
result_cm = trans_cm.fit_transform(X_train, y_train) 
print(trans_cm.boundaries_) # 每个特征的区间切分
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;2） 特征编码（WOE）和评估（IV）&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;trans_woe = woe.WOE_Encoder(output_dataframe=True)
result_woe = trans_woe.fit_transform(result_cm, y_train)
print(trans_woe.iv_) # 每个特征的信息值 (iv)
print(trans_woe.result_dict_) # 每个特征的WOE字典和信息值 (iv)
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;3) 手动调整分箱&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;观察每一个特征的分布和响应率，确定分箱是否合理，如果不合理就需要人工设置边界。&lt;/p&gt;
 &lt;pre&gt;col = &amp;apos;年龄&amp;apos;
fia.plot_event_dist(result_cm[col],y_train,x_rotation=60)
new_x = cm.assign_interval_str(X_train[col].values,[22, 33, 43, 53, 62, 67, 74]) # apply new interval boundaries to the feature
woe.woe_vector(new_x, y_train.values) # check the information value of the resulted feature that applied the new intervals
fia.plot_event_dist(new_x,y_train, x_label=col,x_rotation=60)

feature_list = []
result_cm[col] = new_x # great explainability and predictability. Select.
feature_list.append(col)
print(feature_list)
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;4) WOE编码&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;完成全部特征的分组检查后，再次将分组特征进行WOE编码&lt;/p&gt;
 &lt;pre&gt;trans_woe = woe.WOE_Encoder(output_dataframe=True)
result_woe = trans_woe.fit_transform(result_cm[feature_list], y_train) 
print(result_woe.head())
print(trans_woe.iv_)
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;5) 特征选择&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;剔除预测力过低（通常用IV不足0.02筛选）、以及相关性过高引起共线性问题的特征。(相关性过高的阈值默认为皮尔森相关性系数大于0.6，可通过threshold_corr参数调整)&lt;/p&gt;
 &lt;pre&gt;fs.selection_with_iv_corr(trans_woe, result_woe) # corr_with 列示了与该特征相关性过高的特征和相关系数&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;6) 模型训练&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;model = lrsc.LogisticRegressionScoreCard(trans_woe, PDO=-20, basePoints=100, verbose=True)
model.fit(result_woe, y_train)
print(model.woe_df_) # 从woe_df_属性中可得评分卡规则
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;7) 模型校验&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;sc_table = model.woe_df_.copy()
result = model.predict(X_train[feature_list], load_scorecard=sc_table) # Scorecard should be applied on the original feature values
result_test = model.predict(X_test[feature_list], load_scorecard=sc_table) # Scorecard should be applied on the original feature values
result.head() # if model object&amp;apos;s verbose parameter is set to False, predict will only return Total scores

# Train
evaluation = me.BinaryTargets(y_train, result[&amp;apos;TotalScore&amp;apos;])
evaluation.plot_all()
  
# Validation
evaluation = me.BinaryTargets(y_test, result_test[&amp;apos;TotalScore&amp;apos;])
evaluation.plot_all()
&lt;/pre&gt;
 &lt;p&gt;模型相关数据：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="462" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/k-s-curve.png" width="648"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;KS指标: 用以评估模型对好、坏客户的判别区分能力，计算累计坏客户与累计好客户百分比的最大差距。KS值范围在0%-100%，判别标准如下：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;KS: &amp;lt;20% : 差&lt;/li&gt;
  &lt;li&gt;KS: 20%-40% : 一般&lt;/li&gt;
  &lt;li&gt;KS: 41%-50% : 好&lt;/li&gt;
  &lt;li&gt;KS: 51%-75% : 非常好&lt;/li&gt;
  &lt;li&gt;KS: &amp;gt;75% : 过高，需要谨慎的验证模型&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" height="462" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/roc.png" width="652"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;ROC曲线就越往左上方靠拢，它下面的面积(AUC)也就越大：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;如果AUC的值达到80，那说明分类器分类非常准确&lt;/li&gt;
  &lt;li&gt;如果AUC值在60～0.80之间，那分类器有优化空间，可以通过调节参数得到更好的性能&lt;/li&gt;
  &lt;li&gt;如果AUC值小于60，那说明分类器模型效果比较差&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" height="462" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/p-r.png" width="640"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;参考链接：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="http://shichen.name/slide/20171115scorecard/#1"&gt;信用评分卡模型开发与应用&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://www.yeeach.com/share/scorecard.png"&gt;R语言评分卡模型（公开版）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://cloud.tencent.com/developer/article/1448182"&gt;手把手教你搭建评分卡模型&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;div&gt;
  &lt;h3&gt;相关文章:&lt;/h3&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/imdb-sentiment-analysis-full-connection.html" rel="bookmark" title="&amp;#22810;&amp;#23618;&amp;#20840;&amp;#36830;&amp;#25509;&amp;#31070;&amp;#32463;&amp;#32593;&amp;#32476;&amp;#19982;&amp;#24773;&amp;#24863;&amp;#20998;&amp;#26512;"&gt;多层全连接神经网络与情感分析 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/cluster-score.html" rel="bookmark" title="&amp;#32858;&amp;#31867;&amp;#31639;&amp;#27861;&amp;#35780;&amp;#20272;&amp;#25351;&amp;#26631;"&gt;聚类算法评估指标 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/minimum-entropy-word-vector-dimension.html" rel="bookmark" title="&amp;#26368;&amp;#23567;&amp;#29109;&amp;#21407;&amp;#29702;&amp;#30830;&amp;#35748;&amp;#35789;&amp;#21521;&amp;#37327;&amp;#32500;&amp;#24230;"&gt;最小熵原理确认词向量维度 &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>器→工具 开源项目 数据 术→技巧 机器学习</category>
      <guid isPermaLink="true">https://itindex.net/detail/61198-%E9%A3%8E%E9%99%A9%E6%8E%A7%E5%88%B6-%E4%BF%A1%E7%94%A8%E8%AF%84%E5%88%86-%E6%A8%A1%E5%9E%8B</guid>
      <pubDate>Wed, 27 Jan 2021 20:13:13 CST</pubDate>
    </item>
    <item>
      <title>机器学习之恶意流量检测的特征工程</title>
      <link>https://itindex.net/detail/60300-%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0-%E6%B5%81%E9%87%8F-%E7%89%B9%E5%BE%81</link>
      <description>&lt;h2&gt;      &lt;strong&gt;背景&lt;/strong&gt;&lt;/h2&gt;
 &lt;p&gt;      &lt;strong&gt;   &lt;strong&gt;传统的机器学习除了使用Tfidf-ngram的方式外还有其他做特征工程方式，还可以通过定义不同维度的特征来做特征工程，这种特征工程方式需要安全工程师对每一种攻击有良好特征提取能力。这种方法举个例子来说可以这样理解，我的输入是姚明，此时我在特征工程阶段需要将姚明转化为身高2.2米、体重400斤等等数值特征，再经过标准化等转化为机器可以识别的量纲单位进行学习预测。&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;      &lt;img src="https://image.3001.net/images/20191226/1577341069_5e04508dc6450.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;      &lt;strong&gt;机器学习流程&amp;amp;&lt;/strong&gt;  &lt;strong&gt;特征工程&lt;/strong&gt;&lt;/h2&gt;
 &lt;p&gt;    传统的机器学习可以理解成是生产算法的算法。需要人来先做特征提取，然后在把特征向量化后交给机器去训练。为什么要做特征工程，有这么一句话在业界广泛流传：数据和特征决定了机器学习的上限，而模型和算法只是逼近这个上限而已。我们做特征工程的目的是为了让训练后的结果达到最优的状态。&lt;/p&gt;
 &lt;p&gt;      &lt;img src="https://image.3001.net/images/20191226/1577343601_5e045a71792b5.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;    本例中我们的目的是从流量中识别恶意流量，首先需要在所有的负例样本中筛选出最具代表的特征，在所有的负例样本中筛选出最具代表的特征，我们先从简单关键词特征开始。观察正例样本基本没有类似information_schema.table、 sleep() 、alert(/1/)这种的特殊字符。&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;   &lt;p&gt;format=xml&amp;amp;platform=ppap&amp;amp;channel=withoutchannelfilename=sgim_eng.zip&amp;amp;h=B2EF665558623D671FC19AC78CA2F0F3&amp;amp;v=8.0.0.8381&amp;amp;ifauto=1&lt;/p&gt;   &lt;p&gt;    md5=d10015a0eb30bd33bb917e1d527c649num=8&amp;amp;PaperCode=600054daid=41&amp;amp;clientuin=1264523260&lt;/p&gt;   &lt;p&gt;    clientkey=00015947C124007000F19A1CB5D10832A25TAG=ieg.qqpenguin.desktopdaid=41&amp;amp;clientuin=1264523260&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;    观察负例样本可以将如下负例样本看作是请求的value部份如  &lt;a href="http://www.xx.com/path?key1=value1&amp;key2=value2"&gt;http://x.x.x/path?key1=value1&amp;amp;key2=value2&lt;/a&gt;，可以观察到同类型攻击具有很多相同的特征，比如xss来说具有很多javascript、alert、onerror=等特征，sql注入具有information_schema、information_schema.table、select、from、where等关键词特征&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;&amp;lt;form id=&amp;quot;test&amp;quot; /&amp;gt;&amp;lt;button form=&amp;quot;test&amp;quot;formaction=&amp;quot;javascript:alert(123)&amp;quot;&amp;gt;TESTHTML5FORMACTION&amp;lt;scriptitworksinallbrowsers&amp;gt;/&amp;lt;script */alert(1)&amp;lt;/script&amp;lt;metahttp-equiv=&amp;quot;refresh&amp;quot; content=&amp;quot;0;url=javascript:confirm(1)&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&amp;lt;script&amp;gt;alert(/xss/)&amp;lt;/script&amp;gt;&amp;apos;&amp;gt;&amp;lt;script&amp;gt;alert(document.cookie)&amp;lt;/script&amp;gt;&amp;lt;form&amp;gt;&amp;lt;isindexformaction=&amp;quot;javascript&amp;amp;colon;confirm(1)&amp;quot;alert(String.fromCharCode(88,83,83));&amp;apos;))&amp;quot;&amp;gt;&amp;lt;inputtype=&amp;quot;text&amp;quot; value=``&amp;lt;div/onmouseover=&amp;apos;alert(1)&amp;apos;&amp;gt;X&amp;lt;/div&amp;gt;&amp;lt;BODY ONLOAD=alert(&amp;apos;helloxworldss&amp;apos;)&amp;gt;&amp;lt;![&amp;gt;&amp;lt;img src=&amp;quot;]&amp;gt;&amp;lt;img src=x onerror=alert(XSS)//&amp;quot;&amp;gt;&amp;lt;inputonblur=write(XSS) autofocus&amp;gt;&amp;lt;input autofocus&amp;gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;    本例抽取部份的xss负例样本，从中可以抽取的特征规则大致可以这样表示&lt;/p&gt;
 &lt;table&gt;


  &lt;tr&gt;
   &lt;td&gt;                    &lt;strong&gt;onmouseover=&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;onload=&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;onerror=&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;alert()&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;prompt()&lt;/strong&gt;            &lt;/td&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;                javascript:            &lt;/td&gt;
   &lt;td&gt;                &amp;lt;script&amp;gt;            &lt;/td&gt;
   &lt;td&gt;                &amp;lt;/script&amp;gt;            &lt;/td&gt;
   &lt;td&gt;                confirm()            &lt;/td&gt;
   &lt;td&gt;                onblur=            &lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;    即便如此仍然会存在很多变形特征，比如&amp;lt;script/**/&amp;gt;、console.log()、&amp;lt;Script&amp;gt;，所以进行一步完善特征工程这一次我们将特征分为两个维度，第一个维度是词特征，第二是符号纬度，同时对所有的大小写进行统一转换为小写，对于请求的value是url这种可能会存在很多特殊符号的链接特征，在本例中可以进行统一降噪转换为”x”避免受到此类特征等影响学习结果&lt;/p&gt;
 &lt;table&gt;


  &lt;tr&gt;
   &lt;td&gt;                    &lt;strong&gt;关键词纬度&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;&lt;/td&gt;
   &lt;td&gt;&lt;/td&gt;
   &lt;td&gt;&lt;/td&gt;
   &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;                Javascript:            &lt;/td&gt;
   &lt;td&gt;                script            &lt;/td&gt;
   &lt;td&gt;                Confirm=            &lt;/td&gt;
   &lt;td&gt;                Onblur=            &lt;/td&gt;
   &lt;td&gt;                Src=            &lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;                    &lt;strong&gt;Onmouseover=&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;Onload=&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;Onerror=&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;alert&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;prompt&lt;/strong&gt;            &lt;/td&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;                    &lt;strong&gt;符号纬度&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;&lt;/td&gt;
   &lt;td&gt;&lt;/td&gt;
   &lt;td&gt;&lt;/td&gt;
   &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;                &amp;gt;             &lt;/td&gt;
   &lt;td&gt;                ‘            &lt;/td&gt;
   &lt;td&gt;                ”            &lt;/td&gt;
   &lt;td&gt;                *            &lt;/td&gt;
   &lt;td&gt;                /            &lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;                    &lt;strong&gt;=&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;:&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;（&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;）&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;&amp;lt; &lt;/strong&gt;            &lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;                `            &lt;/td&gt;
   &lt;td&gt;                \            &lt;/td&gt;
   &lt;td&gt;                ；            &lt;/td&gt;
   &lt;td&gt;                &amp;lt;             &lt;/td&gt;
   &lt;td&gt;                &amp;gt;             &lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;    这里特别说明一下特征除此之外的纬度还有很多，playload的长度、请求响应时间、该ip或该来源指纹请求次数等等，这里我们只用了2个维度来简单说明。我们继续将特征进行进行量化，可以大致得到如下每条playload内容对应的特征向量&lt;/p&gt;
 &lt;table&gt;


  &lt;tr&gt;
   &lt;td&gt;                    &lt;strong&gt;playload&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;关键词纬度&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;符号维度&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;特征编码&lt;/strong&gt;            &lt;/td&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;                &amp;lt;form id=”test” /&amp;gt;&amp;lt;button form=”test” formaction=”javascript:alert(1)”&amp;gt;            &lt;/td&gt;
   &lt;td&gt;                javascriptalert            &lt;/td&gt;
   &lt;td&gt;                ():”&amp;quot;&amp;lt;&amp;gt;            &lt;/td&gt;
   &lt;td&gt;                [2,7]            &lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;                num=”&amp;gt;&amp;lt;img src=x onerror=window.open(‘    &lt;a href="https://www.x.com/');"&gt;https://www.x.com/’);&lt;/a&gt;&amp;gt;            &lt;/td&gt;
   &lt;td&gt;                onerror            &lt;/td&gt;
   &lt;td&gt;                “&amp;gt;&amp;lt;’/:            &lt;/td&gt;
   &lt;td&gt;                [2,6]            &lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;    在处理请求的过程中难免会出现特质编码[8,40],[9,35]类似这样具有奇异性的特征编码，在机器学习领域中我们需要将量纲和量纲单位限定在一个空间同样的数量级范围内，经过处理后的数据会消除奇异值带来的影响，以便我们进行综合对比评判。&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;min-max标准化&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;    这里简单说一下标准化，min-Max就是把数据按比例缩放，使之落入一个小的空间里。同时不改变原有的正态分布，特征数据的取值范围并不在[ 0，1 ]之间，着点跟归一化不同。如下其中X代表要转换的对象，[New_max，New_min]代表范围区间。&lt;/p&gt;
 &lt;p&gt;      &lt;img src="https://image.3001.net/images/20191226/1577343742_5e045afed21d5.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;收入范围最低收入12000，最高收入98000，标准化映射到[0，1]之间，现在要将一个人收入是73600进行标准化，映射后的结果如下  &lt;img src="https://image.3001.net/images/20191226/1577289809_5e038851ed9fb.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;      &lt;strong&gt;代码示例&lt;/strong&gt;&lt;/h2&gt;
 &lt;p&gt;    数据预处理，这里正负样本数据结构就不再重复了，保证一个都是恶意样本，一个是正常样本即可。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;def loadFile():
    badXss = &amp;quot;./badx.txt&amp;quot;
    goodXss = &amp;quot;./goox.txt&amp;quot;
    bf = [x.strip().lower() for x inopen(badXss, &amp;quot;r&amp;quot;).readlines()]
    gf = [x.strip().lower() for x inopen(goodXss, &amp;quot;r&amp;quot;).readlines()]
    return bf, gf&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    特征工程阶段，一条playload或者正常样本后进行特征提取，特征拆分成两个维度，一个维度是关键词特征，一个维度是关键符号特征&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;def MakeFeature(x):
    charList = [&amp;quot;onmouseover=&amp;quot;,&amp;quot;onload=&amp;quot;, &amp;quot;onerror=&amp;quot;, &amp;quot;javascript&amp;quot;,&amp;quot;alert&amp;quot;, &amp;quot;src=&amp;quot;, &amp;quot;confirm&amp;quot;, &amp;quot;onblur&amp;quot;]
    markList = [&amp;quot;=&amp;quot;, &amp;quot;:&amp;quot;,&amp;quot;&amp;gt;&amp;quot;, &amp;quot;&amp;lt;&amp;quot;, &amp;apos;&amp;quot;&amp;apos;, &amp;quot;&amp;apos;&amp;quot;, &amp;quot;)&amp;quot;,&amp;quot;(&amp;quot;, &amp;quot;.&amp;quot;]
    featureList = []
    for i in x:
        char_count, mark_count = 0, 0
        payload =urllib.parse.unquote(i.lower().strip())
        for charts in charList:
            char_count = payload.count(charts)+ char_count
        for marks in markList:
            mark_count = payload.count(marks) +mark_count
        featureList.append([char_count,mark_count])
    return featureList&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    训练阶段拆分40%测试集，这里笔者使用了多个算法分别进行训练，目的是想看一下几个算法在相同数据集下的训练时间和效果&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;def train(x, y):
    x_train, x_test, y_train, y_test =train_test_split(x, y, test_size=0.4, random_state=666)
    param = {&amp;apos;n_estimators&amp;apos;: 200, &amp;apos;max_depth&amp;apos;:200, &amp;apos;min_samples_split&amp;apos;: 2, &amp;apos;learning_rate&amp;apos;: 0.01}
    NBM = [MultinomialNB(alpha=0.01),  # 多项式模型-朴素贝叶斯
           BernoulliNB(alpha=0.01),
          DecisionTreeClassifier(max_depth=100),
          RandomForestClassifier(criterion=&amp;apos;gini&amp;apos;, max_depth=100,n_estimators=200),
           LogisticRegression(random_state=40,solver=&amp;apos;lbfgs&amp;apos;, max_iter=10000, penalty=&amp;apos;l2&amp;apos;,multi_class=&amp;apos;multinomial&amp;apos;,class_weight=&amp;apos;balanced&amp;apos;, C=100),
           LinearSVC(class_weight=&amp;apos;balanced&amp;apos;,random_state=100, penalty=&amp;apos;l2&amp;apos;,loss=&amp;apos;squared_hinge&amp;apos;, C=0.92, dual=False),
           SVC(kernel=&amp;apos;rbf&amp;apos;, gamma=0.7, C=1),
           # GradientBoostingClassifier(param)
           ]
    NAME = [&amp;quot;多项式&amp;quot;, &amp;quot;伯努利&amp;quot;, &amp;quot;决策树&amp;quot;, &amp;quot;随机森林&amp;quot;, &amp;quot;线性回归&amp;quot;, &amp;quot;linerSVC&amp;quot;, &amp;quot;svc-rbf&amp;quot;]
    for model, modelName in zip(NBM, NAME):
        model.fit(x_train, y_train)
        pred = model.predict(x_test)
        dts = len(np.where(pred == y_test)[0])/ len(y_test)
        print(&amp;quot;{} 精度:{:.5f} &amp;quot;.format(modelName, dts * 100))
        joblib.dump(model, &amp;apos;./model.pkl&amp;apos;)&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    预测概率，加载模型进行精度预测&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;def predicts(x):
    clf = joblib.load(&amp;apos;./model.pkl&amp;apos;)
    return clf.predict(x)

def run():
    badx, goodx = loadFile()
    goodx = MakeFeature(goodx)
    badx = MakeFeature(badx)
    goody = [0] * len(goodx)
    bady = [1] * len(badx)
    min_max_scaler = preprocessing.MinMaxScaler()
    X_train_minmax =min_max_scaler.fit_transform(bady)
    x = np.array(goodx + badx).reshape(-1, 2)
    y = np.array(goody + bady).reshape(-1, 1)
    train(x, y)
     testX =[&amp;quot;&amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt;&amp;quot;, &amp;quot;123123sadas&amp;quot;,&amp;quot;onloads2s&amp;quot;, &amp;quot;scriptsad23asdasczxc&amp;quot;,&amp;quot;onload=alert(1)&amp;quot;]
     x =MakeFeature(testX)
     forres, req in zip(predicts(x), testX):
         print(&amp;quot;XSS==&amp;gt;&amp;quot; if res == 1else &amp;quot;None==&amp;gt;&amp;quot;, req)&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    预测结果&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;        XSS==&amp;gt;&amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt;    &lt;/p&gt;
  &lt;p&gt;        None==&amp;gt; 123123sadas    &lt;/p&gt;
  &lt;p&gt;        None==&amp;gt; onloads2s    &lt;/p&gt;
  &lt;p&gt;        None==&amp;gt;scriptsad23asdasczxc    &lt;/p&gt;
  &lt;p&gt;        XSS==&amp;gt;onload=alert(1)“””    &lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h2&gt;      &lt;strong&gt;结果&lt;/strong&gt;&lt;/h2&gt;
 &lt;p&gt;    可以看到由于特征工程阶段做的特征维度不够多特征保留的不够充分，在白样本中存在大量的干扰特征，导致最后准确率召回率都不是很高，精度大约只有74%左右，使用这种特征工程的方法笔者不是很推荐，虽然有监督方式的机器学习具有良好的可解释性，但是维护特征是一个永无止尽的过程，难度你真的想有多少智能就有多少人工吗。 ：D&lt;/p&gt;
 &lt;table&gt;


    

  &lt;tr&gt;
   &lt;th&gt;                    &lt;strong&gt;模型名称&lt;/strong&gt;            &lt;/th&gt;
   &lt;th&gt;                    &lt;strong&gt;预测精度&lt;/strong&gt;            &lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;                    &lt;strong&gt;多项式&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;74.9%&lt;/strong&gt;            &lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;                    &lt;strong&gt;伯努利&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;72.5%&lt;/strong&gt;            &lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;                    &lt;strong&gt;决策树&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;73.9%&lt;/strong&gt;            &lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;                    &lt;strong&gt;线性回归&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;72.5%&lt;/strong&gt;            &lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;                    &lt;strong&gt;SVM&lt;/strong&gt;            &lt;/td&gt;
   &lt;td&gt;                    &lt;strong&gt;74.7%&lt;/strong&gt;            &lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;  &lt;strong&gt;    *本文原创作者：邹先生007，本文属于FreeBuf原创奖励计划，未经许可禁止转载&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>WEB安全 恶意流量 机器学习 特征工程</category>
      <guid isPermaLink="true">https://itindex.net/detail/60300-%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0-%E6%B5%81%E9%87%8F-%E7%89%B9%E5%BE%81</guid>
      <pubDate>Tue, 14 Jan 2020 10:00:43 CST</pubDate>
    </item>
    <item>
      <title>支撑位和压力位的原理是什么？支撑位和压力位是怎么形成的</title>
      <link>https://itindex.net/detail/60217-%E5%8E%8B%E5%8A%9B-%E5%8E%9F%E7%90%86-%E5%8E%8B%E5%8A%9B</link>
      <description>&lt;div&gt; 
   
常常听到一些大V或者是股市高手说支撑位压力位的相关词汇，那么这个支撑位和压力位一般是怎么形成的呢?它们的形成往往是蕴含着什么道理呢?请往下看。&lt;/div&gt;
 &lt;div&gt;  &lt;br /&gt;&lt;/div&gt;
 &lt;div&gt;　　支撑位和压力位的原理是什么?&lt;/div&gt;
 &lt;div&gt;  &lt;br /&gt;&lt;/div&gt;
 &lt;div&gt;　　支撑位和压力位的形成往往是股市投资者的心理导致的，所以市场在这个位置发生的波动常常是有理可循的。具体支撑位和压力位一般情况下都有以下几个依据：第一是筹码密集成交带;第二是均线的走势;第三是前高或者新低位置;第三就是顶部盘整地带和底部盘整地带。这些位置往往能给人持股信心和压力，因而也就形成了支撑位和阻力位。&lt;/div&gt;
 &lt;div&gt;  &lt;br /&gt;&lt;/div&gt;
 &lt;div&gt;　　支撑位和压力位一般都是市场预期形成的，例如某股主力的持仓成本在15元左右，市场上绝大多数散户的持仓成本在16元左右，那么最终散户会进行补仓，主力会维持股价，该股的支撑位很有可能就会在15.2—15.4之间实现。&lt;/div&gt;
 &lt;div&gt;  &lt;br /&gt;&lt;/div&gt;
 &lt;div&gt;　　反过来说，某股主力的持仓成本在15元左右，市场上绝大多数散户的持仓成本在16元左右，那么主力拉升的话，它势必要突破16元才能往更高的价格走，若是不突破的话，那么主力拉升价位必须选择一个散户又想卖，又不敢卖的位置，也就是15.7-15.85之间。这个位置主力能实现最大收益，同时散户筹码又不会松动。&lt;/div&gt;
 &lt;div&gt;  &lt;br /&gt;&lt;/div&gt;
 &lt;div&gt;　　长而久之，这个股票就会形成一个15.2-15.4之间的支撑，15.7-15.85之间的压力，故支撑位和压力位也就正式形成了。&lt;/div&gt; &lt;br /&gt; &lt;img src="http://simg.sinajs.cn/blog7style/images/special/1265.gif"&gt;&lt;/img&gt; &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>学习类</category>
      <guid isPermaLink="true">https://itindex.net/detail/60217-%E5%8E%8B%E5%8A%9B-%E5%8E%9F%E7%90%86-%E5%8E%8B%E5%8A%9B</guid>
      <pubDate>Fri, 13 Dec 2019 14:19:32 CST</pubDate>
    </item>
    <item>
      <title>Nimbus: Hulu的深度学习平台</title>
      <link>https://itindex.net/detail/59497-nimbus-hulu-%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0</link>
      <description>&lt;h2&gt;  &lt;strong&gt;背景&lt;/strong&gt;  &lt;br /&gt;&lt;/h2&gt;



 &lt;p&gt;Hulu是美国领先的互联网专业视频服务平台，目前在美国拥有超过2500万付费用户。Hulu的目标是帮助用户在任意时刻、任何地点、以任何方式查找并欣赏到高质量的电视剧、电影和电视直播。实现这一目标离不开各个团队的努力，而AI在其中扮演者越来越重要的角色。在Hulu, 我们拥有诸多的researcher团队，如广告团队，推荐团队，视频理解团队等ji等。早期的时候，大家还是各自为战，以“小作坊”的形式构建机器学习相关环境。这种非集中式的生产模式存在着许多弊端。概括来说就是无法共享资源（计算资源，数据资源，尤其是昂贵的GPU资源），无法共享经验（模型或者框架优化）以及存在很高的额外的环境搭建和运维成本（严重影响了researcher的工作效率）。基于此，我们构建了AI Platform这个跨Team的组织。&lt;/p&gt;



 &lt;p&gt;整个AI Platform可以分为三层，从上到下依次是AI服务层，ML数据层，基础架构层。AI服务层主要是用于线上模型管理和部署，涉及到CICD, 监控，负载均衡等服务相关内容。ML数据层则包含了机器学习所用到的数据源，包含经过多个ETL pipeline清洗后生成的用于存放特征的数据仓库。最底层则是基础架构层，包括分布式存储，计算和调度等等，这也是我们基础架构团队主要负责的一块。&lt;/p&gt;



 &lt;div&gt;  &lt;img alt="" src="http://dongxicheng.org/wp-content/uploads/2019/04/image-45.png"&gt;&lt;/img&gt;&lt;/div&gt;



 &lt;p&gt;一个机器学习作业的生命周期大致可以分为数据准备阶段，模型训练阶段和模型服务阶段。在Hulu, 其对应的基础设施如下图所示。&lt;/p&gt;



 &lt;div&gt;  &lt;img alt="" src="http://dongxicheng.org/wp-content/uploads/2019/04/image-46.png"&gt;&lt;/img&gt;&lt;/div&gt;



 &lt;p&gt;从图中可以看出，Hulu已经拥有大部分ML所需的基础设施，包括线上服务部分以及离线计算所能用到的Hadoop相关的基础设施等。这已经能满足一定程度的机器学习需求，但是Spark / Flink MLlib主要面向传统的机器学习算法，我们缺少了一套专门的机器学习平台，从而可以支持对环境要求更加复杂的机器学习框架尤其是深度学习框架。于是，我们研发了Nimbus。&lt;/p&gt;



 &lt;h2&gt;  &lt;strong&gt;技术选型&lt;/strong&gt;&lt;/h2&gt;



 &lt;h3&gt;  &lt;strong&gt;Docker&lt;/strong&gt;&lt;/h3&gt;



 &lt;p&gt;Docker是我们技术选型中最先确定的方向。机器学习平台一个很重要的需求就是对TensorFlow, Caffe, Pytorch，XGBoost等多种机器学习框架的支持，每种机器学习框架还有多种不同的版本，即使是统一版本也有不同的环境依赖，比如CUDA版本，Python版本等等。而Docker作为一个当下盛行的开源容器引擎，可以为任何模型训练创建一个轻量级的、可移植的、可隔离的容器环境，从而可以轻松应对这些需求。&lt;/p&gt;



 &lt;h3&gt;  &lt;strong&gt;Mesos&lt;/strong&gt;&lt;/h3&gt;



 &lt;p&gt;选定Docker作为底层引擎之后，下一个需要抉择的便是资源管理和编排框架。当时主要考虑的Yarn,Mesos以及Kubernetes。 在选型中，一个很重要的考量是对gpu的支持上。Yarn基本上从yarn 3.1真正开始支持将gpu作为底层可分配资源，而hulu大规模使用的都是基于cdh5.7.3的2.6版本，虽然可以通过打一些补丁进行支持，但是涉及的集群规模很大、服务众多，存在较大的风险。于此同时，yarn与docker的结合也并不是很完美。剩下的Mesos和Kubernetes都可以满足我们的需求，但是基于当时的软件成熟度、运维复杂度以及经验积累等方面的考量，我们最终选择了Mesos。&lt;/p&gt;



 &lt;h3&gt;  &lt;strong&gt;Framework&lt;/strong&gt;&lt;/h3&gt;



 &lt;p&gt;Framework是Mesos上存在的一种概念。Mesos实现了两级调度架构。第一级调度位于Mesos Master的守护进程，负责管理Mesos集群中所有节点上运行的Slave守护进程。Mesos slave将它的可用资源汇报给master，master再将资源分配给对应的Framework，由Framework选择接受整个、部分或者拒绝这个资源。所有接受的资源之后将由Framework负责调度。Mesos上开源的Framework并不是特别的多，起初我们选择Marathon,  并基于此完成了第一个版本，可以实现分布式TensorFlow, XGBoost的训练。很快，我们发现了其存在的一些局限性。其中最主要的原因在于Marathon更适合于长服务类型的作业，在短作业的生命周期管理上不够灵活。另外一点在于采用Marathon-LB作为服务发现引擎时，在分布式训练中，所有流量都需要经过Marathon-LB，从而带来严重的网络性能问题。之后，我们便采用了Hulu自研的针对短作业的高吞吐的调度引擎CapOS作为Mesos之上的调度框架。&lt;/p&gt;



 &lt;h2&gt;  &lt;strong&gt;架构设计&lt;/strong&gt;&lt;/h2&gt;



 &lt;p&gt;整体架构主要分为三块，Nimbus管理层，计算层和存储层。&lt;/p&gt;



 &lt;div&gt;  &lt;img alt="" src="http://dongxicheng.org/wp-content/uploads/2019/04/image-47.png"&gt;&lt;/img&gt;&lt;/div&gt;



 &lt;p&gt;存储层主要包含了三块。（1）用于存储模型代码的Github以及基于Http的文件服务器。（2）Hulu内部用于存放镜像的镜像仓库Cubox。（3）用于存放海量的训练数据的多种分布式存储，如HDFS, S3, Ceph以及Hulu内部专门用作小文件存储的Framehouse等。&lt;/p&gt;



 &lt;p&gt;计算层主要采用了Mesos+Docker的解决方案，上层采用Capos作为调度平台，最终运行在IaaS层提供的物理机或者虚拟机之上。&lt;/p&gt;



 &lt;p&gt;Nimbus管理层上，通过封装和抽象，Nimbus对外提供了包括客户端、网页以及Restful API的访问接口，在这可以对机器学习任务进行一站式管理。Nimbus Server中包含了一些列AI作业管理的逻辑。&lt;/p&gt;



 &lt;div&gt;  &lt;img alt="" src="http://dongxicheng.org/wp-content/uploads/2019/04/image-48.png"&gt;&lt;/img&gt;&lt;/div&gt;



 &lt;p&gt;对于一个AI作业，Nimbus会根据框架类型和运行模式执行不同的处理逻辑。就运行模式而言，无非是分为单机模式和分布式模式。单机模式比较简单，一般直接对应一个容器。分布式模式则会相对复杂。目前主流的分布式训练主要包含一下几种方式。&lt;/p&gt;



 &lt;div&gt;  &lt;img alt="" src="http://dongxicheng.org/wp-content/uploads/2019/04/image-49.png"&gt;&lt;/img&gt;&lt;/div&gt;



 &lt;p&gt;其中模型并行和流水线并行需要依赖于特定模型结构支持，因此并不具有通用型。作为平台方，我们主要考虑支持的是数据并行。数据并行一个主流的方案的是采用Parameter Server架构，这也是TensorFlow默认的方案。&lt;/p&gt;



 &lt;div&gt;  &lt;img alt="" src="http://dongxicheng.org/wp-content/uploads/2019/04/image-50.png"&gt;&lt;/img&gt;&lt;/div&gt;



 &lt;p&gt;  &lt;br /&gt;&lt;/p&gt;



 &lt;p&gt;我们利用CapOS提供的Programing API接口，实现了针对TensorFlow PS架构的Application Master，包含资源申请， 拓扑管理，组件间服务发现等环节。相较于之前同于LB的方式进行服务发现，这里采用了端到端直连的方式进行通信，可以尽量分摊网络开销，优化整体网络性能。&lt;/p&gt;



 &lt;p&gt;&lt;/p&gt;



 &lt;div&gt;  &lt;img alt="" src="http://dongxicheng.org/wp-content/uploads/2019/04/image-51.png"&gt;&lt;/img&gt;&lt;/div&gt;



 &lt;p&gt;  &lt;br /&gt;&lt;/p&gt;



 &lt;p&gt;相对PS模式之外另一种数据并行方案采用的Allreduce模式，根据实现方式不同又可以细分为Ring Allreduce、Tree Allreduce等等。XGBoost的分布式实现就是基于Allreduce模式。同样的，我们也需要实现其对应的Application Master逻辑。  &lt;br /&gt;&lt;/p&gt;



 &lt;h2&gt;  &lt;strong&gt;优化和改进&lt;/strong&gt;&lt;/h2&gt;



 &lt;p&gt;作为平台提供方，我们也一直致力于在功能性、易用性、性能等方面进行改进。下面将罗列一些比较重要的点。&lt;/p&gt;



 &lt;h3&gt;  &lt;strong&gt;框架支持&lt;/strong&gt;&lt;/h3&gt;



 &lt;p&gt;随着AI的火热发展，各种机器学习框架层出不穷。由于人力有限，我们也不可能面面俱到，对所有框架进行集成。根据目前Hulu的使用情况，我们已经完成了对TensorFlow, Pytorch, Caffe2,XGBoost, Gensim, LightGBM的支持。在框架集成上，除了完成一系列和我们底层存储对接外，很重要的一步在于镜像构建。在这里我们也做很多工作，包括&lt;/p&gt;



 &lt;p&gt;（1）一些源码的改造，使XGBoost分布式模式可以运行在容器之中，Caffe2的编译脚本的改造使得可以自动build出各种CUDA、OpenCV组合下的镜像。&lt;/p&gt;



 &lt;p&gt;（2）一系列的构建脚本，可以快速的完成对已支持框架的新版本的测试和集成。&lt;/p&gt;



 &lt;p&gt;（3）利用一些镜像优化手段如多阶段构建、基础层共享以及基础镜像提前分发等手段减少镜像下载时间，从而降低启动延迟。&lt;/p&gt;



 &lt;p&gt;（4）此外，Nimbus也支持自定义镜像，方便用户使用一些独特的计算环境或者框架。我们提供了一些模板镜像，方便用户进行镜像制作。&lt;/p&gt;



 &lt;h3&gt;  &lt;strong&gt;调试工具&lt;/strong&gt;&lt;/h3&gt;



 &lt;p&gt;调试是模型开发阶段必不可少的环境。一般而言，本地运行都会比远端执行更容易调试。为了较少这部分影响，我们提供了一些列工具。除了常规的作业日志和监控系统外，我们也集成了Terminal，可以远程登陆到机器内部进行调试。集成了Jupyter notebook, 并且提供debug模式，内置了nimbus-run和nimbus-tensorboard等命令，可以很方便的在容器内进行边修改，边调试运行，边看结果，尽可能实现和本地调试一样的体验。由于debug模式同实际运行使用了同样的资源，为了避免资源浪费，我们会有时间限制，到期后除非续约，否则将自动回收资源。/在这种模式下，Jupyter主要用作debug场景，我们也在计划构建真正意义的Jupyter as a Service，可以取代reacher的工作站，完成云端编码、测试、训练等一条龙服务。  &lt;br /&gt;&lt;/p&gt;



 &lt;h3&gt;  &lt;strong&gt;调度策略优化&lt;/strong&gt;&lt;/h3&gt;



 &lt;p&gt;CapOS默认的调度策略针对高吞吐做了大量优化，缺乏针对机器学习任务特别是分布式机器学习任务的优化。得益于其提供的灵活的Programming API，在默认策略之上我们封装了一层的针对AI作业的调度策略。&lt;/p&gt;



 &lt;p&gt;（1）数据中心自动选择。Hulu目前同时使用了多套数据中心，包括自己本地构建的两个数据中心以及云上的多个数据中心。Nimbus提供了对多个数据中心进行调度的能力。用户可以自己选择数据中心也可以由系统进行设置。数据中心一旦配置错误，将带来大量的跨数据中心的访问，极大的影响模型训练的速度。为此，我们加入了对数据源的位置感知能力，系统根据AI作业的输入和输出信息，以及各个数据中心的资源使用情况，可以自动选择最为合适的数据中心。&lt;/p&gt;



 &lt;p&gt;（2）GPU 优先的调度策略。GPU对于机器学习尤其是深度学习来说是一种极为重要的资源。在当前Nimbus集群中，同时存在了GPU作业和一般的CPU作业。在之前调度逻辑中，CPU作业可能会占据GPU机器上的CPU和内存资源，导致GPU任务因为CPU或者内存不足而没法调度出去，从而可能引发GPU资源闲置。在之后，我们对GPU机器做了额外的保护，尽量避免CPU作业抢占GPU的资源，只有当GPU机器出现闲置并且CPU机器不足以满足的情况下才会进行调度。&lt;/p&gt;



 &lt;p&gt;（3）分布式任务拓扑感知。分布式训练主要瓶颈在于多机之间的通信开销。因此我们需要分布式训练的各个节点尽可能位于同一机房，同一机架甚至同一机器上。在PS架构下，PS 节点作为中心节点，网络流量往往非常大，很容易把带宽跑满，在这种场景下需要尽可能把PS节点打散到不同的宿主机上，从而分摊网络流量。我们当前的版本还是基于一个比较粗的力度，这一点不如K8S，其可以利用Pod Affinity很容易实现更细粒度的优化。&lt;/p&gt;



 &lt;h3&gt;  &lt;strong&gt;统一的IO层&lt;/strong&gt;&lt;/h3&gt;



 &lt;p&gt;虽然用户的模型代码几乎可以不用修改就可以直接运行在Nimbus之上，然而在大规模机器学习训练过程中，势必需要引入分布式存储。从本地存储转到分布式存储，这将带来很大的使用习惯的改变。尽管主流分布式存储也都支持POSIX标准，然而这会降低IO性能，影响训练效率。为了减少这层gap，我们引入了一个统一的IO层，提供常用的文件Open,List, Delete接口，对外屏蔽底层文件系统的差异。所有的IO请求都将根据schema不同自动路由到不同文件系统之中。&lt;/p&gt;



 &lt;h3&gt;  &lt;strong&gt;开源社区合作&lt;/strong&gt;&lt;/h3&gt;



 &lt;p&gt;Nimbus相关生态系统中有大量的来源于开源社区的组件。作为平台方，我们也一直紧密关注着开源社区尤其是机器学习框架的动态。汲取社区力量更好的服务Hulu内部用户，也把Hulu的需求和优化回馈给社区。我们向XGBoost社区反馈了分布式模式下的文件处理的bug并解决后贡献给开源社区（dmlc-core/pull/452）。后续我们也会和社区保持紧密的联系，共同推动社区的良性发展。&lt;/p&gt;



 &lt;pre&gt;  &lt;strong&gt;   &lt;em&gt;作者简介：苏经纬，Hulu AI基础架构研发工程师，专注于AI、容器、容器编排等技术。&lt;/em&gt;&lt;/strong&gt;&lt;/pre&gt;



 &lt;p&gt;  &lt;br /&gt;&lt;/p&gt;
 &lt;div&gt;  &lt;p&gt;   &lt;strong&gt;原创文章，转载请注明：&lt;/strong&gt; 转载自   &lt;a href="http://dongxicheng.org/"&gt;董的博客&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;   &lt;strong&gt;本文链接地址:&lt;/strong&gt;    &lt;a href="http://dongxicheng.org/ai/ai-infrastructure-in-hulu/"&gt;Nimbus: Hulu的深度学习平台&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;   &lt;strong&gt;微信公众号：hadoop-123&lt;/strong&gt;，专注于大数据技术分享，欢迎加入！&lt;/p&gt;&lt;/div&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>人工智能 AI 深度学习</category>
      <guid isPermaLink="true">https://itindex.net/detail/59497-nimbus-hulu-%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0</guid>
      <pubDate>Wed, 03 Apr 2019 23:37:08 CST</pubDate>
    </item>
    <item>
      <title>分享：个人是怎么学习新知识的</title>
      <link>https://itindex.net/detail/59433-%E5%88%86%E4%BA%AB-%E4%B8%AA%E4%BA%BA-%E5%AD%A6%E4%B9%A0</link>
      <description>&lt;p&gt;挺多童鞋问我是怎么学习新知识的，干脆写篇文章总结一下，希望对大家有所帮助。对照书、技术博客、极客时间等学习的方式我就不说了。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://www.itmuch.com/#&amp;#19968;&amp;#12289;&amp;#26089;&amp;#26399;" title="&amp;#19968;&amp;#12289;&amp;#26089;&amp;#26399;"&gt;&lt;/a&gt;一、早期&lt;/h2&gt; &lt;p&gt;在15年及更早，由于知识储备少，基础偏弱，大致采取了如下的步骤：&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://www.itmuch.com/#1-1-&amp;#20837;&amp;#38376;&amp;#65306;&amp;#25214;&amp;#25945;&amp;#23398;&amp;#35270;&amp;#39057;" title="1.1 &amp;#20837;&amp;#38376;&amp;#65306;&amp;#25214;&amp;#25945;&amp;#23398;&amp;#35270;&amp;#39057;"&gt;&lt;/a&gt;1.1 入门：找教学视频&lt;/h3&gt; &lt;p&gt;了解xx是什么，能解决什么问题。例如个人学习Spring、Struts、Hibernate时，就是找了 马士兵 老师的视频。&lt;/p&gt;
 &lt;p&gt;值得一提的是，记笔记非常重要，一是可以形成相对完整的知识体系，二来也能应对面试——面试之前花点时间看看笔记就能很快记忆唤醒。  &lt;strong&gt;个人原创学习笔记可关注公众号“IT牧场”，点击 资源领取 即可获得&lt;/strong&gt;。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://www.itmuch.com/#1-2-&amp;#23454;&amp;#25112;&amp;#65306;&amp;#27169;&amp;#25311;&amp;#39033;&amp;#30446;" title="1.2 &amp;#23454;&amp;#25112;&amp;#65306;&amp;#27169;&amp;#25311;&amp;#39033;&amp;#30446;"&gt;&lt;/a&gt;1.2 实战：模拟项目&lt;/h3&gt; &lt;p&gt;俗话说，学以致用，为学而学是没有意义的——即使有意义，过段时间也会遗忘。所以个人在掌握基础知识点以及常见用法后，一般都会做个简单的项目。作用主要有几点：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;巩固知识点&lt;/li&gt;
  &lt;li&gt;总结最佳实践&lt;/li&gt;
  &lt;li&gt;锻炼自己的产品思维&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;从15年起，个人每年都会定”做一个   &lt;code&gt;Side Project&lt;/code&gt; “的KPI——这个项目能承载如上三点作用即可。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;15年：基于当时的主流框架捣鼓的快速开发脚手架    &lt;a href="https://gitee.com/itmuch/platform" rel="noopener" target="_blank"&gt;Platform&lt;/a&gt;（选型较老，已被时代抛弃） ；&lt;/li&gt;
  &lt;li&gt;16年：基于Spring Boot的半成品    &lt;a href="https://gitee.com/mm808/zb" rel="noopener" target="_blank"&gt;CMS&lt;/a&gt; （起因是来了个私活儿，于是开工，后来合作崩了，就废弃了）；&lt;/li&gt;
  &lt;li&gt;17年：基于Spring Cloud的快速开发脚手架与最佳实践总结    &lt;a href="https://gitee.com/itmuch/spring-cloud-yes" rel="noopener" target="_blank"&gt;Spring Cloud YES&lt;/a&gt; ，现已升级到Spring Cloud Greenwich SR1版本；&lt;/li&gt;
  &lt;li&gt;18年：微信小程序    &lt;code&gt;IT牧场&lt;/code&gt; ，在公众号导航栏点击”资源领取”即可进入，近期开源。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;顺便说下，这些项目的内核基本一样，但每次又有优化——每次拷贝老代码前，都再思考一下，看有没有改进的空间——这不是正是”重构”？既是对自己过去代码的重构，也是对自己技术的重新审视。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://www.itmuch.com/#&amp;#20108;&amp;#12289;15&amp;#24180;&amp;#21518;" title="&amp;#20108;&amp;#12289;15&amp;#24180;&amp;#21518;"&gt;&lt;/a&gt;二、15年后&lt;/h2&gt; &lt;p&gt;15年后，由于工作好几年了，知识面、知识深度都达到一定程度——特别是15年初自学Hadoop以及Spark后。&lt;/p&gt;
 &lt;blockquote&gt;
  &lt;p&gt;   &lt;strong&gt;一点趣闻&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;学习Hadoop和Spark的契机：当时大数据很火，工资很高，于是面向工资编程，想转型大数据。当时正好所在公司高层变动非常频繁，大佬们都忙着政治斗争——倒是爽了我这种小技术经理以及开发兄弟，整整一个半月，没有任何需求。于是投入全日制投入学习，偶尔改改bug。&lt;/p&gt;
  &lt;p&gt;后来发现，学习Hadoop和Spark是个笑话——因为学完发现在南京找不到大数据岗位——&lt;/p&gt;
  &lt;ul&gt;
   &lt;li&gt;面试中兴，技术通过了，开17K，部长面也过了，结果卡在学习，说民办本二算本三，不符合他们的学历要求，我也是醉了。&lt;/li&gt;
   &lt;li&gt;面试鸿信：对方说自己有1T数据，规模在南京排前三。我嘴上不说心里想1T算个毛的大数据……&lt;/li&gt;
&lt;/ul&gt;
  &lt;p&gt;后来又继续搞Web了。但学习还是有好处的——&lt;/p&gt;
  &lt;ul&gt;
   &lt;li&gt;大数据知识点杂，有时候还涉及不同语言……这锻炼了自己的知识整合能力；&lt;/li&gt;
   &lt;li&gt;Hadoop、Spark本身普遍是分布式应用，这为后来玩微服务打下很好的基础；&lt;/li&gt;
   &lt;li&gt;很多时候，知识点是相通的，如果能探索到本质，会发现很多所谓高大上的东西其实也就是那么回事。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
 &lt;p&gt;此时，我觉得看视频入门效率太低了，所以调整了下节奏：&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://www.itmuch.com/#2-1-&amp;#20837;&amp;#38376;&amp;#65306;GitHub-Demo" title="2.1 &amp;#20837;&amp;#38376;&amp;#65306;GitHub Demo"&gt;&lt;/a&gt;2.1 入门：GitHub Demo&lt;/h3&gt; &lt;p&gt;看教学视频固然是个很好的方法——因为学习曲线足够低，而且会有导师告诉你怎么用，甚至给你总结好最佳实践。但多数情况下，视频教程对于这个阶段的我，效率已经偏低了。很多视频几十分钟才讲一两个知识点…即使倍速，依然感觉在浪费时间。&lt;/p&gt;
 &lt;p&gt;于是我采取了Demo驱动的方式学习。以学习 Spring Boot 为例，Spring Boot官方提供了   &lt;a href="https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples" rel="noopener" target="_blank"&gt;Spring Boot Samples&lt;/a&gt; ，把代码clone下来玩一遍，就能相对系统得了解Spring Boot提供的能力。&lt;/p&gt;
 &lt;blockquote&gt;
  &lt;p&gt;   &lt;strong&gt;TIPS&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;并非完全放弃教学视频，学习途径的选择是多样的，有时候是二选一，有时是两者配合。&lt;/p&gt;
  &lt;p&gt;只是进入这个阶段后，个人GitHub Demo驱动相对用得相对更多一些。建议大家做个简单的评估，怎么快怎么来。&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h3&gt;  &lt;a href="http://www.itmuch.com/#2-2-&amp;#31995;&amp;#32479;&amp;#23398;&amp;#20064;&amp;#65306;&amp;#23448;&amp;#26041;&amp;#25991;&amp;#26723;" title="2.2 &amp;#31995;&amp;#32479;&amp;#23398;&amp;#20064;&amp;#65306;&amp;#23448;&amp;#26041;&amp;#25991;&amp;#26723;"&gt;&lt;/a&gt;2.2 系统学习：官方文档&lt;/h3&gt; &lt;p&gt;Demo驱动入门后，我一般会对照官方文档撸一遍，例如学习Spring Cloud时，我把官方文档中的用例都过了一遍。&lt;/p&gt;
 &lt;p&gt;官方文档带来的好处如下：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;一手文档——官方文档永远是最好的文档，书、视频等等本质也是别人通过学习官方文档进行二次创作的；&lt;/li&gt;
  &lt;li&gt;能体会官方的意图&lt;/li&gt;
  &lt;li&gt;感知该软件的发展趋势(例如阅读Release Log)&lt;/li&gt;
  &lt;li&gt;系统、详尽。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;很多人可能觉得自己英文水平欠缺，不敢阅读英文文档，这点我只能说硬着头皮上吧，其实坚持一段时间后，你会发现也就那么回事。大部分英文文档还是比较通俗易懂的——再者，现在谷歌翻译质量非常高，进一步降低了阅读英文文档的难度。&lt;/p&gt;
 &lt;p&gt;我的   &lt;a href="https://github.com/itmuch/docker-book" rel="noopener" target="_blank"&gt;Kubernetes开源书&lt;/a&gt; ，本质就是官方文档翻译(一般看一边翻译) + 个人理解 + 批注。&lt;/p&gt;
 &lt;p&gt;总的来说，IT行业人才分布也是符合二八定律的——80%的普通人，20%的高手；20%高手里面，80%的普通高手，20%的大佬。我觉得英文不好不是借口，主要还是看你想成为哪个20%——付出和回报是成一定比例的。&lt;/p&gt;
 &lt;p&gt;另外还有10000小时理论，也可以给大家共勉——要想成为某一领域的精英，必须在这一领域深耕10000小时——如果连英文文档都不敢去碰，怎么可能成为精英？&lt;/p&gt;
 &lt;p&gt;大道理大家都懂，不再废话了。&lt;/p&gt;
 &lt;blockquote&gt;
  &lt;p&gt;   &lt;strong&gt;闲话&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;13年个人定了一个原则，就是不找客观借口。因为客观借口只要想找，永远可以找100个。失败是客观事实，但很多失败都是由于主观因素导致的。失败就是失败，首先要有勇气直面。如果连尝试都不敢，就失败了，那真的是可耻到极致的失败。&lt;/p&gt;
  &lt;p&gt;进入阿里后，我的思想再次发生变化。以前有时候我会觉得由于我没有xx资源，所以我做不了xx事；但现在，我的思想变成因为我要做xx事，所以我需要xx资源，如果没有，那我就去争取；如果没有那我就想办法抢。我离成功还很远，但我会继续努力。技术上成功的难度，对我来说相对低一些，所以我坚持技术路线。&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h3&gt;  &lt;a href="http://www.itmuch.com/#2-3-&amp;#23454;&amp;#25112;&amp;#65306;&amp;#27169;&amp;#25311;&amp;#39033;&amp;#30446;" title="2.3 &amp;#23454;&amp;#25112;&amp;#65306;&amp;#27169;&amp;#25311;&amp;#39033;&amp;#30446;"&gt;&lt;/a&gt;2.3 实战：模拟项目&lt;/h3&gt; &lt;p&gt;和之前一样，还是做个简单的项目玩玩，项目能起到练手的目的即可。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://www.itmuch.com/#2-4-&amp;#28145;&amp;#20837;&amp;#65306;&amp;#24102;&amp;#30528;&amp;#38382;&amp;#39064;&amp;#20998;&amp;#26512;&amp;#28304;&amp;#30721;" title="2.4 &amp;#28145;&amp;#20837;&amp;#65306;&amp;#24102;&amp;#30528;&amp;#38382;&amp;#39064;&amp;#20998;&amp;#26512;&amp;#28304;&amp;#30721;"&gt;&lt;/a&gt;2.4 深入：带着问题分析源码&lt;/h3&gt; &lt;p&gt;起源于在   &lt;code&gt;焦点科技&lt;/code&gt; 的一场面试——焦点科技面试官是对我职业生涯中造成影响的面试官，虽然只有一面之缘，甚至连对方的名字也不知道。&lt;/p&gt;
 &lt;p&gt;与其说是面试，倒不如说是”切磋”——一般面试，往往是你问我答，OK，NEXT。回答对不对，到不到位，往往不会揭晓答案。&lt;/p&gt;
 &lt;p&gt;这位面试官很有意思，他会从一个简单的问题逐步深入，并且如果回答不上来，对方会给你很多提示，就像武侠片里师父给徒弟喂招一样——这样面试下来会有所收获，也会了解自己欠缺的地方；更好玩的是，如果你聊到对方不了解的地方，也会问到他懂为止——这其实是考察候选人的沟通能力的常见手法之一，但目前业界又有多少面试官能做到呢？&lt;/p&gt;
 &lt;p&gt;这次面试给我的启发是：如果广度已经很好，是不是应该深挖呢？&lt;/p&gt;
 &lt;p&gt;深入，最好的方式就是阅读代码。而为了看代码而看代码，在我看来是浪费生命、浪费时间。所以，我选择在遇到问题时，带着问题分析源码——这里的遇到问题，并不是代码运行报错，或者是项目出异常；而是指对xxx感到好奇，想要了解原理，于是带着问题阅读代码。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://www.itmuch.com/#2-5-&amp;#24191;&amp;#24230;&amp;#65306;&amp;#36319;&amp;#36827;&amp;#19994;&amp;#30028;&amp;#21160;&amp;#24577;" title="2.5 &amp;#24191;&amp;#24230;&amp;#65306;&amp;#36319;&amp;#36827;&amp;#19994;&amp;#30028;&amp;#21160;&amp;#24577;"&gt;&lt;/a&gt;2.5 广度：跟进业界动态&lt;/h3&gt; &lt;p&gt;个人比较喜欢看科技新闻，大学开始，常年在煎蛋、CNBeta、36氪等科技站上潜水。然而，从15年开始，就一直在995/996，跳到哪儿哪儿加班……时间不够用，必须做出取舍。&lt;/p&gt;
 &lt;p&gt;经过分析，发现开源动态对自己更有价值。于是坚持每天花10-15分钟刷开源中国的”软件更新咨询”栏目——软件更新栏目相信很多人有所了解，其实就是某某开源软件又发布了新版本的新闻列表。&lt;/p&gt;
 &lt;p&gt;然而，长期关注至少能获得如下几点信息：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;咦？xx软件发布了，这个是啥？解决什么问题的？&lt;/li&gt;
  &lt;li&gt;咦？xx软件又发布了，这玩意儿挺活跃啊！&lt;/li&gt;
  &lt;li&gt;咦？xx评论挺多的，看来很快会流行起来。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;长期关注的收益：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;了解行业动态&lt;/li&gt;
  &lt;li&gt;增进知识广度&lt;/li&gt;
  &lt;li&gt;培养行业敏感性&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;我在15年玩Spring Cloud、16年玩Docker、17年玩Kubernetes，时间基本都在业界流行之前。之所以有这种行业敏感性，和长期刷开源中国是分不开的。&lt;/p&gt;
 &lt;blockquote&gt;
  &lt;p&gt;   &lt;strong&gt;TIPS&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;再安利就变成开源中国软文了……相信我，开源中国没有给我钱……&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h2&gt;  &lt;a href="http://www.itmuch.com/#&amp;#19977;&amp;#12289;16&amp;#24180;&amp;#21518;" title="&amp;#19977;&amp;#12289;16&amp;#24180;&amp;#21518;"&gt;&lt;/a&gt;三、16年后&lt;/h2&gt; &lt;h3&gt;  &lt;a href="http://www.itmuch.com/#3-1-&amp;#20889;&amp;#21338;&amp;#23458;&amp;#65306;&amp;#35753;&amp;#21035;&amp;#20154;&amp;#20063;&amp;#33021;&amp;#25026;" title="3.1 &amp;#20889;&amp;#21338;&amp;#23458;&amp;#65306;&amp;#35753;&amp;#21035;&amp;#20154;&amp;#20063;&amp;#33021;&amp;#25026;"&gt;&lt;/a&gt;3.1 写博客：让别人也能懂&lt;/h3&gt; &lt;p&gt;16年，因为一些契机，从开发转型成为架构师。角色的转变，带来的是思考视角的转变。之前做技术经理时，名下只有四五个人，多数问题口头交流就OK了；但成为架构师后，负责的面变大了，有时得和几十个人沟通，而很多沟通是重复的。&lt;/p&gt;
 &lt;p&gt;此时，我意识到，不如把大家常见问题总结总结，写成手把手的文档——&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;手把手上手系列&lt;/li&gt;
  &lt;li&gt;手把手解决问题系列&lt;/li&gt;
  &lt;li&gt;血的教训系列……&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;再后来，发现写Spring Cloud开源书、博客、实体书……&lt;/p&gt;
 &lt;p&gt;写作本身也是总结的过程，而且不仅要自己懂，还要想办法让别人也能看懂。&lt;/p&gt;
 &lt;p&gt;可能是写手把手系列多了，所以我的文章一直也是手把手、附具体步骤、配详细代码，原理、源码分析写得相对少一些，这点也被一些人诟病。个人对博客的定位，主要是引导新手，其次是个人心得总结。如果人家已经入门了，还需要到处找文章吗？自己研究研究就OK了。&lt;/p&gt;
 &lt;p&gt;那些喜欢看源码解读的”高手”，有多少是真高手，有多少是伪高手？我相信有源码阅读经验的，都不会觉得阅读源码是一件高大上的事情——多数情况下，看懂开源软件源码真的比看懂你所在项目的遗留代码简单多了！&lt;/p&gt;
 &lt;blockquote&gt;
  &lt;p&gt;   &lt;strong&gt;趣闻&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;18年在华为面试 ，和面试官聊到Zuul相关源码。大致问题是：聊聊Zuul ErrorFilter存在的Bug。这个Bug其实在Camden已经修复了，但是我好说歹说面试官都不信。结果再一打听，原来面试官看到《Spring Cloud微服务实战》是这么写的——这本著作是基于Spring Cloud Brixton撰写的，该版本确实有Bug，所以作者非常贴心地给出了解决方案，却被这位面试官拿来做考察一个人对Spring Cloud是否深入的尺子。&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h2&gt;  &lt;a href="http://www.itmuch.com/#&amp;#22235;&amp;#12289;&amp;#20889;&amp;#22312;&amp;#26368;&amp;#21518;" title="&amp;#22235;&amp;#12289;&amp;#20889;&amp;#22312;&amp;#26368;&amp;#21518;"&gt;&lt;/a&gt;四、写在最后&lt;/h2&gt; &lt;p&gt;以上是我历年学习方法的分享。其实总结起来就一句话：我不够聪明，但我会死磕，逐步积累；我不找客观原因，硬上。&lt;/p&gt;
 &lt;p&gt;中间穿插了很多例子，文章可能有点碎……Anyway，希望对大家有用。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>其他 工作 学习</category>
      <guid isPermaLink="true">https://itindex.net/detail/59433-%E5%88%86%E4%BA%AB-%E4%B8%AA%E4%BA%BA-%E5%AD%A6%E4%B9%A0</guid>
      <pubDate>Tue, 09 Apr 2019 22:09:49 CST</pubDate>
    </item>
    <item>
      <title>机器学习的流程是怎样的呢？如何应用到实践中去呢？</title>
      <link>https://itindex.net/detail/59319-%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0-%E5%BA%94%E7%94%A8-%E5%AE%9E%E8%B7%B5</link>
      <description>&lt;blockquote&gt;  &lt;p&gt;机器学习是一种能够实现人工智能的技术，可以通过大量的数据，训练出来一个处理数据的模型。本文笔者将与大家分享：机器学习的相关实践应用。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;  &lt;img alt="" height="450" src="http://image.woshipm.com/wp-files/2019/02/PPP2MKyzxmXjA3AvoUsr.jpg" width="800"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;我所理解的机器学习是一种能够实现人工智能的技术，建立能从经验（数据）中进行学习的模型，从而使这个模型可以达到自行处理此类数据的能力。&lt;/p&gt;
 &lt;p&gt;也可以理解为：通过大量的数据，训练出一个能处理此类数据的模型。使得这个模型可以根据已知的数据，准确率很高的判断出未知的数据，从而使得人类能够采取正确的方法去处理某些事情。&lt;/p&gt;
 &lt;p&gt;想要了解机器学习你需要知道以下几点：&lt;/p&gt;
 &lt;h2&gt;一、机器学习的流程&lt;/h2&gt;
 &lt;p&gt;从实际的应用场景出发，要训练出来一个能够适应某场景的模型需要经过以下几步：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="407" src="http://image.woshipm.com/wp-files/2019/02/cEb9cjaloX8SwfLJx4AV.png" width="500"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;图1&lt;/p&gt;
 &lt;h3&gt;1. 场景解析&lt;/h3&gt;
 &lt;p&gt;场景解析就是将业务逻辑，抽象成为通过算法能够解决的问题。&lt;/p&gt;
 &lt;p&gt;比如：做一个心脏病预测系统，那么就可以抽象为二分类问题——要么有心脏病，要么没有。然后，根据已有的数据看看有没有目标值，可以判断出：是监督学习还是无监督学习，还是半监督学习。从而，选择出能够处理好此类数据的算法。&lt;/p&gt;
 &lt;p&gt;（不同场景采用的算法是不同的）高频的有以下几种类型的场景：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;分类场景：广告投放预测，网站用户点击预测。&lt;/li&gt;
  &lt;li&gt;聚类场景：人群划分，产品种类划分。&lt;/li&gt;
  &lt;li&gt;回归场景&lt;/li&gt;
  &lt;li&gt;文本分析类场景：新闻的标签提取，文本自动分类和文本关键信息抽取。&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;  &lt;strong&gt;关系图算法：&lt;/strong&gt;社交网络关系，网络关系挖掘和金融风险控制。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;模式识别：&lt;/strong&gt;语音识别，图像识别和手写文字识别。&lt;/p&gt;
 &lt;h3&gt;2. 数据预处理&lt;/h3&gt;
 &lt;p&gt;场景解析完，选择适合处理此类数据的算法后，需要对数据进行预处理——就是对数据进行清洗工作，对空值，乱码进行处理。&lt;/p&gt;
 &lt;p&gt;数据预处理的主要目的就是：减少噪音数据对训练数据的影响。&lt;/p&gt;
 &lt;h3&gt;3. 特征工程&lt;/h3&gt;
 &lt;p&gt;特征工程是机器学习中最重要的一部分，因为根据已有的训练数据，可选用的算法是有限的，那么在同样的算法下特征的选取是不同的，100个人对一件事情会有100种看法，也就有100种特征，最后特征的质量决定模型的好坏。特征工程需要做的包括：特征抽象，特征重要性的评估，特征衍生，特征降维。&lt;/p&gt;
 &lt;h3&gt;4. 模拟训练&lt;/h3&gt;
 &lt;p&gt;在经过以上过成后，进入训练模块，生成模型。&lt;/p&gt;
 &lt;h3&gt;5. 模型评估&lt;/h3&gt;
 &lt;p&gt;对生成模型的成熟度进行评估。&lt;/p&gt;
 &lt;h3&gt;6. 离线/在线服务&lt;/h3&gt;
 &lt;p&gt;在实际运用过程中，需要配合调度系统来使用。&lt;/p&gt;
 &lt;p&gt;案例场景：每天将用户当日新增的数据量流入数据库表里，通过调度系统启用离线训练服务，生成最新的离线模型，然后通过在线预测服务进行实时预测。&lt;/p&gt;
 &lt;h3&gt;7. 数据源结构&lt;/h3&gt;
 &lt;p&gt;结构化数据：机构化数据是指以矩阵结构储存的数据。&lt;/p&gt;
 &lt;p&gt;数据库里的数据就是以这种结构存在，可以通过二维结构来显示，如下图：  &lt;img alt="" height="538" src="http://image.woshipm.com/wp-files/2019/02/Dgl561kcZtZ4NmFUisjU.png" width="420"&gt;&lt;/img&gt;  &lt;br /&gt;
图2结构化数据中，有两个重要的概念需要介绍一下：特征列和目标列。&lt;/p&gt;
 &lt;p&gt;上图里age，sex，cp列都是特征列，ifhealth是目标列。&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;非结构化数据：典型的非结构化数据是图像，文本，语音等文件。这些数据不能以矩阵的结构储存，目前的做法也是通过把非结构化的数据转化为二进制储存格式。&lt;/li&gt;
  &lt;li&gt;半结构化数据：半结构化数据是指按照一定的结构储存，但不一定是二维的数据库行存储形态的数据。还有一种是以二维数据形态储存的，但某些字段是文本类型，某些字段是数值类型的。如下图：   &lt;img alt="" height="287" src="http://image.woshipm.com/wp-files/2019/02/wL7WN3IgnKmjafqIO42p.png" width="640"&gt;&lt;/img&gt;&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;图3&lt;/p&gt;
 &lt;h3&gt;8. 算法分类&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;监督学习&lt;/strong&gt;：是指每个进入算法的训练样本数据都有对应的目标值。&lt;/p&gt;
 &lt;p&gt;如上图2所示，Ifhealth为目标值。&lt;/p&gt;
 &lt;p&gt;常见的监督学习算法：  &lt;img alt="" height="201" src="http://image.woshipm.com/wp-files/2019/02/LfthR64Y311oJWrKwiv5.png" width="623"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;图4&lt;/p&gt;
 &lt;p&gt;无监督学习：就是训练样本的数据里没有目标列，不依赖于打标好的机器学习算法。&lt;/p&gt;
 &lt;p&gt;那么，这样的数据可能对一些分类和回归的场景就不太适合了。&lt;/p&gt;
 &lt;p&gt;无监督学习主要是来解决一些聚类场景的问题。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="124" src="http://image.woshipm.com/wp-files/2019/02/CAL5kzp7IE9TryfHtR5F.png" width="596"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;图5&lt;/p&gt;
 &lt;p&gt;半监督学习：&lt;/p&gt;
 &lt;p&gt;通过上面的监督学习和无监督学习的概念，再来看半监督学习就比较好理解了。&lt;/p&gt;
 &lt;p&gt;也就是说，训练数据里只有部分数据是打标的。目前，半监督学习的算法，都是监督学习算法的变形。&lt;/p&gt;
 &lt;p&gt;强化学习：&lt;/p&gt;
 &lt;p&gt;强化学习是一种比较复杂的机器学习种类。强调的是：系统与外界不断的交换，获得外界的反馈，然后决定自身的行为。&lt;/p&gt;
 &lt;p&gt;如：无人驾驶，阿尔法狗下围棋就是强化学习的应用。&lt;/p&gt;
 &lt;h3&gt;9. 过拟合问题（欠拟合这里不做详细的介绍）&lt;/h3&gt;
 &lt;p&gt;过拟合是数据挖掘（通过大量数据，训练模型的过程也称为数据挖掘）领域中最常见的问题，是指：通过训练集训练了一个模型，这个模型对于训练集的预测准确率很高，可以达到95%以上，但是换一份儿数据集进行预测，准确率大幅度下降。&lt;/p&gt;
 &lt;p&gt;出现这种情况的原因可能是：训练的过拟合现象。&lt;/p&gt;
 &lt;p&gt;导致过拟合问题的原因有以下三种：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;训练数据集样本单一。&lt;/li&gt;
  &lt;li&gt;训练样本噪音数据干扰过大。&lt;/li&gt;
  &lt;li&gt;模型过于复杂。&lt;/li&gt;
&lt;/ol&gt;
 &lt;h3&gt;10. 结果评估&lt;/h3&gt;
 &lt;p&gt;机器学习最终的目的是：生成模型。&lt;/p&gt;
 &lt;p&gt;模型生成后需要一些指标来评估这个模型的好坏。&lt;/p&gt;
 &lt;p&gt;常用到的概念有：精确率，召回率，F1值，ROC和AUC几种。&lt;/p&gt;
 &lt;p&gt;首先介绍一下精确率，召回率和F1值。这3个指标是由：TP，TN，FP，FN这4个值计算而来的（这里不做解释了）。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;精确率=TP/（TP+FP）&lt;/li&gt;
  &lt;li&gt;召回率=TP/（TP+FN）&lt;/li&gt;
  &lt;li&gt;F1=（2*精确率*召回率）/（精确率*召回率）&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" height="387" src="http://image.woshipm.com/wp-files/2019/02/l33npARiFpGqTXCI7T35.png" width="320"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;图6&lt;/p&gt;
 &lt;p&gt;ROC曲线是常用的二分类场景的模型评估算法曲线，下图齿状弧形曲线就是ROC曲线。&lt;/p&gt;
 &lt;p&gt;如图所示：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="278" src="http://image.woshipm.com/wp-files/2019/02/xAldEiLcyuaCKBN75AXi.png" width="536"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;图7&lt;/p&gt;
 &lt;p&gt;通过ROC曲线可以清晰的展示出来，只要模型曲线越来越接近左上角就说明模型的效果越好。&lt;/p&gt;
 &lt;p&gt;AUC的值是ROC与横轴所围起来的面积（图中带阴影的部分），这个AUC的值越大说明模型的效果越好。&lt;/p&gt;
 &lt;p&gt;AUC的值取0～1之间，通常大于0.5，当AUC的值大于0.9以上时，证明这个模型的效果比较好。&lt;/p&gt;
 &lt;p&gt; &lt;/p&gt;
 &lt;p&gt;本文由 @梦想在起飞 原创发布于人人都是产品经理，未经许可，禁止转载&lt;/p&gt;
 &lt;p&gt;题图来自 Unsplash，基于CC0协议。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>AI人工智能 3年 中级 机器学习</category>
      <guid isPermaLink="true">https://itindex.net/detail/59319-%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0-%E5%BA%94%E7%94%A8-%E5%AE%9E%E8%B7%B5</guid>
      <pubDate>Fri, 01 Mar 2019 08:13:06 CST</pubDate>
    </item>
    <item>
      <title>KNN算法实战：验证码的识别</title>
      <link>https://itindex.net/detail/59284-knn-%E7%AE%97%E6%B3%95-%E9%AA%8C%E8%AF%81%E7%A0%81</link>
      <description>&lt;p&gt;识别验证码的方式很多，如tesseract、SVM等。前面的几篇文章介绍了  &lt;a href="https://www.biaodianfu.com/knn.html"&gt;KNN算法&lt;/a&gt;，今天主要学习的是如何使用KNN进行验证码的识别。&lt;/p&gt;
 &lt;h2&gt;数据准备&lt;/h2&gt;
 &lt;p&gt;本次实验采用的是CSDN的验证码做演练，相关的接口：https://download.csdn.net/index.php/rest/tools/validcode/source_ip_validate/10.5711163911089325&lt;/p&gt;
 &lt;p&gt;目前接口返回的验证码共2种：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;img alt="" height="20" src="https://www.biaodianfu.com/wp-content/uploads/2019/02/captcha-1.png" width="48"&gt;&lt;/img&gt;  纯数字、干扰小的验证码，简单进行图片去除背景、二值化和阈值处理后，使用kNN算法即可识别。&lt;/li&gt;
  &lt;li&gt;   &lt;img alt="" height="25" src="https://www.biaodianfu.com/wp-content/uploads/2019/02/captcha-2.png" width="70"&gt;&lt;/img&gt; 字母加数字、背景有干扰、图形字符位置有轻微变形，进行图片去除背景、二值化和阈值处理后，使用kNN算法识别&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;这里选择第二种进行破解。由于两种验证码的图片大小不一样，所以可以使用图片大小来判断哪个是第一种验证码，哪个是第二种验证码。&lt;/p&gt;
 &lt;h3&gt;下载验证码&lt;/h3&gt;
 &lt;p&gt;&lt;/p&gt; &lt;pre&gt;import requests
import uuid
from PIL import Image
import os
url = &amp;quot;http://download.csdn.net/index.php/rest/tools/validcode/source_ip_validate/10.5711163911089325&amp;quot;
for i in range(1000):
    resp = requests.get(url)
    filename = &amp;quot;./captchas/&amp;quot; + str(uuid.uuid4()) + &amp;quot;.png&amp;quot;
    with open(filename, &amp;apos;wb&amp;apos;) as f:
        for chunk in resp.iter_content(chunk_size=1024):
            if chunk:  # filter out keep-alive new chunks
                f.write(chunk)
                f.flush()
        f.close()
    im = Image.open(filename)
    if im.size != (70, 25):
        im.close()
        os.remove(filename)
    else:
        print(filename)&lt;/pre&gt; &lt;p&gt;&lt;/p&gt;
 &lt;h3&gt;分割字符&lt;/h3&gt;
 &lt;p&gt;下载过后，就需要对字母进行分割。分割字符还是一件比较麻烦的工作。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;灰度化&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;将彩色的图片转化为灰度图片，便于后面的二值化处理，示例代码：&lt;/p&gt; &lt;pre&gt;from PIL import Image

file = &amp;quot;.\\captchas\\0a4a22cd-f16b-4ae4-bc52-cdf4c081301d.png&amp;quot;
im = Image.open(file)
im_gray = im.convert(&amp;apos;L&amp;apos;)
im_gray.show()&lt;/pre&gt; &lt;p&gt;处理前：  &lt;img alt="" height="25" src="https://www.biaodianfu.com/wp-content/uploads/2019/02/captcha-3.png" width="70"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;处理后：  &lt;img alt="" height="33" src="https://www.biaodianfu.com/wp-content/uploads/2019/02/captcha-4.png" width="89"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;二值化&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;灰度化以后，有颜色的像素点为0-255之间的值。二值化就是将大于某个值的像素点都修改为255，小于该值的修改为0，示例代码：&lt;/p&gt; &lt;pre&gt;from PIL import Image
import numpy as np
file = &amp;quot;.\\captchas\\0a4a22cd-f16b-4ae4-bc52-cdf4c081301d.png&amp;quot;
im = Image.open(file)
im_gray = im.convert(&amp;apos;L&amp;apos;)
# im_gray.show()

pix = np.array(im_gray)
print(pix.shape)
print(pix)

threshold = 100 #阈值

pix = (pix &amp;gt; threshold) * 255
print(pix)

out = Image.fromarray(pix)
out.show()&lt;/pre&gt; &lt;p&gt;二值化输出的结果：  &lt;img alt="" height="31" src="https://www.biaodianfu.com/wp-content/uploads/2019/02/captcha-5.png" width="89"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;去除边框&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;从二值化输出的结果可以看到除了字符，还存在边框，在切割字符前还需要先将边框去除。&lt;/p&gt; &lt;pre&gt;border_width = 1
new_pix = pix[border_width:-border_width,border_width:-border_width]&lt;/pre&gt; &lt;p&gt;  &lt;strong&gt;字符切割&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;由于字符与字符间没有存在连接，可以使用比较简单的“投影法”进行字符的切割。原理就是将二值化后的图片先在垂直方向进行投影，根据投影后的极值来判断分割边界。分割后的小图片再在水平方向进行投影。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="436" src="https://www.biaodianfu.com/wp-content/uploads/2019/02/shadow.png" width="600"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;代码实现：&lt;/p&gt; &lt;pre&gt;def vertical_image(image):
    height, width = image.shape
    h = [0] * width
    for x in range(width):
        for y in range(height):
            s = image[y, x]
            if s == 255:
                h[x] += 1
    new_image = np.zeros(image.shape, np.uint8)
    for x in range(width):
        cv2.line(new_image, (x, 0), (x, h[x]), 255, 1)
    cv2.imshow(&amp;apos;vert_image&amp;apos;, new_image)
    cv2.waitKey()
cv2.destroyAllWindows()&lt;/pre&gt; &lt;p&gt;  &lt;strong&gt;整体代码&lt;/strong&gt;&lt;/p&gt; &lt;pre&gt;from PIL import Image
import cv2
import numpy as np
import os
import uuid


def clean_bg(filename):
    im = Image.open(filename)
    im_gray = im.convert(&amp;apos;L&amp;apos;)
    image = np.array(im_gray)
    threshold = 100  # 阈值
    pix = (image &amp;gt; threshold) * 255
    border_width = 1
    new_image = pix[border_width:-border_width, border_width:-border_width]
    return new_image


def get_col_rect(image):
    height, width = image.shape
    h = [0] * width
    for x in range(width):
        for y in range(height):
            s = image[y, x]
            if s == 0:
                h[x] += 1
    col_rect = []
    in_line = False
    start_line = 0
    blank_distance = 1
    for i in range(len(h)):
        if not in_line and h[i] &amp;gt;= blank_distance:
            in_line = True
            start_line = i
        elif in_line and h[i] &amp;lt; blank_distance:
            rect = (start_line, i)
            col_rect.append(rect)
            in_line = False
            start_line = 0
    return col_rect


def get_row_rect(image):
    height, width = image.shape
    h = [0] * height
    for y in range(height):
        for x in range(width):
            s = image[y, x]
            if s == 0:
                h[y] += 1
    in_line = False
    start_line = 0
    blank_distance = 1
    row_rect = (0, 0)
    for i in range(len(h)):
        if not in_line and h[i] &amp;gt;= blank_distance:
            in_line = True
            start_line = i
        elif in_line and i == len(h)-1:
            row_rect = (start_line, i)
        elif in_line and h[i] &amp;lt; blank_distance:
            row_rect = (start_line, i)
            break
    return row_rect


def get_block_image(image, col_rect):
    col_image = image[0:image.shape[0], col_rect[0]:col_rect[1]]
    row_rect = get_row_rect(col_image)
    if row_rect[1] != 0:
        block_image = image[row_rect[0]:row_rect[1], col_rect[0]:col_rect[1]]
    else:
        block_image = None
    return block_image


def clean_bg(filename):
    im = Image.open(filename)
    im_gray = im.convert(&amp;apos;L&amp;apos;)
    image = np.array(im_gray)
    threshold = 100  # 阈值
    pix = (image &amp;gt; threshold) * 255
    border_width = 2
    new_image = pix[border_width:-border_width, border_width:-border_width]
    return new_image

def split(filename):
    image = clean_bg(filename)
    col_rect = get_col_rect(image)
    for cols in col_rect:
        block_image = get_block_image(image, cols)
        if block_image is not None:
            new_image_filename = &amp;apos;letters/&amp;apos; + str(uuid.uuid4()) + &amp;apos;.png&amp;apos;
            cv2.imwrite(new_image_filename, block_image)


if __name__ == &amp;apos;__main__&amp;apos;:
    for filename in os.listdir(&amp;apos;captchas&amp;apos;):
        current_file = &amp;apos;captchas/&amp;apos; + filename
        split(current_file)
        print(&amp;apos;split file:%s&amp;apos; % current_file)&lt;/pre&gt; &lt;p&gt;&lt;/p&gt;
 &lt;h2&gt;数据集准备&lt;/h2&gt;
 &lt;p&gt;在完成图像切割后，需要做将切分的字母建立由标签的样本。即将切分后的字符梳理到正确的分类中。比较常见的方式是人工梳理。&lt;/p&gt;
 &lt;p&gt;由于图像比较多，这里使用使用Tesseract-OCR进行识别。&lt;/p&gt;
 &lt;p&gt;官方项目地址：  &lt;a href="https://github.com/tesseract-ocr/tesseract"&gt;https://github.com/tesseract-ocr/tesseract&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;Windows安装包地址：  &lt;a href="https://github.com/UB-Mannheim/tesseract/wiki"&gt;https://github.com/UB-Mannheim/tesseract/wiki&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;Tesseract-OCR的安装&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;下载完安装包后，直接运行安装即可，比较重要的是环境变量的设置。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;将安装目录（D:\Program Files (x86)\Tesseract-OCR）添加进PATH&lt;/li&gt;
  &lt;li&gt;新建TESSDATA_PREFIX系统变量，值为tessdata 文件夹的路径（D:\Program Files (x86)\Tesseract-OCR\tessdata）&lt;/li&gt;
  &lt;li&gt;安装Python包pytesseract（pip install pytesseract）&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;Tesseract-OCR的使用&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;使用起来非常的简单，代码如下：&lt;/p&gt; &lt;pre&gt;from PIL import Image
import pytesseract
import os


def copy_to_dir(filename):
    image = Image.open(filename)
    code = pytesseract.image_to_string(image, config=&amp;quot;-c tessedit&amp;quot;
                                                     &amp;quot;_char_whitelist=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789&amp;quot;
                                                     &amp;quot; --psm 10&amp;quot;
                                                     &amp;quot; -l osd&amp;quot;
                                                     &amp;quot; &amp;quot;)
    if not os.path.exists(&amp;quot;dataset/&amp;quot; + code):
        os.mkdir(&amp;quot;dataset/&amp;quot; + code)
    image.save(&amp;quot;dataset/&amp;quot; + code + filename.replace(&amp;quot;letters&amp;quot;, &amp;quot;&amp;quot;))
    image.close()


if __name__ == &amp;quot;__main__&amp;quot;:
    for filename in os.listdir(&amp;apos;letters&amp;apos;):
        current_file = &amp;apos;letters/&amp;apos; + filename
        copy_to_dir(current_file)
        print(current_file)&lt;/pre&gt; &lt;p&gt;由于Tesseract-OCR识别的准确率非常的低，完全不能使用，放弃~，还是需要手工整理。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;图片尺寸统一&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;在完成人工处理后，发现切割后的图片大小不一。在字符识别前需要对图片进行的尺寸进行统一。&lt;/p&gt;
 &lt;p&gt;具体实现方法：&lt;/p&gt; &lt;pre&gt;import cv2

def image_resize(filename):
    img = cv2.imread(filename, cv2.IMREAD_GRAYSCALE) #读取图片时采用单通道
    print(img)
    if img.shape[0] != 10 or img.shape[1] != 6:
        img = cv2.resize(img, (6, 10), interpolation=cv2.INTER_CUBIC)
        print(img)
        cv2.imwrite(filename, img)&lt;/pre&gt; &lt;p&gt;使用cv2.resize时，参数输入是 宽×高×通道，这里使用的时单通道的，interpolation的选项有：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;INTER_NEAREST 最近邻插值&lt;/li&gt;
  &lt;li&gt;INTER_LINEAR 双线性插值（默认设置）&lt;/li&gt;
  &lt;li&gt;INTER_AREA 使用像素区域关系进行重采样。 它可能是图像抽取的首选方法，因为它会产生无云纹理的结果。 但是当图像缩放时，它类似于INTER_NEAREST方法。&lt;/li&gt;
  &lt;li&gt;INTER_CUBIC 4×4像素邻域的双三次插值&lt;/li&gt;
  &lt;li&gt;INTER_LANCZOS4 8×8像素邻域的Lanczos插值&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;另外为了让数据更加便于利用，可以将图片再进行二值化的归一。具体代码如下：&lt;/p&gt; &lt;pre&gt;import cv2
import numpy as np

def image_normalize(filename):
    img = cv2.imread(filename, cv2.IMREAD_GRAYSCALE) #读取图片时采用单通道
    if img.shape[0] != 10 or img.shape[1] != 6:
        img = cv2.resize(img, (6, 10), interpolation=cv2.INTER_CUBIC)
    normalized_img = np.zeros((6, 10))  # 归一化
    normalized_img = cv2.normalize(img, normalized_img, 0, 1, cv2.NORM_MINMAX)
    cv2.imwrite(filename, normalized_img)&lt;/pre&gt; &lt;p&gt;归一化的类型，可以有以下的取值：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;NORM_MINMAX:数组的数值被平移或缩放到一个指定的范围，线性归一化，一般较常用。&lt;/li&gt;
  &lt;li&gt;NORM_INF:此类型的定义没有查到，根据OpenCV 1的对应项，可能是归一化数组的C-范数(绝对值的最大值)&lt;/li&gt;
  &lt;li&gt;NORM_L1 :  归一化数组的L1-范数(绝对值的和)&lt;/li&gt;
  &lt;li&gt;NORM_L2: 归一化数组的(欧几里德)L2-范数&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;字符识别&lt;/h2&gt;
 &lt;p&gt;字符图片 宽6个像素，高10个像素 ，理论上可以最简单粗暴地可以定义出60个特征：60个像素点上面的像素值。但是显然这样高维度必然会造成过大的计算量，可以适当的降维。比如：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;每行上黑色像素的个数，可以得到10个特征&lt;/li&gt;
  &lt;li&gt;每列上黑色像素的个数，可以得到6个特征&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;&lt;/p&gt; &lt;pre&gt;from sklearn.neighbors import KNeighborsClassifier
import os
from sklearn import preprocessing
import cv2
import numpy as np
import warnings
warnings.filterwarnings(module=&amp;apos;sklearn*&amp;apos;, action=&amp;apos;ignore&amp;apos;, category=DeprecationWarning)


def get_feature(file_name):
    img = cv2.imread(file_name, cv2.IMREAD_GRAYSCALE)  # 读取图片时采用单通道
    height, width = img.shape

    pixel_cnt_list = []
    for y in range(height):
        pix_cnt_x = 0
        for x in range(width):
            if img[y, x] == 0:  # 黑色点
                pix_cnt_x += 1

        pixel_cnt_list.append(pix_cnt_x)

    for x in range(width):
        pix_cnt_y = 0
        for y in range(height):
            if img[y, x] == 0:  # 黑色点
                pix_cnt_y += 1

        pixel_cnt_list.append(pix_cnt_y)

    return pixel_cnt_list


if __name__ == &amp;quot;__main__&amp;quot;:
    test = get_feature(&amp;quot;dataset/K/04a0844c-12f2-4344-9b78-ac1d28d746c0.png&amp;quot;)
    category = []
    features = []
    for dir_name in os.listdir(&amp;apos;dataset&amp;apos;):
        for filename in os.listdir(&amp;apos;dataset/&amp;apos; + dir_name):
            category.append(dir_name)
            current_file = &amp;apos;dataset/&amp;apos; + dir_name + &amp;apos;/&amp;apos; + filename
            feature = get_feature(current_file)
            features.append(feature)
            # print(current_file)
    le = preprocessing.LabelEncoder()
    label = le.fit_transform(category)

    model = KNeighborsClassifier(n_neighbors=1)
    model.fit(features, label)
    predicted= model.predict(np.array(test).reshape(1, -1))
    print(predicted)
    print(le.inverse_transform(predicted))&lt;/pre&gt; &lt;p&gt;这里直接使用了sklearn中的KNN方法，如需了解更多见：  &lt;a href="https://www.biaodianfu.com/scikit-learn-knn.html"&gt;使用 Scikit-learn 进行 KNN 分类&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;The post   &lt;a href="https://www.biaodianfu.com/knn-captcha-recognition.html" rel="nofollow"&gt;KNN算法实战：验证码的识别&lt;/a&gt; appeared first on   &lt;a href="https://www.biaodianfu.com" rel="nofollow"&gt;标点符&lt;/a&gt;.&lt;/p&gt;
 &lt;div&gt;
  &lt;p&gt;Related posts:&lt;/p&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/using-mahalanobis-distance-to-find-outliers.html" rel="bookmark" title="&amp;#20351;&amp;#29992;&amp;#39532;&amp;#27663;&amp;#36317;&amp;#31163;&amp;#21457;&amp;#29616;&amp;#24322;&amp;#24120;&amp;#28857;"&gt;使用马氏距离发现异常点 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/scikit-learn-knn.html" rel="bookmark" title="&amp;#20351;&amp;#29992; Scikit-learn &amp;#36827;&amp;#34892; KNN &amp;#20998;&amp;#31867;"&gt;使用 Scikit-learn 进行 KNN 分类 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/python-check-statistics-code.html" rel="bookmark" title="&amp;#20351;&amp;#29992;Python&amp;#26469;&amp;#26816;&amp;#26597;&amp;#32479;&amp;#35745;&amp;#20195;&amp;#30721;&amp;#26159;&amp;#21542;&amp;#24067;&amp;#32622;&amp;#21040;&amp;#20301;"&gt;使用Python来检查统计代码是否布置到位 &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;  &lt;p&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>数据 机器学习 算法</category>
      <guid isPermaLink="true">https://itindex.net/detail/59284-knn-%E7%AE%97%E6%B3%95-%E9%AA%8C%E8%AF%81%E7%A0%81</guid>
      <pubDate>Fri, 15 Feb 2019 12:25:48 CST</pubDate>
    </item>
    <item>
      <title>文本分类中的一些经验和 tricks</title>
      <link>https://itindex.net/detail/59268-%E6%96%87%E6%9C%AC-%E5%88%86%E7%B1%BB-%E7%BB%8F%E9%AA%8C</link>
      <description>&lt;p&gt;最近在总结之前做的文本分类实验的一些经验和 tricks，同时也参考了网上的一些相关资料(见文末)，其中有些 tricks 没尝试过，先在这里记下，或者日后能用上。&lt;/p&gt;
 &lt;a&gt;&lt;/a&gt;
 &lt;p&gt;这里的经验和 tricks 大概可分为两部分：预处理部分和模型部分，下面分别介绍&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#&amp;#39044;&amp;#22788;&amp;#29702;" title="&amp;#39044;&amp;#22788;&amp;#29702;"&gt;&lt;/a&gt;预处理&lt;/h2&gt; &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;文本更正，一些基本的操作包括：繁体转简体，全角转半角，拼音纠错等&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;文本泛化&lt;/strong&gt;，如一个手机号码，因为有几千万的手机号码，不可能为每个手机号码设一个特征，所以最好将手机号码转化为同一个特征；另外表情符号、人名、地址、网址、命名实体等也要考虑这种泛化，泛化的程度这个视具体的任务，比如说地址可以以国家为粒度，也可以以省份为粒度&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;规范文本为统一长度时，取所有长度的均值或者中位数，但是别取最大值；截断时根据具体任务考虑从前面阶段或从后面截断&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;构建数据集的 vocabulary 时，需要考虑以下几个方面&lt;/p&gt;
   &lt;ul&gt;
    &lt;li&gt;取前N个高频词或者过滤掉出现次数小于某个阈值的词&lt;/li&gt;
    &lt;li&gt;根据具体任务确定是否需要去掉 stop words&lt;/li&gt;
    &lt;li&gt;假如采用了预训练的词向量，要尽可能让 vocabulary 中的词语能找到对应的词向量(这个问题也涉及到预训练的词向量和分词器的选择)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;词向量的选择，当数据集较小时，直接使用预训练好的词向量（如google、facebook开源的），且不用微调；当训练集比较大的时候，可随机初始化进行训练，也可以对预训练的词向量进行微调（微调收敛得更快，但是结果差异不大）&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;分词时考虑以下几个方面&lt;/p&gt;
   &lt;ul&gt;
    &lt;li&gt;是否需要分词，使用 char-level 的方法时不需要分词，但是在很多场景下 word-level 的效果都要比 char-level 的要好&lt;/li&gt;
    &lt;li&gt;分词时可以只保留长度大于1的词(会去除很多停止词)，对结果精度没什么影响，但是可以有效降低特征维度&lt;/li&gt;
    &lt;li&gt;采用预训练的词向量时，要保证分词后的大部分词语能够出现在预训练的词向量表中，否则这个词语的 embedding 就相当于是随机初始化，预训练的词向量没有提供任何信息；具体方法可参考     &lt;a href="https://www.zhihu.com/question/265357659/answer/578944550" rel="external" target="_blank"&gt;这里&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;ul&gt;
  &lt;li&gt;文本数据增强的方法有   &lt;ul&gt;
    &lt;li&gt;drop: 随机删掉文本&lt;/li&gt;
    &lt;li&gt;shuffle: 随机改变文本顺序&lt;/li&gt;
    &lt;li&gt;replace: 用近义词进行替换&lt;/li&gt;
    &lt;li&gt;….&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#&amp;#27169;&amp;#22411;" title="&amp;#27169;&amp;#22411;"&gt;&lt;/a&gt;模型&lt;/h2&gt; &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;规则有时能解决大部分的问题，不一定要用到模型，使用时要权衡模型带来的收益和复杂性&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;传统的机器学习方法根据其特征工程的不同可分为三大类&lt;/p&gt;
   &lt;ul&gt;
    &lt;li&gt;词袋模型：将出现的词记为1，否则记为 0，问题是维度高且稀疏性严重&lt;/li&gt;
    &lt;li&gt;向量空间模型：根据文档频率、互信息、信息增益、χ²统计量等进行了特征(词语)的选择，同时通过 tfidf 值为每个词赋权重；一定程度上缓解了上面提到的词袋模型维度高且稀疏性严重的问题&lt;/li&gt;
    &lt;li&gt;主题模型：pLSA/LDA/HDP 等主题模型将文本表示低维实数向量，类似于深度学习中的 embedding，但是比 embedding 有更好的解释性&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;fasttext 简单、速度快，是一个非常不错的 baseline；随着问题复杂性增加可依次尝试 CNN -&amp;gt; RNN -&amp;gt; BERT&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;对于深度学习模型，把模型变得更深更宽更复杂往往能够提升效果；但是当模型复杂到一定程度的时候，提升的效果微乎其微甚至没提升&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;rnn 类模型用双向一般会比单向要好&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;使用 dropout(一般设为 0.5) 基本都能提升效果，使用的地方包括：embedding 层后、FC层后&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;训练震荡问题：增加随机采样因素尽可能使得数据分布 iid，默认 shuffle 机制能使得训练结果更稳定。如果训练模型仍然很震荡，可以考虑调整学习率 或     &lt;code&gt;mini_batch_size&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;学习率一般设置衰减，如在N个 epoch 之后将学习率乘上 0.1，从而不断降低学习率&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;超参数的设置经验可参考     &lt;a href="https://arxiv.org/abs/1510.03820" rel="external" target="_blank"&gt;A Sensitivity Analysis of (and Practitioners’ Guide to) Convolutional Neural Networks for Sentence Classification&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;模型融合时，差异性越大，融合效果越好，具体可参    &lt;a href="https://zhuanlan.zhihu.com/p/28923961" rel="external" target="_blank"&gt;这里&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;p&gt;参考&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://www.zhihu.com/question/265357659" rel="external" target="_blank"&gt;在文本分类任务中，有哪些论文中很少提及却对性能有重要影响的tricks？&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://zhuanlan.zhihu.com/p/28923961" rel="external" target="_blank"&gt;知乎看山杯夺冠记&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://zhuanlan.zhihu.com/p/25928551" rel="external" target="_blank"&gt;用深度学习（CNN RNN Attention）解决大规模文本分类问题 - 综述和实践&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://zhuanlan.zhihu.com/p/24720954" rel="external" target="_blank"&gt;深度学习网络调参技巧&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>NLP 深度学习 NLP</category>
      <guid isPermaLink="true">https://itindex.net/detail/59268-%E6%96%87%E6%9C%AC-%E5%88%86%E7%B1%BB-%E7%BB%8F%E9%AA%8C</guid>
      <pubDate>Sat, 26 Jan 2019 17:01:56 CST</pubDate>
    </item>
    <item>
      <title>机器学习算法Boosting</title>
      <link>https://itindex.net/detail/59206-%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0-%E7%AE%97%E6%B3%95-boosting</link>
      <description>&lt;p&gt;机器学习通常会被分为2大类：监督学习和非监督学习。在监督学习中，训练数据由输入和期望的输出组成，然后对非训练数据进行预测输出，也就是找出输入x与输出y之间的函数关系F：y = F(x)。根据输出的精确特性又可以分为分类和回归。分类和回归的区别在于输出变量的类型。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;定量输出称为回归，或者说是连续变量预测。&lt;/li&gt;
  &lt;li&gt;定性输出称为分类，或者说是离散变量预测。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;举个例子：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;预测明天的气温是多少度，这是一个回归任务。&lt;/li&gt;
  &lt;li&gt;预测明天是阴、晴还是雨，就是一个分类任务。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" height="143" src="https://www.biaodianfu.com/wp-content/uploads/2019/01/diff.png" width="720"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;集成学习&lt;/h2&gt;
 &lt;p&gt;集成学习是通过训练弱干个弱学习器，并通过一定的结合策略，从而形成一个强学习器。有时也被称为多分类器系统（multi-classifier system）、基于委员会的学习（committee-based learning）等。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="160" src="https://www.biaodianfu.com/wp-content/uploads/2019/01/ensembling.png" width="395"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;集成学习先产生一组“个体学习器”（individual learner），再用某种策略将它们结合起来。通常来说，很多现有的学习算法都足以从训练数据中产生一个个体学习器。一般来说，我们会将这种由个体学习器集成的算法分为两类&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;同质（homogeneous）的，即集成中仅包含同种类型的一个体学习器，像“决策树集成”中就仅包含决策树，“神经网络集成”中就全是神经网络。同质集成中的个体学习器又称为基学习器（base learner），相应的学习算法也被称为基学习算法（base learning algorithm）。&lt;/li&gt;
  &lt;li&gt;异质（heterogenous）的，相对同质，异质集成中的个体学习其就是由不同的学习算法生成的，这是，个体学习器就被称为组件学习器（component learner）&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;其中用的比较多的是同质学习器。同质学习器按照个体学习器之间是否存在依赖关系可以分为两类：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;第一个是个体学习器之间存在强依赖关系，一系列个体学习器基本都需要串行生成，代表算法是boosting系列算法；&lt;/li&gt;
  &lt;li&gt;第二个是个体学习器之间不存在强依赖关系，一系列个体学习器可以并行生成，代表算法是bagging和随机森林（Random Forest）系列算法。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" height="488" src="https://www.biaodianfu.com/wp-content/uploads/2019/01/bagging-boosting.png" width="750"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="279" src="https://www.biaodianfu.com/wp-content/uploads/2019/01/single-bagging-boosting.png" width="750"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;现在我们来简单评估一下集成学习方法的性能：考虑一个简单的例子，在二分类任务中，假定三个分类器在三个测试样本上的表现入下图所示，其中√代表正确，×代表分类错误，集成学习的结果则是由投票法（voting）决出，即“少数服从多数”：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="138" src="https://www.biaodianfu.com/wp-content/uploads/2019/01/vote.png" width="595"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;在a图中每个分类器只有66.6%的精度的时候，集成学习达到了100%的精度；在b图中，三个分类器相同导致集成性能没有提高；c图中由于每个分类器的精度只有33.3%导致集成学习的效果变得更糟。由此我们可以看出来，集成学习中对个体学习器的要求应该是“好而不同”，即既满足准确性，又满足多样性（diversity），也即是说，学习器既不能太坏，而且学习器与学习器之间也要有差异。&lt;/p&gt;
 &lt;p&gt;随着集成中个体分类器数目T的增大，集成的错误率将指数级下降从而最终趋于0（这里还有一个前置条件就是个体分类器的错误率不能大于50%）。但我们曾假设各个分类器之间的错误率是相互独立的，而实际上再同一个任务中个体学习器视为解决同一个问题训练出来的，这也就意味着它们之间显然不可能相互独立。换句话说，个体学习器的“准确性”和“多样性”本身也是存在冲突的。一般的，准确性很高之后，若要增加多样性就需要准确性做出一定的牺牲。因此，如何产生“好而不同”的个体学习器，便是集成学习研究的核心。&lt;/p&gt;
 &lt;p&gt;目前，有三种常见的集成学习框架：bagging，boosting和stacking。国内，南京大学的周志华教授对集成学习有很深入的研究，其在09年发表的一篇概述性论文  &lt;a href="http://cs.nju.edu.cn/zhouzh/zhouzh.files/publication/springerEBR09.pdf"&gt;《&lt;/a&gt;  &lt;a href="http://cs.nju.edu.cn/zhouzh/zhouzh.files/publication/springerEBR09.pdf"&gt;Ensemble Learning》&lt;/a&gt;对这三种集成学习框架有了明确的定义，概括如下：&lt;/p&gt;
 &lt;p&gt;bagging：从训练集从进行子抽样组成每个基模型所需要的子训练集，对所有基模型预测的结果进行综合产生最终的预测结果：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="452" src="https://www.biaodianfu.com/wp-content/uploads/2019/01/bagging.jpg" width="1252"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;boosting：训练过程为阶梯状，基模型按次序一一进行训练（实现上可以做到并行），基模型的训练集按照某种策略每次都进行一定的转化。对所有基模型预测的结果进行线性综合产生最终的预测结果：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="1025" src="https://www.biaodianfu.com/wp-content/uploads/2019/01/boosting.jpg" width="1575"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;stacking：将训练好的所有基模型对训练基进行预测，第j个基模型对第i个训练样本的预测值将作为新的训练集中第i个样本的第j个特征值，最后基于新的训练集进行训练。同理，预测的过程也要先经过所有基模型的预测形成新的测试集，最后再对测试集进行预测：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="1044" src="https://www.biaodianfu.com/wp-content/uploads/2019/01/stacking.jpg" width="1325"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;有了这些基本概念之后，直觉将告诉我们，由于不再是单一的模型进行预测，所以模型有了“集思广益”的能力，也就不容易产生过拟合现象。但是，直觉是不可靠的，接下来我们将从模型的偏差和方差入手，彻底搞清楚这一问题。&lt;/p&gt;
 &lt;h2&gt;什么是Boosting？&lt;/h2&gt;
 &lt;p&gt;Boosting 是个非常强大的学习方法, 它也是一个监督的分类学习方法。它组合许多“弱”分类器来产生一个强大的分类器组。一个弱分类器的性能只是比随机选择好一点，因此它可以被设计的非常简单并且不会有太大的计算花费。将很多弱分类器结合起来组成一个集成的类似于SVM或者神经网络的强分类器。&lt;/p&gt;
 &lt;p&gt;现在我们知道boosting是组合多个弱学习器形成一个强学习器，那么一个自然而然的问题就是“boosting如何确定弱的规则？”为了发现弱的规则，我们可以应用不同分配下的基础的（机器）学习算法，每个算法都会生成一个弱规则，这是一个迭代的过程，多次迭代后，Boosting算法可以将它们组合成一个强大的决策规则。为了选择正确的分配方式，可以遵循下面几个步骤：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;步骤1：所有分布下的基础学习器对于每个观测值都应该有相同的权重&lt;/li&gt;
  &lt;li&gt;步骤2：如果第一个基础的学习算法预测错误，则该点在下一次的基础学习算法中有更高的权重&lt;/li&gt;
  &lt;li&gt;步骤3：迭代第2步，直到到达预定的学习器数量或预定的预测精度。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;最后，将输出的多个弱学习器组合成一个强的学习器，提高模型的整体预测精度。Boosting总是更加关注被错误分类的弱规则。&lt;/p&gt;
 &lt;p&gt;Boosting算法的底层可以是任何算法，关于boosting算法，我们需要知道其中最有名的3个算法：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;AdaBoost(Adaptive Boosting)&lt;/li&gt;
  &lt;li&gt;GBM(Gradient Boosting Machine)&lt;/li&gt;
  &lt;li&gt;XGBoost&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;AdaBoost&lt;/h2&gt;
 &lt;p&gt;假设我们有一个分类问题，给定一个训练样本集，从这个分类问题中求出一个粗糙的分类规则（弱分类器）往往要比求一个精确的分类规则（强分类器）容易得多。提升方法便是从弱学习算法出发，反复学习得到一系列弱分类器（又称为基本分类器），然后通过组合这些弱分类器来构成一个强分类器。大多数的提升方法都是改变训练数据的概率分布（或说训练数据的权值分布），来针对不同的训练分布调用弱学习算法学习一系列弱分类器。这样一来，对于提升方法而言，就有了两个问题需要去解决：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;在每一轮如何改变训练数据的权值或概率分布？&lt;/li&gt;
  &lt;li&gt;如何将弱分类器组合成一个强分类器？&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;AdaBoost针对第一个问题的做法是提高那些被前一轮弱分类器错误分类样本的权值，并降低那些被正确分类的样本的权值。经过一轮的权值加大后，后一轮的弱分类器就会更关注那些没有被正确分类的样本。持续下去，分类问题便会被一系列弱分类器“分而治之”。而对于第二个问题，即弱分类器的组合，AdaBoost采取加权多数表决法，具体的所，就是加大误差率小的弱分类器的权值，使其在表决中起更大的作用，另一方面，减小分类误差率大的弱分类器的权值，使其在表决中起较小的作用。&lt;/p&gt;
 &lt;p&gt;下面这张图对Ada-boost做了恰当的解释：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Box 1: 你可以看到我们假设所有的数据点有相同的权重（正号、负号的大小都一样），并用一个决策树桩D1将它们分为两部分。我们可以看到这个决策树桩将其中的三个正号标记的数据点分类错误，因此我们将这三个点赋予更大的权重交由下一个预测树桩进行分类。&lt;/li&gt;
  &lt;li&gt;Box 2: 在这里你可以看到三个未被正确分类的（+）号的点的权重变大。在这种情况下，第二个决策树桩D2试图将这三个错误的点准确的分类，但是这又引起新的分类错误，将三个（-）号标记的点识别错误，因此在下一次分类中，这三个（-）号标记的点被赋予更大的权重。&lt;/li&gt;
  &lt;li&gt;Box 3: 在这里三个被错误分类的（-）号标记的点被赋予更大的权重，利用决策树桩D3进行新的分类，这时候又产生了新的分类错误，图中用小圆圈圈起来的一个负号点和两个正号点&lt;/li&gt;
  &lt;li&gt;Box 4: 在这里，我们将D1、D2和D3三个决策器组合起来形成一个复杂的规则，你可以看到这个组合后的决策器比它们任何一个弱分类器表现的都足够好。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" height="372" src="https://www.biaodianfu.com/wp-content/uploads/2019/01/adaboost.png" width="493"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;该原理可同样用于回归算法。它在不同权重的训练数据集上生成一系列的弱学习器，最开始的时候所有的数据拥有相同的权重，对于第一个分类器没有正确分类的点则在下一个决策器中的权重将会加大，作为一个迭代的过程，直到分类器数量达到预定值或预测精度达到预定值。大多数情况下，我们在AdaBoost中使用decision stamps。但是如果它可以接受带有权重的训练集，我们也可以使用其他任何的机器学习算法作为基础学习器。我们可以使用AdaBoost算法解决分类和回归问题。&lt;/p&gt;
 &lt;p&gt;Python 代码：&lt;/p&gt; &lt;pre&gt;from sklearn.ensemble import AdaBoostClassifier # For Classification
from sklearn.ensemble import AdaBoostRegressor  # For Regression
from skleran.tree import DecisionTreeClassifier

dt = DecisionTreeClassifier()
clf = AdaBoostClassifier(n_estimators=100, base_estimator=dt, learning_rate=1)
# Above I have used decision tree as a base estimator, you can use any ML learner as base estimator if it accepts sample weight
clf.fit(x_train, y_train)&lt;/pre&gt; &lt;p&gt;可以调整参数以优化算法的性能：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;n_estimators：它控制了弱学习器的数量&lt;/li&gt;
  &lt;li&gt;learning_rate：控制在最后的组合中每个弱分类器的权重，需要在learning_rate和n_estimators间有个权衡&lt;/li&gt;
  &lt;li&gt;base_estimators：它用来指定不同的ML算法。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;也可以调整基础学习器的参数以优化它自身的性能。&lt;/p&gt;
 &lt;h2&gt;Gradient Boosting&lt;/h2&gt;
 &lt;p&gt;Gradient Boosting是一种Boosting的方法，它主要的思想是，每一次建立模型是在之前建立模型损失函数的梯度下降方向。这句话有一点拗口，损失函数(loss function)描述的是模型的不靠谱程度，损失函数越大，则说明模型越容易出错（其实这里有一个方差、偏差均衡的问题，但是这里就假设损失函数越大，模型越容易出错）。如果我们的模型能够让损失函数持续的下降，则说明我们的模型在不停的改进，而最好的方式就是让损失函数在其梯度（Gradient)的方向上下降。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="422" src="https://www.biaodianfu.com/wp-content/uploads/2019/01/Gradient-Boosting.png" width="450"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;偏差和方差&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;广义的偏差（bias）描述的是预测值和真实值之间的差异，方差（variance）描述距的是预测值作为随机变量的离散程度。  &lt;a href="http://scott.fortmann-roe.com/docs/BiasVariance.html"&gt;《Understanding the Bias-Variance Tradeoff》&lt;/a&gt;当中有一副图形象地向我们展示了偏差和方差的关系：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="383" src="https://www.biaodianfu.com/wp-content/uploads/2019/01/bias-variance.png" width="405"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;模型的偏差是一个相对来说简单的概念：训练出来的模型在训练集上的准确度。要解释模型的方差，首先需要重新审视模型：模型是随机变量。设样本容量为n的训练集为随机变量的集合(X1, X2, …, Xn)，那么模型是以这些随机变量为输入的随机变量函数（其本身仍然是随机变量）：F(X1, X2, …, Xn)。抽样的随机性带来了模型的随机性。&lt;/p&gt;
 &lt;p&gt;定义随机变量的值的差异是计算方差的前提条件，通常来说，我们遇到的都是数值型的随机变量，数值之间的差异再明显不过（减法运算）。但是，模型的差异性呢？我们可以理解模型的差异性为模型的结构差异，例如：线性模型中权值向量的差异，树模型中树的结构差异等。在研究模型方差的问题上，我们并不需要对方差进行定量计算，只需要知道其概念即可。研究模型的方差有什么现实的意义呢？我们认为方差越大的模型越容易过拟合：假设有两个训练集A和B，经过A训练的模型Fa与经过B训练的模型Fb差异很大，这意味着Fa在类A的样本集合上有更好的性能，而Fb反之，这便是我们所说的过拟合现象。我们常说集成学习框架中的基模型是弱模型，通常来说弱模型是偏差高（在训练集上准确度低）方差小（防止过拟合能力强）的模型。&lt;/p&gt;
 &lt;p&gt;基于boosting框架的整体模型可以用线性组成式来描述，其中  &lt;img alt="h_i(x)" height="23" src="https://www.biaodianfu.com/wp-content/ql-cache/quicklatex.com-7c19b22d98d914cfd6b9293b26956565_l3.png" title="Rendered by QuickLaTeX.com" width="49"&gt;&lt;/img&gt;为基模型与其权值的乘积：&lt;/p&gt;
 &lt;p&gt;        &lt;img alt="\[F(x) = \sum_{i}^{m}h_i(x)\]" height="65" src="https://www.biaodianfu.com/wp-content/ql-cache/quicklatex.com-7b5a4c67d51af24b9b1e4fbcaec634cb_l3.png" title="Rendered by QuickLaTeX.com" width="167"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;根据上式，整体模型的训练目标是使预测值F(x)逼近真实值y，也就是说要让每一个基模型的预测值逼近各自要预测的部分真实值。由于要同时考虑所有基模型，导致了整体模型的训练变成了一个非常复杂的问题。所以，研究者们想到了一个贪心的解决手段：每次只训练一个基模型。那么，现在改写整体模型为迭代式：&lt;/p&gt;
 &lt;p&gt;        &lt;img alt="\[F^i(x) = F^{i-1}(x)+h_i(x)\]" height="28" src="https://www.biaodianfu.com/wp-content/ql-cache/quicklatex.com-b338930b14c64039361344b1fa19b9eb_l3.png" title="Rendered by QuickLaTeX.com" width="245"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;这样一来，每一轮迭代中，只要集中解决一个基模型的训练问题：使  &lt;img alt="F^i(x)" height="26" src="https://www.biaodianfu.com/wp-content/ql-cache/quicklatex.com-3a319e3e780dae3447ec566534914a57_l3.png" title="Rendered by QuickLaTeX.com" width="54"&gt;&lt;/img&gt;逼近真实值y。&lt;/p&gt;
 &lt;p&gt;使  &lt;img alt="F^i(x)" height="26" src="https://www.biaodianfu.com/wp-content/ql-cache/quicklatex.com-3a319e3e780dae3447ec566534914a57_l3.png" title="Rendered by QuickLaTeX.com" width="54"&gt;&lt;/img&gt;逼近真实值，其实就是使  &lt;img alt="h_i(x)" height="23" src="https://www.biaodianfu.com/wp-content/ql-cache/quicklatex.com-7c19b22d98d914cfd6b9293b26956565_l3.png" title="Rendered by QuickLaTeX.com" width="49"&gt;&lt;/img&gt;逼近真实值和上一轮迭代的预测值  &lt;img alt="F^{i-1}(x)" height="26" src="https://www.biaodianfu.com/wp-content/ql-cache/quicklatex.com-9239c5c1ce440555458a4336eba74286_l3.png" title="Rendered by QuickLaTeX.com" width="77"&gt;&lt;/img&gt;之差，即残差  &lt;img alt="y- F^{i-1}(x)" height="26" src="https://www.biaodianfu.com/wp-content/ql-cache/quicklatex.com-fbfb751bd01b9126acf160ccb3ac5b5f_l3.png" title="Rendered by QuickLaTeX.com" width="118"&gt;&lt;/img&gt;。最直接的做法是构建基模型来拟合残差。研究者发现，残差其实是最小均方损失函数的关于预测值的反向梯度：&lt;/p&gt;
 &lt;p&gt;        &lt;img alt="\[-\frac{\delta (\frac{1}{2}*(y-F^{i-1}(x))^2)}{\delta F(x)}=y-F^{i-1}(x)\]" height="60" src="https://www.biaodianfu.com/wp-content/ql-cache/quicklatex.com-f3f80fdf62c86352f148917b1d43ffae_l3.png" title="Rendered by QuickLaTeX.com" width="384"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;也就是说，若  &lt;img alt="F^{i-1}(x)" height="26" src="https://www.biaodianfu.com/wp-content/ql-cache/quicklatex.com-9239c5c1ce440555458a4336eba74286_l3.png" title="Rendered by QuickLaTeX.com" width="77"&gt;&lt;/img&gt;加上拟合了反向梯度的  &lt;img alt="h_i(x)" height="23" src="https://www.biaodianfu.com/wp-content/ql-cache/quicklatex.com-7c19b22d98d914cfd6b9293b26956565_l3.png" title="Rendered by QuickLaTeX.com" width="49"&gt;&lt;/img&gt;得到  &lt;img alt="F^{i}(x)" height="26" src="https://www.biaodianfu.com/wp-content/ql-cache/quicklatex.com-394eeeec6310b066e27480a97901cbe2_l3.png" title="Rendered by QuickLaTeX.com" width="54"&gt;&lt;/img&gt;，该值可能将导致平方差损失函数降低，预测的准确度提高。&lt;/p&gt;
 &lt;p&gt;引入任意损失函数后，我们可以定义整体模型的迭代式如下：&lt;/p&gt;
 &lt;p&gt;Gradient Boosting是非常经典而又重要的提升方法，他与AdaBoost一样都是讲弱分类器合成强分类，但是其大致区别有:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Gradient Boosting通过残差来变量的改变错误分类的权重,而AdaBoost就真的直接去修改分类错误的训练权重了&lt;/li&gt;
  &lt;li&gt;Gradient Boosting接入的分类器一般完整的决策树居多，但是AdaBoost一般使用二层决策树&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;Gradient Boosting中最有代表性的就是GBDT,该模型虽好，使用时理解数据以及正确调参才是王道&lt;/p&gt;
 &lt;p&gt;在Python Sklearn库中，我们可以使用Gradient Tree Boosting或GBDT（Gradient Boosting Descision Tree）。它是一个关于任意可微损失函数的一个泛化，可以用来解决分类和回归问题。&lt;/p&gt; &lt;pre&gt;from sklearn.ensemble import GradientBoostingClassifier  # For Classification
from sklearn.ensemble import GradientBoostingRegressor   # For Regression

clf = GradientBoostingClassfier(n_estimators=100, learning_rate=1.0, max_depth=1)
clf.fit(X_train, y_train)&lt;/pre&gt; &lt;p&gt;可以调整参数以优化算法的性能：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;n_estimators :控制弱学习器的数量&lt;/li&gt;
  &lt;li&gt;learning_rate:控制最后组合中弱学习器的权重，，需要在learning_rate和n_estimators间有个权衡&lt;/li&gt;
  &lt;li&gt;max_depth：单个回归估计的最大深度。最大深度限制了树的结点数量。调整该参数的最佳性能：最好的值取决于输入的变量&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;你可以调整损失函数以获得更好地性能。&lt;/p&gt;
 &lt;h2&gt;XGBoost&lt;/h2&gt;
 &lt;p&gt;XGBoost 是 “Extreme Gradient Boosting”的简称，是GBDT的一种高效实现，XGBoost中的基学习器除了可以是CART（gbtree）也可以是线性分类器（gblinear）。&lt;/p&gt;
 &lt;p&gt;Gradient Boosting Decision Tree从名称上来讲包含三个部分：Decision Tree、Boosting、Gradient Boosting。决策树我们都比较熟悉，在此略过不谈。Boosting这种方法，是指用一组弱分类器，得到一个性能比较好的分类器；这里用到的思路是给每个弱分类器的结果进行加权。Gradient Boosting是指使用gradient信息对分类器进行加权，之后的部分会详细介绍gradient加权的思路。综上，GBDT是一种使用gradient作为信息，将不同的弱分类decision trees进行加权，从而获得较好性能的方法。&lt;/p&gt;
 &lt;p&gt;GBDT的一般步骤&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Step 1: 初始化。初始化y_hat在第0时刻的值。&lt;/li&gt;
  &lt;li&gt;Step 2：求残差。通过类似梯度下降法的思路，每次y都向梯度下降的方向挪一小步。只是在GBDT，y挪的一小步并不是一个variable，而是一个function。&lt;/li&gt;
  &lt;li&gt;Step 3：构建决策树。使用决策树逼近这个残差 –g，得到第t个决策树：f_t。&lt;/li&gt;
  &lt;li&gt;Step 4：求叶节点权重。&lt;/li&gt;
  &lt;li&gt;Step 5：更新输出y。y(t) = y(t – 1) + learning_rate * f_t&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;在GBDT思想下，XGBoost对其中的步骤进行了具体实现。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;变化1：提高了精度 – 对Loss的近似从一阶到二阶。。传统GBDT只使用了一阶导数对loss进行近似，而XGBoost对Loss进行泰勒展开，取一阶导数和二阶导数。同时，XGBoost的Loss考虑了正则化项，包含了对复杂模型的惩罚，比如叶节点的个数、树的深度等等。通过对Loss的推导，得到了构建树时不同树的score。具体score计算方法见论文Sec 2.2。&lt;/li&gt;
  &lt;li&gt;变化2：提高了效率 – 近似算法加快树的构建。XGBoost支持几种构建树的方法。
   &lt;ul&gt;
    &lt;li&gt;第一：使用贪心算法，分层添加decision tree的叶节点。对每个叶节点，对每个feature的所有instance值进行排序，得到所有可能的split。选择score最大的split，作为当前节点。&lt;/li&gt;
    &lt;li&gt;第二：使用quantile对每个feature的所有instance值进行分bin，将数据离散化。&lt;/li&gt;
    &lt;li&gt;第三：使用histogram对每个feature的所有instance值进行分bin，将数据离散化。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;变化3：提高了效率 – 并行化与cache access。XGBoost在系统上设计了一些方便并行计算的数据存储方法，同时也对cache access进行了优化。这些设计使XGBoost的运算表现在传统GBDT系统上得到了很大提升。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" height="147" src="https://www.biaodianfu.com/wp-content/uploads/2019/01/xgboost.png" width="724"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Xgboost和GBDT的区别&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;传统GBDT以CART作为基分类器，xgboost还支持线性分类器，这个时候xgboost相当于带L1和L2正则化项的逻辑斯蒂回归（分类问题）或者线性回归（回归问题）。&lt;/li&gt;
  &lt;li&gt;传统GBDT在优化时只用到一阶导数信息，xgboost则对代价函数进行了二阶泰勒展开，同时用到了一阶和二阶导数。顺便提一下，xgboost工具支持自定义代价函数，只要函数可一阶和二阶求导。&lt;/li&gt;
  &lt;li&gt;Xgboost在代价函数里加入了正则项，用于控制模型的复杂度。正则项里包含了树的叶子节点个数、每个叶子节点上输出的score的L2模的平方和。从Bias-variance tradeoff角度来讲，正则项降低了模型的variance，使学习出来的模型更加简单，防止过拟合，这也是xgboost优于传统GBDT的一个特性。&lt;/li&gt;
  &lt;li&gt;Shrinkage（缩减），相当于学习速率（xgboost中的eta）。xgboost在进行完一次迭代后，会将叶子节点的权重乘上该系数，主要是为了削弱每棵树的影响，让后面有更大的学习空间。实际应用中，一般把eta设置得小一点，然后迭代次数设置得大一点。（补充：传统GBDT的实现也有学习速率）&lt;/li&gt;
  &lt;li&gt;列抽样（column subsampling）。xgboost借鉴了随机森林的做法，支持列抽样，不仅能降低过拟合，还能减少计算，这也是xgboost异于传统gbdt的一个特性。&lt;/li&gt;
  &lt;li&gt;缺失值的处理。对于特征的值有缺失的样本，xgboost可以自动学习出它的分裂方向。&lt;/li&gt;
  &lt;li&gt;xgboost工具支持并行。boosting不是一种串行的结构吗?怎么并行的？注意xgboost的并行不是tree粒度的并行，xgboost也是一次迭代完才能进行下一次迭代的（第t次迭代的代价函数里包含了前面t-1次迭代的预测值）。xgboost的并行是在特征粒度上的。我们知道，决策树的学习最耗时的一个步骤就是对特征的值进行排序（因为要确定最佳分割点），xgboost在训练之前，预先对数据进行了排序，然后保存为block结构，后面的迭代中重复地使用这个结构，大大减小计算量。这个block结构也使得并行成为了可能，在进行节点的分裂时，需要计算每个特征的增益，最终选增益最大的那个特征去做分裂，那么各个特征的增益计算就可以开多线程进行。&lt;/li&gt;
  &lt;li&gt;可并行的近似直方图算法。树节点在进行分裂时，我们需要计算每个特征的每个分割点对应的增益，即用贪心法枚举所有可能的分割点。当数据无法一次载入内存或者在分布式情况下，贪心算法效率就会变得很低，所以xgboost还提出了一种可并行的近似直方图算法，用于高效地生成候选的分割点。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;XGBoost优势：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;显式地将树模型的复杂度作为正则项加在优化目标&lt;/li&gt;
  &lt;li&gt;公式推导里用到了二阶导数信息，而普通的GBDT只用到一阶&lt;/li&gt;
  &lt;li&gt;允许使用列抽样(column(feature)sampling)来防止过拟合，借鉴了Random Forest的思想，sklearn里的gbm好像也有类似实现。&lt;/li&gt;
  &lt;li&gt;实现了一种分裂节点寻找的近似算法，用于加速和减小内存消耗。&lt;/li&gt;
  &lt;li&gt;节点分裂算法能自动利用特征的稀疏性。&lt;/li&gt;
  &lt;li&gt;样本数据事先排好序并以block的形式存储，利于并行计算&lt;/li&gt;
  &lt;li&gt;penalty function Omega主要是对树的叶子数和叶子分数做惩罚，这点确保了树的简单性。&lt;/li&gt;
  &lt;li&gt;支持分布式计算可以运行在MPI，YARN上，得益于底层支持容错的分布式通信框架rabit。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;参考链接：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="http://xgboost.apachecn.org/#/"&gt;http://xgboost.apachecn.org/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://xgboost.readthedocs.io/en/latest/index.html"&gt;https://xgboost.readthedocs.io/en/latest/index.html&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.52caml.com/head_first_ml/ml-chapter6-boosting-family/"&gt;http://www.52caml.com/head_first_ml/ml-chapter6-boosting-family/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://www.zybuluo.com/Team/note/1095836"&gt;https://www.zybuluo.com/Team/note/1095836&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://www.zybuluo.com/hanxiaoyang/note/985880"&gt;https://www.zybuluo.com/hanxiaoyang/note/985880&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://yxzf.github.io/2017/03/xgboost-v1/"&gt;https://yxzf.github.io/2017/03/xgboost-v1/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://www.ibm.com/developerworks/cn/analytics/library/machine-learning-hands-on6-adaboost/index.html"&gt;https://www.ibm.com/developerworks/cn/analytics/library/machine-learning-hands-on6-adaboost/index.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;The post   &lt;a href="https://www.biaodianfu.com/boosting.html" rel="nofollow"&gt;机器学习算法Boosting&lt;/a&gt; appeared first on   &lt;a href="https://www.biaodianfu.com" rel="nofollow"&gt;标点符&lt;/a&gt;.&lt;/p&gt;
 &lt;div&gt;
  &lt;p&gt;Related posts:&lt;/p&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/cosine-similarity.html" rel="bookmark" title="&amp;#30456;&amp;#20284;&amp;#24230;&amp;#35745;&amp;#31639;&amp;#20043;&amp;#20313;&amp;#24358;&amp;#30456;&amp;#20284;&amp;#24230;"&gt;相似度计算之余弦相似度 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/scikit-learn-knn.html" rel="bookmark" title="&amp;#20351;&amp;#29992; Scikit-learn &amp;#36827;&amp;#34892; KNN &amp;#20998;&amp;#31867;"&gt;使用 Scikit-learn 进行 KNN 分类 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/clustering-on-a-one-dimensional-array.html" rel="bookmark" title="&amp;#19968;&amp;#32500;&amp;#25968;&amp;#32452;&amp;#30340;&amp;#32858;&amp;#31867;"&gt;一维数组的聚类 &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;  &lt;p&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>数据 机器学习 算法</category>
      <guid isPermaLink="true">https://itindex.net/detail/59206-%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0-%E7%AE%97%E6%B3%95-boosting</guid>
      <pubDate>Fri, 11 Jan 2019 17:59:56 CST</pubDate>
    </item>
    <item>
      <title>使用 Scikit-learn 的进行 KNN 分类</title>
      <link>https://itindex.net/detail/59097-scikit-learn-knn</link>
      <description>&lt;p&gt;最近邻(KNN)是一种非常简单、易于理解、通用性强的机器学习算法，广泛应用于金融、医疗、政治、手写检测、图像识别、视频识别等领域。在信用评级中，金融机构会预测客户的信用评级。在贷款支付中，银行机构将预测贷款是否安全或有风险。在政治学中，将潜在选民分为两类，要么投票，要么不投票。上一篇《  &lt;a href="https://www.biaodianfu.com/knn.html"&gt;K-近邻算法KNN学习笔记&lt;/a&gt;》主要讲解的是KNN的理论内容，今天主要学习怎么用KNN进行实战。&lt;/p&gt;
 &lt;h2&gt;使用Scikit-Learn进行分类预测&lt;/h2&gt;
 &lt;p&gt;由于Scikit-Learn包中已经封装好了相应的方法，所以使用起来非常的简单。具体代码如下：&lt;/p&gt; &lt;pre&gt;定义数据：
# 定义特征变量
weather = [&amp;apos;Sunny&amp;apos;,&amp;apos;Sunny&amp;apos;,&amp;apos;Overcast&amp;apos;,&amp;apos;Rainy&amp;apos;,&amp;apos;Rainy&amp;apos;,&amp;apos;Rainy&amp;apos;,&amp;apos;Overcast&amp;apos;,&amp;apos;Sunny&amp;apos;,&amp;apos;Sunny&amp;apos;,
&amp;apos;Rainy&amp;apos;,&amp;apos;Sunny&amp;apos;,&amp;apos;Overcast&amp;apos;,&amp;apos;Overcast&amp;apos;,&amp;apos;Rainy&amp;apos;]
temperature = [&amp;apos;Hot&amp;apos;,&amp;apos;Hot&amp;apos;,&amp;apos;Hot&amp;apos;,&amp;apos;Mild&amp;apos;,&amp;apos;Cool&amp;apos;,&amp;apos;Cool&amp;apos;,&amp;apos;Cool&amp;apos;,&amp;apos;Mild&amp;apos;,&amp;apos;Cool&amp;apos;,&amp;apos;Mild&amp;apos;,&amp;apos;Mild&amp;apos;,&amp;apos;Mild&amp;apos;,&amp;apos;Hot&amp;apos;,&amp;apos;Mild&amp;apos;]

# 定义分类标签
play=[&amp;apos;No&amp;apos;,&amp;apos;No&amp;apos;,&amp;apos;Yes&amp;apos;,&amp;apos;Yes&amp;apos;,&amp;apos;Yes&amp;apos;,&amp;apos;No&amp;apos;,&amp;apos;Yes&amp;apos;,&amp;apos;No&amp;apos;,&amp;apos;Yes&amp;apos;,&amp;apos;Yes&amp;apos;,&amp;apos;Yes&amp;apos;,&amp;apos;Yes&amp;apos;,&amp;apos;Yes&amp;apos;,&amp;apos;No&amp;apos;]


# 将特征变量和分类标签数值化
from sklearn import preprocessing

le = preprocessing.LabelEncoder()
weather_encoded=le.fit_transform(weather)
temperature_encoded=le.fit_transform(temperature)

# 整合特征变量
features=list(zip(weather_encoded,temperature_encoded))

label=le.fit_transform(play)

生成模型并预测
from sklearn.neighbors import KNeighborsClassifier

model = KNeighborsClassifier(n_neighbors=3)
model.fit(features,label)
predicted= model.predict([[0,2]]) # 0:Overcast, 2:Mild
print(predicted)&lt;/pre&gt; &lt;p&gt;&lt;/p&gt;
 &lt;h2&gt;使用Grid Search确定K值&lt;/h2&gt;
 &lt;p&gt;使用KNN遇到的最大问题和使用  &lt;a href="https://www.biaodianfu.com/k-means-choose-k.html"&gt;K-Means&lt;/a&gt;类似，就是怎么确定K值，常见的方法主要是Grid Search。简单的说就是遍历K值，然后再计算评分，去评分最好的值作为最终选择。&lt;/p&gt;
 &lt;p&gt;这里我们使用Scikit-Learn自带的葡萄酒数据集，该数据是对意大利同一地区种植的三种不同品种葡萄酒进行化学分析的结果。这项分析确定了三种葡萄酒中每种葡萄酒中13种成分的含量。该数据库包括13个特征和一个目标(品种类型)。品种类型包括:’0类’、’1类’和’2类’。&lt;/p&gt; &lt;pre&gt;from sklearn import datasets
wine = datasets.load_wine()

print(wine.feature_names) #获取特性向量名称
print(wine.target_names) #获取分类标签名称

print(wine.data[0:5]) #查看特征向量数据
print(wine.target) #查看分类标签数据
print(wine.data.shape)
print(wine.target.shape)


from sklearn.model_selection import train_test_split

# 将数据划分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(wine.data, wine.target, test_size=0.3) # 70% training and 30% test

import numpy as np
from sklearn.neighbors import KNeighborsClassifier

neighbors = np.arange(1,10)
train_accuracy =np.empty(len(neighbors))
test_accuracy = np.empty(len(neighbors))

for i,k in enumerate(neighbors):
    knn = KNeighborsClassifier(n_neighbors=k)
    knn.fit(X_train, y_train)
    train_accuracy[i] = knn.score(X_train, y_train) #使用训练集测试准确率
    test_accuracy[i] = knn.score(X_test, y_test) #使用测试集测试准确率

import matplotlib.pyplot as plt
%matplotlib inline

plt.title(&amp;apos;k-NN Varying number of neighbors&amp;apos;)
plt.plot(neighbors, test_accuracy, label=&amp;apos;Testing Accuracy&amp;apos;)
plt.plot(neighbors, train_accuracy, label=&amp;apos;Training accuracy&amp;apos;)
plt.legend()
plt.xlabel(&amp;apos;Number of neighbors&amp;apos;)
plt.ylabel(&amp;apos;Accuracy&amp;apos;)
plt.show()&lt;/pre&gt; &lt;p&gt;  &lt;img alt="" height="305" src="https://www.biaodianfu.com/wp-content/uploads/2018/12/k.png" width="426"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;所以在本例中，K选择1是最合适的。（多次执行此代码，发现最后生成的图片不一致，原因是随机划分数据导致的）&lt;/p&gt;
 &lt;p&gt;参考链接：  &lt;a href="https://www.datacamp.com/community/tutorials/k-nearest-neighbor-classification-scikit-learn"&gt;https://www.datacamp.com/community/tutorials/k-nearest-neighbor-classification-scikit-learn&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;The post   &lt;a href="https://www.biaodianfu.com/scikit-learn-knn.html" rel="nofollow"&gt;使用 Scikit-learn 的进行 KNN 分类&lt;/a&gt; appeared first on   &lt;a href="https://www.biaodianfu.com" rel="nofollow"&gt;标点符&lt;/a&gt;.&lt;/p&gt;
 &lt;div&gt;
  &lt;p&gt;Related posts:&lt;/p&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/python-normalization-method.html" rel="bookmark" title="&amp;#20351;&amp;#29992;Python&amp;#23545;&amp;#25968;&amp;#25454;&amp;#36827;&amp;#34892;&amp;#24402;&amp;#19968;&amp;#21270;&amp;#35268;&amp;#26684;&amp;#21270;"&gt;使用Python对数据进行归一化规格化 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/pearson-correlation-coefficient.html" rel="bookmark" title="&amp;#30456;&amp;#20284;&amp;#24230;&amp;#35745;&amp;#31639;&amp;#20043;&amp;#30382;&amp;#23572;&amp;#36874;&amp;#30456;&amp;#20851;&amp;#31995;&amp;#25968;"&gt;相似度计算之皮尔逊相关系数 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/clustering-on-a-one-dimensional-array.html" rel="bookmark" title="&amp;#19968;&amp;#32500;&amp;#25968;&amp;#32452;&amp;#30340;&amp;#32858;&amp;#31867;"&gt;一维数组的聚类 &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;  &lt;p&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>数据 Python 机器学习</category>
      <guid isPermaLink="true">https://itindex.net/detail/59097-scikit-learn-knn</guid>
      <pubDate>Wed, 19 Dec 2018 09:59:30 CST</pubDate>
    </item>
    <item>
      <title>表面繁荣之下，人工智能的发展已陷入困境</title>
      <link>https://itindex.net/detail/59010-%E8%A1%A8%E9%9D%A2-%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD-%E5%8F%91%E5%B1%95</link>
      <description>&lt;p&gt;《连线》杂志在其最近发布的12月刊上，以封面故事的形式报道了人工智能的发展状况。现在，深度学习面临着无法进行推理的困境，这也就意味着，它无法让机器具备像人一样的智能。但是真正的推理在机器中是什么样子的呢？如果深度学习不能帮助我们达到目的，那什么可以呢？文章作者为克莱夫·汤普森(@pomeranian99)，原标题为“How to Teach Artificial Intelligence Some Common Sense”。&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://www.199it.com/wp-content/uploads/2018/11/1543343069-5278-so8dmxkpyuzywc5d.jpg"&gt;   &lt;img alt="" height="756" src="http://www.199it.com/wp-content/uploads/2018/11/1543343069-5278-so8dmxkpyuzywc5d.jpg" width="578"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;h3&gt;一、&lt;/h3&gt;
 &lt;p&gt;五年前，总部位于伦敦的人工智能公司DeepMind的程序员，兴奋地看着人工智能自学玩一款经典的街机游戏。他们在一项看似“异想天开”的任务上使用了当今最热门的技术——深度学习——掌握了Breakout。&lt;/p&gt;
 &lt;p&gt;这是一款雅达利（Atari）开发的游戏，在游戏中，你需要用移动下方的平板，把球弹起，然后把上方的所有砖块都打消失。&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://www.199it.com/wp-content/uploads/2018/11/1543343069-5216-murblzbcpoooikz11200.jpg"&gt;   &lt;img alt="" height="255" src="http://www.199it.com/wp-content/uploads/2018/11/1543343069-5216-murblzbcpoooikz11200.jpg" width="390"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;深度学习，是机器进行自我教育的一种方式；你给人工智能提供大量的数据，它会自己识别模式。在这个游戏中，数据就是屏幕上的活动——代表砖块、球和玩家平板的块状像素。&lt;/p&gt;
 &lt;p&gt;DeepMind的人工智能，一个由分层算法组成的神经网络，并不知道任何关于Breakout的工作原理、规则、目标，甚至如何发挥它都不清楚。编码器只是让神经网络检查每个动作的结果，每次球的弹起轨迹。这会导致什么？&lt;/p&gt;
 &lt;p&gt;事实证明，它会掌握一些令人印象深刻的技能。在最初的几场游戏中，人工智能只是控制下方的平板四处乱晃。但是玩了几百次之后，它已经开始准确地将球弹起了。到了第600场比赛时，神经网络使用了一种专业的人类Breakout游戏玩家使用的动作，凿穿整排砖块，让球沿着墙顶不停跳跃。&lt;/p&gt;
 &lt;p&gt;“这对我们来说，是一个很大的惊喜，”DeepMind的首席执行官德米斯·哈萨比斯(Demis Hassabis)当时说道。“这一策略完全来自底层系统。”&lt;/p&gt;
 &lt;p&gt;人工智能，已经显示出它能够像人类一样进行异常微妙的思考，掌握Breakout背后的内在概念。因为神经网络松散地反映了人脑的结构，所以从理论上说，它们应该在某些方面模仿我们自己的认知方式。这一刻似乎证明了这个理论是正确的。&lt;/p&gt;
 &lt;p&gt;去年，位于旧金山的一家人工智能公司Vicorance的计算机科学家，提供了一个有趣的现实检验。他们采用了一种类似DeepMind所用的人工智能，并在Breakout上进行了训练。&lt;/p&gt;
 &lt;p&gt;结果很棒。但随后，他们稍微调整了游戏的布局。在一次迭代中，他们将平板提得更高了；另一次迭代中，他们在上方增加了一个牢不可破的区域。&lt;/p&gt;
 &lt;p&gt;人类玩家可以快速适应这些变化，但神经网络却不能。 这个看起来很聪明的人工智能，只能打出它花了数百场比赛掌握的Breakout的方法。 它不能应对新变化。&lt;/p&gt;
 &lt;p&gt;“我们人类不仅仅是模式识别器，”Vicarious的共同创始人之一、计算机科学家迪利普·乔治（Dileep George）告诉我。“我们也在为我们看到的东西建立模型。这些是因果模型——有我们对因果关系的理解。”&lt;/p&gt;
 &lt;p&gt;人类能够推理，也会对我们周围的世界进行逻辑推理，我们有大量的常识知识来帮助我们发现新的情况。当我们看到一款与我们刚刚玩的游戏略有不同的Breakout游戏时，我们会意识到，它可能有着大致相同的规则和目标。&lt;/p&gt;
 &lt;p&gt;但另一方面，神经网络对Breakout一无所知。它所能做的就是遵循这个模式。当模式改变时，它无能为力。&lt;/p&gt;
 &lt;p&gt;深度学习是人工智能的主宰。在它成为主流以来的六年里，它已经成为帮助机器感知和识别周围世界的主要方式。&lt;/p&gt;
 &lt;p&gt;它为Alexa的语音识别、Waymo的自动驾驶汽车和谷歌的即时翻译提供了动力。从某些方面来说，Uber的网络也是一个巨大的优化问题，它利用机器学习来找出乘客需要汽车的地方。中国科技巨头百度，有2000多名工程师在神经网络人工智能上努力工作。&lt;/p&gt;
 &lt;p&gt;多年来，深度学习看上去越来越好，不可阻挡地让机器拥有像人一样流畅、灵活的智力。&lt;/p&gt;
 &lt;p&gt;但是一些人认为，深度学习正在面临困境。他们说，单凭这一点，它永远不会产生广义上的智能，因为真正像人类一样的智能，不仅仅是模式识别。&lt;/p&gt;
 &lt;p&gt;我们需要开始弄清楚如何让人工智能具备常识。他们警告说，如果我们不这样做，我们将会不断地触及深度学习的极限，就像视觉识别系统，只要改变一些输入，就会很容易被愚弄，比如，让深度学习模型认为乌龟就是一杆枪。&lt;/p&gt;
 &lt;p&gt;但他们说，如果我们成功了，我们将见证更安全、更有用的设备爆炸式增长——比如在杂乱的家中自由行动的医疗机器人、不会误报的欺诈检测系统等等。&lt;/p&gt;
 &lt;p&gt;但是，真正的推理在机器中是什么样子的呢？如果深度学习不能帮助我们达到目的，那什么可以呢？&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://www.199it.com/wp-content/uploads/2018/11/1543343069-9275-ve74z8fwkyhh8z4m1200.jpg"&gt;   &lt;img alt="" height="800" src="http://www.199it.com/wp-content/uploads/2018/11/1543343069-9275-ve74z8fwkyhh8z4m1200.jpg" width="1200"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;h3&gt;二、&lt;/h3&gt;
 &lt;p&gt;加里·马库斯（Gary Marcus）是纽约大学的心理学和神经科学教授，现年48岁，戴着眼镜，忧心忡忡。他可能是最著名的深度学习反对者。&lt;/p&gt;
 &lt;p&gt;马库斯第一次对人工智能感兴趣，是在20世纪80年代和90年代，当时神经网络还处于实验阶段，从那以后，他就一直在做同样的论证。&lt;/p&gt;
 &lt;p&gt;“我不只是来晚了，而且还想在派对上撒尿，”当我在纽约大学附近的公寓遇见他时，马库斯告诉我。(我们也是私人朋友。)“深度学习刚开始爆发的时候，我就说‘方向错了，伙计们！’”&lt;/p&gt;
 &lt;p&gt;那时，深度学习背后的策略和现在是一样的。比方说，你想要一台机器来自己学习识别雏菊。首先，你需要编写一些算法“神经元”，像三明治一样，将它们层层连接起来(当你使用多层时，三明治会变得更厚或更深——因此是“深度”学习)。&lt;/p&gt;
 &lt;p&gt;你在第一层输入一个雏菊的图像，它的神经元会根据图像是否像它以前看到的雏菊的例子而进行判断。然后，信号将移动到下一层，在那里循环这个过程。最终，这些层会得出一个结论。&lt;/p&gt;
 &lt;p&gt;起初，神经网络只是盲目猜测；它或多或少地让生活从一张白纸开始。关键是建立一个有用的反馈回路。每当人工智能没有识别出雏菊时，那组神经连接就会削弱导致错误猜测的链接；如果它成功了，它会加强。&lt;/p&gt;
 &lt;p&gt;给定足够的时间和足够多的雏菊样本，神经网络会变得更加精确。它学会了通过直觉来识别一些雏菊的模式，让它每次都能识别出雏菊(而不是向日葵或菊花)。&lt;/p&gt;
 &lt;p&gt;随着时间的推移，这一核心理念——从一个简单的网络开始，通过重复训练——得到了改进，似乎可以应用到几乎任何地方。&lt;/p&gt;
 &lt;p&gt;但是马库斯从未被说服。对他来说，问题就在于一张白纸：它假设人类纯粹通过观察周围的世界来建立他们的智力，机器也可以。&lt;/p&gt;
 &lt;p&gt;但是马库斯不认为人类就是这样工作的。他认可诺姆·乔姆斯基( Noam Chomsky )的智力发展理论，他认为人类天生就有学习的天赋，能够掌握语言和解释物质世界，而不是一张白纸。&lt;/p&gt;
 &lt;p&gt;他指出，尽管有很多人认为神经网络是智能的，但它似乎不像人类大脑那样工作。首先，它们太需要数据了。&lt;/p&gt;
 &lt;p&gt;在大多数情况下，每个神经网络都需要数千或数百万个样本来学习。更糟糕的是，每次你想让神经网络识别一种新的项目，你都必须从头开始训练。一个识别金丝雀的神经网络在识别鸟鸣或人类语言方面没有任丝毫用处。&lt;/p&gt;
 &lt;p&gt;“我们不需要大量的数据来学习，”马库斯说。他的孩子不需要看一百万辆车就能认出车辆来。更好的是，他们可以“抽象化”，当他们第一次看到拖拉机时，他们会知道它有点像汽车。他们也可以进行反事实的工作。&lt;/p&gt;
 &lt;p&gt;谷歌翻译可以将法语翻译成英语。但是它不知道这些话是什么意思。马库斯指出，人类不仅掌握语法模式，还掌握语法背后的逻辑。你可以给一个小孩一个假动词，比如pilk，她很可能会推断过去式是 pilked。当然，她以前没见过这个词。她没有接受过这方面的“训练”。她只是凭直觉知道了语言运作的一些逻辑，并能将其应用到一个新的情况中。&lt;/p&gt;
 &lt;p&gt;“这些深度学习系统不知道如何整合抽象知识，”马库斯说，他创立了一家公司，创造了用更少的数据进行学习的人工智能(并在2016年将公司卖给了Uber)。&lt;/p&gt;
 &lt;p&gt;今年早些时候，马库斯发表了一份关于arXiv的白皮书，认为如果没有一些新的方法，深度学习可能永远不会突破目前的局限。它需要的是一种推动力——补充或内置的规则，以帮助它对世界进行推理。&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://www.199it.com/wp-content/uploads/2018/11/1543343069-6940-5kn7hrcxsu1djbyo1200.jpg"&gt;   &lt;img alt="" height="800" src="http://www.199it.com/wp-content/uploads/2018/11/1543343069-6940-5kn7hrcxsu1djbyo1200.jpg" width="1200"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;h3&gt;三、&lt;/h3&gt;
 &lt;p&gt;奥伦·埃齐奥尼（Oren Etzioni）经常面带微笑。他是一位计算机科学家，在西雅图经营着艾伦人工智能研究所(Allen Institute for Artificial Intelligence)。&lt;/p&gt;
 &lt;p&gt;在他明亮的办公室里向我打招呼，领我走过一块白板，上面潦草地写着对机器智能的思考。(“定义成功”，“任务是什么？”)在外面，年轻的人工智能研究员戴着耳机，敲击着键盘。&lt;/p&gt;
 &lt;p&gt;埃茨奥尼和他的团队正在研究常识问题。他将此定义为两个传奇的人工智能时刻——1997年 IBM 的深蓝(Deep Blue)击败象棋大师加里·卡斯帕罗夫(Garry Kasparov) ，以及去年DeepMind的AlphaGo击败世界顶尖围棋选手李世石。(谷歌在2014年收购了DeepMind。)&lt;/p&gt;
 &lt;p&gt;“有了深蓝，当房间着火的时候，我们的程序可以做出超人一般的象棋棋步。”埃茨奥尼开玩笑说。“对吧？完全缺乏背景。快进20年，当房间着火的时候，我们有了一台电脑，可以下出超人一般的围棋棋步。”&lt;/p&gt;
 &lt;p&gt;当然，人类没有这个限制。如果发生火灾，人们会拉响警报，奔向大门。&lt;/p&gt;
 &lt;p&gt;换句话说，人类拥有关于这个世界的基本知识(火会烧东西) ，同时还有推理的能力(你应该试着远离失控的火)。&lt;/p&gt;
 &lt;p&gt;为了让人工智能真正像人类一样思考，我们需要教它所有人都知道的东西，比如物理学(抛向空中的球会落下)或相对大小的东西(大象无法被放进浴缸)。 在人工智能拥有这些基本概念之前，埃茨奥尼认为人工智能无法进行推理。&lt;/p&gt;
 &lt;p&gt;随着保罗·艾伦（Paul Allen）投入了数亿美元，埃茨奥尼和他的团队正在努力开发一个常识推理层，以适应现有的神经网络。(艾伦研究所是一个非营利组织，所以他们发现的一切都将被公开，任何人都可以使用。)&lt;/p&gt;
 &lt;p&gt;他们面临的第一个问题，就是回答一个问题：什么是常识？&lt;/p&gt;
 &lt;p&gt;埃茨奥尼把它描述为我们认为理所当然，但很少大声说出的关于世界的所有知识。他和他的同事创造了一系列基准问题，一个真正理性的人工智能应该能够回答：如果我把袜子放在抽屉里，它们明天会在那里吗？如果我踩了别人的脚趾，他们会生气吗？&lt;/p&gt;
 &lt;p&gt;获取这种知识的一种方法，是从人类那里提取。埃茨奥尼的实验室正在付费给亚马逊土耳其机器人上的众包人员，以帮助他们制作常识性的陈述。&lt;/p&gt;
 &lt;p&gt;然后，研究团队会使用各种机器学习技术——一些老式的统计分析，一些深度学习的神经网络——基于这些陈述进行训练。如果他们做得对，埃茨奥尼相信他们可以生产出可重复使用的计算机推理“乐高积木”：一套能够理解文字，一套能够掌握物理知识，等等。&lt;/p&gt;
 &lt;p&gt;崔叶金 (Yejin Choi)是埃茨奥尼团队研究常识的科学家之一，她负责了几次众包工作。 在一个项目中，她想开发一种人工智能，能够理解一个人的行为，或陈述出来其隐含的意图或情感。&lt;/p&gt;
 &lt;p&gt;她首先研究了成千上万个 Wiktionary 中的在线故事、博客和习语条目，提取出“短语事件”，比如“杰夫（Jeff）把罗杰（Roger）打昏了” 。然后，她会匿名记录每个短语——“X把Y打昏”——并要求土耳其机器人平台上的众包人员描述X的意图：他们为什么这样做？&lt;/p&gt;
 &lt;p&gt;当她收集了25000个这样的标记句子后，她用它们训练一个机器学习系统，来分析它从未见过的句子，并推断出句子的情绪或意图。&lt;/p&gt;
 &lt;p&gt;充其量，新系统运行的时候，只有一半时间是正常的。但是当它正式运行的时候，它展示了一些非常人性化的感知：给它一句像“奥伦（Oren）做了感恩节晚餐”这样的话，它预测奥伦试图给家人留下深刻印象。&lt;/p&gt;
 &lt;p&gt;“我们也可以对其他人的反应进行推理，即使他们没有被提及，”崔说。“所以X的家人可能会感到印象深刻和被爱。”&lt;/p&gt;
 &lt;p&gt;她的团队建立的另一个系统使用土耳其机器人平台上的众包人员在故事中标记人们的心理状态；当给定一个新的情况时，由此产生的系统也可以得出一些“尖锐”的推论。&lt;/p&gt;
 &lt;p&gt;例如，有人告诉我，一名音乐教练对他的乐队糟糕的表演感到愤怒，并说“教练很生气，把他的椅子扔了。人工智能会预测他们会“事后感到恐惧”，尽管这个故事没有明确说明这一点。&lt;/p&gt;
 &lt;p&gt;崔叶金、埃茨奥尼和他们的同事并没有放弃深度学习。事实上，他们认为这是一个非常有用的工具。但是，他们不认为有捷径，可以说服人们明确陈述我们所有人都拥有的怪异、无形、隐含的知识。&lt;/p&gt;
 &lt;p&gt;深度学习是垃圾输入，垃圾输出。仅仅给一个神经网络提供大量新闻文章是不够的，因为它不会吸取未陈述的知识，这是作家们不愿提及的显而易见的事情。&lt;/p&gt;
 &lt;p&gt;正如崔叶金所说，“人们不会说‘我的房子比我大’。”为了帮助解决这个问题，她让土耳其机器人平台上的众包人员分析了1100个常见动词所隐含的物理关系，例如“X扔了Y”。这反过来又提供了一个简单的统计模型，可以用“奥伦扔了一个球”这个句子来推断球一定比奥伦小。&lt;/p&gt;
 &lt;p&gt;另一个挑战是视觉推理。阿尼鲁达·凯姆巴维（Aniruddha Kembhavi）是埃茨奥尼团队中的另一位人工智能科学家，他向我展示了一个在屏幕上漫步的虚拟机器人。 艾伦研究所的其他科学家建造了类似模拟人生的房子，里面装满了日常用品——厨房橱柜里装满了碗碟，沙发可以随意摆放，并符合现实世界中的物理定律。&lt;/p&gt;
 &lt;p&gt;然后他们设计了这个机器人，它看起来像是一个有手臂的深灰色垃圾筒，研究人员告诉它，让它搜寻某些物品。在完成数千项任务后，这个神经网络获得了在现实生活中生活的基础。&lt;/p&gt;
 &lt;p&gt;“当你问它‘我有西红柿吗？它不会打开所有的橱柜。它更倾向去打开冰箱，”凯姆巴韦说。“或者，如果你说‘给我找我的钥匙’，它不会试图拿起电视。它会去看电视机后面。它已经知道，电视机通常不会被拿走。”&lt;/p&gt;
 &lt;p&gt;埃茨奥尼和他的同事希望这些不同的组成部分——崔叶金的语言推理、视觉思维，以及他们正在做的让人工智能掌握教科书科学信息的其他工作——最终能够结合在一起。&lt;/p&gt;
 &lt;p&gt;但是需要多长时间，最终的产品会是什么样子？他们不知道。他们正在建立的常识系统仍然会出错，有时甚至超过一半的概率。&lt;/p&gt;
 &lt;p&gt;崔叶金估计，她将需要大约一百万人工语言来训练她的各种语言解析器。 建立常识似乎异乎寻常地困难。&lt;/p&gt;
 &lt;h3&gt;四、&lt;/h3&gt;
 &lt;p&gt;制造机器还有其他合理的方式，但它们的劳动密集程度更高。 例如，你可以坐下来，用手写出所有要告诉机器世界如何运作的规则。 这就是道格·莱纳特（Doug Lenat）的 Cyc 项目的工作原理。&lt;/p&gt;
 &lt;p&gt;34年来，莱纳特雇佣了一个工程师和哲学家团队，来编写2500万条常识性规则，比如”“水是湿的”或者“大多数人都知道他们朋友的名字”。这让Cyc能够推断：“如果你的衬衫湿了，所以你可能是在雨中。” 优势在于，莱纳特能够精确地控制输入 Cyc 数据库的内容; 而众包知识并非如此。&lt;/p&gt;
 &lt;p&gt;这种由粗暴的手动行为做出来的人工智能，在深度学习的世界中已经变得不流行。这在一定程度上是因为它可能“脆弱”：如果没有正确的世界规则，人工智能可能会陷入困境。这就是程式化的聊天机器人如此“智障”的原因；如果如果没有明确告诉它们如何回答一个问题，它们没有办法推理出来。&lt;/p&gt;
 &lt;p&gt;Cyc的能力比聊天机器人更强，并且已经经过批准，可以用于医疗保健系统、金融服务和军事项目。但是这项工作进展非常缓慢，而且耗资巨大。莱纳特说开发Cyc花费了大约2亿美元。&lt;/p&gt;
 &lt;p&gt;但是，一点一点地进行手工编程可能只是复制一些固有的知识，根据乔姆斯基（Chomskyite）的观点，这是人类大脑拥有的知识。&lt;/p&gt;
 &lt;p&gt;这就是迪利普·乔治和研究人员对Breakout所做的事情。为了创造一个不会面对游戏布局变化而变“智障”的人工智能，他们放弃了深入学习，建立了一个包含硬编码基本假设的系统。&lt;/p&gt;
 &lt;p&gt;乔治告诉我，他们的人工智能不费吹灰之力就学会了“物体是存在的，物体之间有相互作用，一个物体的运动与其和其他物体之间的碰撞有因果关系。”&lt;/p&gt;
 &lt;p&gt;在Breakout中，这套系统发展出了衡量不同行动过程及其可能结果的能力。但这也起到了相反的作用。如果人工智能想要打破屏幕最左上角的一个砖块，它会理性地将平板放在最右边的角落。&lt;/p&gt;
 &lt;p&gt;这意味着，当Vicarious改变游戏的规则时——添加新砖块或提升平板——系统会得到补偿。 它似乎抓住了一些关于 Breakout 本身的通用性理解。&lt;/p&gt;
 &lt;p&gt;显然，这种人工智能在工程中存在权衡。 可以说，精心设计和仔细规划，以精确找出将什么预先设定的逻辑输入到系统中，是一个更艰苦的工作。 在设计一个新系统时，很难在速度和精度之间取得恰当的平衡。&lt;/p&gt;
 &lt;p&gt;乔治说，他寻找最小的数据集“放入模型，以便它能够快速学习。”你需要的假设越少，机器做决策的效率就越高。&lt;/p&gt;
 &lt;p&gt;一旦你训练了一个深度学习模型来识别猫，你就可以给它看一只它从未见过的俄罗斯蓝猫，然后它就会立刻给出结论——这是一只猫。 在处理了数百万张照片之后，它不仅知道是什么让一只猫变成了猫，还知道识别一只猫的最快方法。&lt;/p&gt;
 &lt;p&gt;相比之下，Vicarious的人工智能速度较慢，因为随着时间的推移，它会主动地做出逻辑推论。&lt;/p&gt;
 &lt;p&gt;当Vicarious的人工智能运行良好时，它可以从更少的数据中学习。乔治的团队通过识别扭曲的字体形象，创造一种人工智能来突破神经网络上“我不是机器人”的障碍。&lt;/p&gt;
 &lt;p&gt;就像Breakout系统一样，他们预先给人工智能赋予了一些能力，比如帮助它识别字符的知识。随着引导就位，他们只需要在人工智能学会以90.4 %的准确率破解验证码之前，在260张图像上训练人工智能。相比之下，神经网络需要在超过230万张图像上训练，才能破解验证码。&lt;/p&gt;
 &lt;p&gt;其他人，正在以不同的方式将常识般的结构构建到神经网络中。例如，DeepMind的两名研究人员最近创建了一个混合系统：部分是深度学习，部分是更传统的技术。他们将这个系统称为归纳逻辑编程。目标是创造出能够进行数学推理的东西。&lt;/p&gt;
 &lt;p&gt;他们用儿童游戏“fizz-buzz”来训练它，在这个游戏中，你从1开始向上数，如果一个数字可以被3整除，就说“fizz”，如果它可以被5整除，就说“buzz”。一个普通的神经网络，只能处理它以前见过的数字；如果把它训练到100分钟，它就会知道99时该“fizz”，100时“buzz”。&lt;/p&gt;
 &lt;p&gt;但它不知道如何处理105。相比之下，DeepMind的混合深度思维系统似乎理解了这个规则，并在数字超过100时没有出现任何问题。爱德华·格雷芬斯特（Edward Grefenstette）是开发这种混合系统的DeepMind程序员之一，他说，“你可以训练出一些系统，这些系统会以一种深度学习网络无法独自完成的方式进行推理。”&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://www.199it.com/wp-content/uploads/2018/11/1543343069-2515-opy5sj33nk40d0ny1200.jpg"&gt;   &lt;img alt="" height="800" src="http://www.199it.com/wp-content/uploads/2018/11/1543343069-2515-opy5sj33nk40d0ny1200.jpg" width="1200"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;深度学习的先驱、Facebook人工智能研究部门的现任负责人杨立昆（Yann LeCun）对许多针对这个领域的批评表示赞同。他承认，它需要太多的训练数据，不能推理，也不具备常识。&lt;/p&gt;
 &lt;p&gt;“在过去的四年里，我基本上在一遍又一遍地重复这句话，”他提醒我。但是他仍然坚信，进行正确的深入学习，可以获取答案。他不同意乔姆斯基对人类智力的看法。他认为，人类大脑是通过互动而不是内在的规则来发展出推理能力的。&lt;/p&gt;
 &lt;p&gt;“如果你思考一下动物和婴儿是如何学习的，在生命的最初几分钟、几小时、几天里，学很多东西都学得很快，以至于看起来像是天生的，”他指出。“但事实上，他们不需要硬编码，因为它们可以很快学会一些东西。”&lt;/p&gt;
 &lt;p&gt;从这个角度来看，为了了解世界的物理规律，一个婴儿只需要四处移动它的头，对传入的图像进行数据处理，并得出结论，景深就是这么一回事。&lt;/p&gt;
 &lt;p&gt;尽管如此，杨立昆承认，目前还不清楚哪些途径可以帮助深度学习走出低谷。有可能是“对抗性”神经网络，一种相对新的技术，其中一个神经网络试图用虚假数据欺骗另一个神经网络，迫使第二个神经网络发展出极其微妙的图像、声音和其他输入的内部表征。&lt;/p&gt;
 &lt;p&gt;它的优势是没有“数据缺乏”的问题。你不需要收集数百万个数据来训练神经网络，因为它们是通过相互学习来学习的。(作者注：一种类似的方法正在被用来制作那些让人深感不安的“深度伪造”（deepfake）视频，在这些视频中，有些人似乎在说或做一些他们没有说或做的事情。)&lt;/p&gt;
 &lt;p&gt;我在Facebook位于纽约的人工智能实验室的办公室里遇见了杨立昆。马克·扎克伯格（Mark Zuckerberg）在2013年招募了他，承诺实验室的目标将是推动人工智能突破极限，而不仅仅是对Facebook的产品进行微小的调整。像学术实验室一样，杨立昆和他的研究人员可以将他们的研究成果发表出来，供其他人参阅。&lt;/p&gt;
 &lt;p&gt;杨立昆仍然保留了他的法国本土口音，他站在白板前，精力充沛地勾画出可能推动深入学习进步的理论。对面的墙上挂着一套斯坦利·库布里克（Stanley Kubrick）《2001：太空漫游》（2001: A Space Odyssey）中的华丽画作——漂浮在太空深处的主宇宙飞船，一艘绕地球运行的轮式飞船。“哦，是的，”当我指出他们时，杨立昆说，他们重印了库布里克为这部电影制作的艺术品。&lt;/p&gt;
 &lt;p&gt;借着周围的图片来讨论类人人工智能，让人感到莫名的不安，因为2001年的HAL 9000，一个类人人工智能，是一个高效的杀手。&lt;/p&gt;
 &lt;p&gt;这指向了一个更深层次的哲学问题，超越了人工智能发展方向的争论：制造更聪明的人工智能是一个好主意吗？Vicarious的系统破解了验证码，但验证码的意义在于防止机器人模仿人类。&lt;/p&gt;
 &lt;p&gt;一些人工智能研究者担心，与人类交谈并理解人类心理的能力可能会使恶人工智能变得极其危险。 牛津大学的尼克 · 博斯特龙(Nick Bostrom)敲响了创造”超级智能”(superintelligence)的警钟。超级智能是一种自我改进并快速超越人类的人工智能，能够在各个方面超越我们。 (他认为积聚控制力的一种方式是通过操纵人们——拥有”心智理论”对此会非常有用。)&lt;/p&gt;
 &lt;p&gt;埃隆·马斯克（Elon Musk）对这种危险深信不疑，他资助了致力于安全人工智能理念的组织OpenAI。&lt;/p&gt;
 &lt;p&gt;这样的未来不会让埃齐奥尼晚上失眠。他不担心人工智能会变成恶意的超级智能。“我们担心会有什么东西会接管这个世界，”他嘲笑道，“那甚至不能自己决定再下一盘棋。”目前，还不清楚人工智能会如何发展出这些意愿，也不清楚这种意愿软件中会是什么样子。深度学习可以征服国际象棋，但它没有天生的下棋意愿。&lt;/p&gt;
 &lt;p&gt;令他担忧的是，是目前的人工智能非常无能。因此，虽然我们可能不会创造出具有自我保护智能的HAL，但他说，“致命武器+无能的人工智能很容易杀人。”这也是为什么埃齐奥尼如此坚决地要给人工智能灌输一些常识的部分原因。他认为，最终，这将使人工智能更加安全；不应该大规模屠杀人类，也是一种常识。(艾伦研究所的一部分任务是使人工智能更加合理化，从而使其更加安全。)&lt;/p&gt;
 &lt;p&gt;埃齐奥尼指出，对人工智能的反乌托邦式的科幻愿景，其风险要小于短期的经济转移。如果人工智能在常识方面做得更好，它就能更快地完成那些目前仅仅是模式匹配深度学习所难以完成的工作：司机、出纳员、经理、各行各业的分析师，甚至是记者。&lt;/p&gt;
 &lt;p&gt;但真正有理性的人工智能造成的破坏甚至可能会超出经济范围。 想象一下，如果散布虚假政治信息的机器人能够运用常识，在 Twitter、 Facebook 或大量电话中显得与人类毫无区别，那该会是什么样子。&lt;/p&gt;
 &lt;p&gt;马库斯同意人工智能具备推理能力会有危险。但是，他说，这样带来的好处是巨大的。人工智能可以像人类一样推理和感知，但却能以计算机的速度运算，它可以彻底改变科学，以我们人类不可能的速度找出因果关系。&lt;/p&gt;
 &lt;p&gt;除了拥有大量的机器人知识之外，它可以像人类一样进行心理实验，可以遵循“if – then”链条，思考反事实。“例如，最终我们可能能够治愈精神疾病，”马库斯补充道。“人工智能或许能够理解这些复杂的蛋白质生物级联，这些蛋白质参与到了大脑的构建中，会让它们正常工作或不正常工作。”&lt;/p&gt;
 &lt;p&gt;坐在《2001：太空漫游》的照片下面，杨立昆自己提出了一个“异端”观点。当然，让人工智能更加人性化有助于人工智能给我们的世界提供帮助。但是直接复制人类的思维方式呢？没有人清楚这是否有用。我们已经有了像人类一样思考的人；也许智能机器的价值在于它们与我们完全不同。&lt;/p&gt;
 &lt;p&gt;“如果他们有我们没有的能力，他们会更有用，”他告诉我。“那么他们将成为智力的放大器。所以在某种程度上，你希望他们拥有非人类形式的智力……你希望他们比人类更理性。”换句话说，也许让人工智能有点人工是值得的。&lt;/p&gt;
 &lt;p&gt;原文链接：  &lt;a href="https://www.wired.com/story/how-to-teach-artificial-intelligence-common-sense/"&gt;https://www.wired.com/story/how-to-teach-artificial-intelligence-common-sense/&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;   &lt;a href="https://www.36kr.com/p/5163672"&gt;编译组出品&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;h3&gt;更多阅读：&lt;/h3&gt;   &lt;ul&gt;    &lt;li&gt;     &lt;a href="http://www.199it.com/archives/541924.html"&gt;一张图了解深度学习的前世今生——信息图&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;a href="http://www.199it.com/archives/504272.html"&gt;人工智能、机器学习、深度学习三者之间的同心圆关系&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;a href="http://www.199it.com/archives/574043.html"&gt;Tractica：预计2025年全球深度学习芯片市场将达到122亿美元&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;a href="http://www.199it.com/archives/714988.html"&gt;2018人工智能前沿报告：深度学习的应用和价值&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;a href="http://www.199it.com/archives/687600.html"&gt;Accenture：调查显示企业不愿花钱对员工进行人工智能培训&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;a href="http://www.199it.com/archives/575066.html"&gt;Yann LeCun清华演讲：深度学习与人工智能的未来（附下载）&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;a href="http://www.199it.com/archives/440552.html"&gt;艾瑞咨询 | 2015年中国人工智能应用市场研究报告&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;a href="http://www.199it.com/archives/522764.html"&gt;深度学习洪流：为何它能瞬间改变你的生活？（上）&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;a href="http://www.199it.com/archives/550215.html"&gt;新智元：2016年全球AI巨头开源深度学习库Top 50&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;a href="http://www.199it.com/archives/522768.html"&gt;深度学习洪流：为何它能瞬间改变你的生活？ （下）&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;a href="http://www.199it.com/archives/600713.html"&gt;2017美国财富500强： 智能汽车厂商特斯拉、人工智能芯片商英伟达首次入围&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;a href="http://www.199it.com/archives/561235.html"&gt;2017年最值得关注的5家深度学习创业公司&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;a href="http://www.199it.com/archives/572019.html"&gt;谷歌用深度学习协助病理学家检测癌症，准确率在89%&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;a href="http://www.199it.com/archives/526604.html"&gt;《财富》杂志：全球AI巨头正用深度学习改变生活&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;a href="http://www.199it.com/archives/522981.html"&gt;AI革命：深度学习缘何突然改变你的生活&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>人工智能 深度学习</category>
      <guid isPermaLink="true">https://itindex.net/detail/59010-%E8%A1%A8%E9%9D%A2-%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD-%E5%8F%91%E5%B1%95</guid>
      <pubDate>Wed, 28 Nov 2018 02:36:02 CST</pubDate>
    </item>
    <item>
      <title>CTR 预估模型简介--非深度学习篇</title>
      <link>https://itindex.net/detail/58525-ctr-%E6%A8%A1%E5%9E%8B-%E7%AE%80%E4%BB%8B</link>
      <description>&lt;p&gt;本文主要介绍 CTR 预估中常用的一些模型，主要是非深度学习模型，包括 LR、GBDT+LR、FM/FFM、MLR。每个模型会简单介绍其原理、论文出处以及其一些开源实现。&lt;/p&gt;
 &lt;a&gt;&lt;/a&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#LR-Logistic-Regerssion" title="LR(Logistic Regerssion)"&gt;&lt;/a&gt;LR(Logistic Regerssion)&lt;/h2&gt; &lt;p&gt;  &lt;strong&gt;LR + 海量人工特征&lt;/strong&gt; 是业界流传已久的做法，这个方法由于简单、可解释性强，因此在工业界得到广泛应用，但是这种做法依赖于特征工程的有效性，也就是需要对具体的业务场景有深刻的认识才能提取出好的特征。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#21407;&amp;#29702;" title="&amp;#21407;&amp;#29702;"&gt;&lt;/a&gt;  &lt;strong&gt;原理&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;LR 是一个很简单的线性模型，其输出的值可认为是事件发生($y=1$)的概率，即输出值如下式所示&lt;/p&gt;
 &lt;p&gt;$$ h(x) = p(y=1|x) = \sigma(w^Tx+b)$$&lt;/p&gt;
 &lt;p&gt;其中$w$ 为模型参数，$x$ 为提取的样本特征，两者均为向量，$b$ 是偏置项。$\sigma$ 为 sigmoid 函数，即 $\sigma(x) = 1/(1+e^{-x})$&lt;/p&gt;
 &lt;p&gt;有了事件发生的概率，则事件不发生的概率为 $p(y=0|x) = 1-h(x)$,将这两个概率通过如下一条公式表示为&lt;/p&gt;
 &lt;p&gt;$$p(y|x) = h(x)^y(1-h(x))^{1-y}$$&lt;/p&gt;
 &lt;p&gt;有了这个概率值，则给定 $n$ 个样本，便可通过极大似然估计来估算模型参数，即目标函数为&lt;/p&gt;
 &lt;p&gt;$$\max \prod_{i=1}^np(y_i|x_i)$$&lt;/p&gt;
 &lt;p&gt;通常我们还会对概率取 log，同时添加负号将 max 改成min，则可将目标函数改写成如下的形式&lt;/p&gt;
 &lt;p&gt;$$\min -\sum_{i=1}^ny_i\log h(x_i)+(1-y_i)\log (1-h(x_i))$$&lt;/p&gt;
 &lt;p&gt;上面的损失函数也叫作   &lt;strong&gt;log loss&lt;/strong&gt;，实际上多分类的   &lt;strong&gt;cross entropy&lt;/strong&gt; 也同以通过极大似然估计推导出来。&lt;/p&gt;
 &lt;p&gt;有了损失函数，便可通过优化算法来求出最优的参数，由于这是个无约束的最优化问题，可选用的方法很多，最常用的就是 gradient descent，除此之外，另外还有基于二阶导数的牛顿法系列，适用于分布式中的 ADMM，以及由 Google 在论文   &lt;a href="https://static.googleusercontent.com/media/research.google.com/zh-CN//pubs/archive/41159.pdf" rel="external" target="_blank"&gt;Ad Click Prediction: a View from the Trenches&lt;/a&gt; 中提出的 FTRL 算法，目前也是业界普遍采用的方法，该算法具有online learning 和稀疏性的良好特性，online learning 指的是其更新方式与 SGD(stochastic gradient descent) 相似，稀疏性指的是该算法能够解决带非光滑的L1正则项的优化问题。由于这里这篇文章主要讲述各种 CTR 预估模型，因此这里不对优化算法做展开了。&lt;/p&gt;
 &lt;p&gt;上面提到了 L1 正则项，就是在原来的损失函数基础上加上了 $C\sum_{i=1}^m |w_i|$ 这一项, 表示各个参数的绝对值的和乘上常数 $C$；加上这一项后能够使得最终的求解出来的参数中大部分的 $|w_i|$ 为0，这也是稀疏性的名称来源。稀疏性使得模型的复杂度下降，缓解了过拟合的问题，同时具有有特征筛选的能力。因为 LR 模型可以理解为对各个特征进行加权求和，如果某些特征的权重即 $w_i$ 为0，则可认为这些特征的重要性不高。在CTR预估中输入的是海量人工特征，因此添加 L1 正则化就更有必要了。&lt;/p&gt;
 &lt;p&gt;由于 L1 正则项不再是处处光滑可导的函数，因此在优化损失函数时。原来的 gradient descent 不能够直接使用，而是要通过   &lt;a href="https://en.wikipedia.org/wiki/Subgradient_method" rel="external" target="_blank"&gt;subgradient&lt;/a&gt; 的方法或前面提到的 FTRL 算法进行优化。&lt;/p&gt;
 &lt;p&gt;上面涵盖了 LR 模型的基本原理。而  &lt;strong&gt;在 CTR 预估中，应用 LR 模型的重点在于特征工程。LR 模型适用于高维稀疏特征&lt;/strong&gt;。对于 categorical 特征，可以通过 one-hot 编码使其变得高纬且稀疏。而对于 continious 特征，可以先通过区间划分为 categorical 特征再进行 one-hot 编码。同时还需要进行特征的组合/交叉，以获取更有效的特征。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#19968;&amp;#20123;&amp;#38382;&amp;#39064;" title="&amp;#19968;&amp;#20123;&amp;#38382;&amp;#39064;"&gt;&lt;/a&gt;  &lt;strong&gt;一些问题&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;上面介绍过程中有一些结论我们直接就使用了，下面对于上面提到的某些结论做出一些解释&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;1. LR 的输出为什么可以被当做是概率值？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;这部分涉及到广义线性模型(GLM，  &lt;a href="https://en.wikipedia.org/wiki/Generalized_linear_model" rel="external" target="_blank"&gt;Generalized linear model&lt;/a&gt;) 的知识，这里略过复杂的推导，直接给出结论。简单来说，LR 实际上是一个广义线性模型，其假设是二分类中 $(y|x,\theta)$ 服从伯努利分布(二项分布)，即给定输入样本 $x$ 和模型参数 $\theta$, 事件是否发生服从伯努利分布。假设伯努利分布的参数 $\phi$ ，则 $\phi$ 可作为点击率。通过 广义线性模型的推导，能够推出 $\phi$ 的表示形式如下&lt;/p&gt;
 &lt;p&gt;$$\phi = 1/(1+e^{-\eta})$$&lt;/p&gt;
 &lt;p&gt;从上面的式子可知，  &lt;strong&gt;LR 中的 sigmoid 函数并不是凭空来的&lt;/strong&gt;，而式子中的 $\eta$ 也被称为连接函数（Link function), 是确定一个 GLM 的重要部分，在 LR 中为简单的线性加权。&lt;/p&gt;
 &lt;p&gt;另外，如果将输出值与真实值的误差的分布假设为高斯分布，那么从 GLM 可推导出 Linear Regression，关于 GLM 详细的推导可参考这篇文章   &lt;a href="https://www.cnblogs.com/dreamvibe/p/4259460.html" rel="external" target="_blank"&gt;广义线性模型（GLM）&lt;/a&gt;。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2. 为什么 L1 正则项能够带来稀疏性？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;这里有个很直观的回答，  &lt;a href="https://www.zhihu.com/question/37096933/answer/70426653" rel="external" target="_blank"&gt;l1 相比于 l2 为什么容易获得稀疏解？&lt;/a&gt;，简单来说，就是  &lt;strong&gt;当不带正则项的损失函数对于某个参数 $w_i$ 的导数的绝对值小于 l1 正则项中的常数 $C$ 时，这个参数 $w_i$ 的最优解就是0&lt;/strong&gt;。&lt;/p&gt;
 &lt;p&gt;因为求解某个参数 $w_i$ 使得损失函数取极小值时可分两种情况讨论(下面的 $L$ 为不带正则项的损失函数)  &lt;br /&gt;1）$w_i&amp;lt;0$ 时, $L+C|w_i|$ 的导数为 $L’- C$  &lt;br /&gt;2) $w_i&amp;gt;0$时, $L+C|w_i|$ 的导数为 $L’+C$&lt;/p&gt;
 &lt;p&gt;当 $w_i&amp;lt;0$ 时，令=&amp;quot;&amp;quot; $l&amp;apos;-=&amp;quot;&amp;quot; c=&amp;quot;&amp;quot; &amp;lt;=&amp;quot;&amp;quot; 0$,=&amp;quot;&amp;quot; 函数在递减；而当$w_i=&amp;quot;&amp;quot;&amp;gt;0$时, 令 $L&amp;apos;+C &amp;gt; 0$, 函数在递增，则 $w_i=0$ 便是使得损失函数最小的最优解，且结合 $L’- C &amp;lt; 0$ 和 $L’+C &amp;gt; 0$，可得 $C &amp;gt; |L’|$。这便是我们上面得到的结论，上面是针对某一个参数，实际上也可以推广到所有参数上。事实上，通过 subgradient descent 求解这个问题时也能够得到相同的结论。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3.连续特征为什么需要离散化？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;参考这个问题：  &lt;a href="https://www.zhihu.com/question/31989952/answer/54184582" rel="external" target="_blank"&gt;连续特征的离散化：在什么情况下将连续的特征离散化之后可以获得更好的效果？&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;离散化后有以下几个好处：&lt;/p&gt;
 &lt;p&gt;1) 稀疏向量内积乘法运算速度快，计算结果方便存储  &lt;br /&gt;2) 离散化后的特征对异常数据有很强的鲁棒性：比如一个特征是年龄&amp;gt;30是1，否则0。如果特征没有离散化，一个异常数据“年龄300岁”会给模型造成很大的干扰；  &lt;br /&gt;3) 逻辑回归属于广义线性模型，表达能力受限；单变量离散化为N个后，  &lt;strong&gt;可以通过 one-hot 编码为每个变量设置单独的权重，相当于为模型引入了非线性，能够提升模型表达能力，加大拟合&lt;/strong&gt;；  &lt;br /&gt;4) 离散化后可以进行  &lt;strong&gt;特征交叉&lt;/strong&gt;，由M+N个变量变为M*N个变量，进一步引入非线性，提升表达能力；  &lt;br /&gt;5) 特征离散化后，模型会更稳定，比如如果对用户年龄离散化，20-30作为一个区间，不会因为一个用户年龄长了一岁就变成一个完全不同的人。当然处于区间相邻处的样本会刚好相反，所以怎么划分区间要取决于具体的场景&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4.1 为什么要对 categorical 特征做 One-hot 编码后再输入 LR？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;参考这篇文章   &lt;a href="http://www.jiehuozhe.com/article/3" rel="external" target="_blank"&gt;One-Hot编码与哑变量&lt;/a&gt;，简单来说，就是LR建模时，要求特征具有线性关系，而实际应用中很少有满足这个假设关系的，因此LR模型效果很难达到应用要求。但是通过对离散特征进行 one-hot 编码，LR 可以为某个特征中所有可能的值设置一个权重，这样就能够更准确的建模，也就能够获得更精准的模型。而 one-hot 编码后特征实际上也是做了一个 min-max 归一化，能够克服不同特征的量纲差异，同时使模型收敛更快。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;" title="&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;"&gt;&lt;/a&gt;  &lt;strong&gt;开源实现&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;由于 LR 模型的广泛性，基本上每个机器学习库或者框架都有相关实现，如 sklearn 提供了  &lt;a href="http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html" rel="external" target="_blank"&gt;单机版的实现&lt;/a&gt;，spark 提供了  &lt;a href="https://spark.apache.org/docs/2.3.0/mllib-linear-methods.html" rel="external" target="_blank"&gt;分布式版本的实现&lt;/a&gt;，腾讯开源的 Parameter Server   &lt;a href="https://github.com/Tencent/angel" rel="external" target="_blank"&gt;Angel&lt;/a&gt; 中也提供了   &lt;a href="https://github.com/Tencent/angel/blob/master/docs/algo/sona/sparselr_ftrl.md" rel="external" target="_blank"&gt;LR+FTRL&lt;/a&gt; 的实现，Angel 支持 Spark，目前也还在开发中 。除此之外，Github 上也有很多个人开源的实现，这里不再列举。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#LS-PLM-Large-Scale-Piece-wise-Linear-Model" title="LS-PLM(Large Scale Piece-wise Linear Model)"&gt;&lt;/a&gt;LS-PLM(Large Scale Piece-wise Linear Model)&lt;/h2&gt; &lt;p&gt;LS-PLM(也叫作 MLR, Mixture of Logistics Regression)是阿里妈妈在 2017 年在论文   &lt;a href="https://arxiv.org/abs/1704.05194" rel="external" target="_blank"&gt;Learning Piece-wise Linear Models from Large Scale Data for Ad Click Prediction&lt;/a&gt; 中公开的，但是模型早在 2012 年就在阿里妈妈内部使用。这个模型在 LR 基础上进行了拓展，目的是为了解决单个 LR 无法进行非线性分割的问题。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#21407;&amp;#29702;-1" title="&amp;#21407;&amp;#29702;"&gt;&lt;/a&gt;  &lt;strong&gt;原理&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;LR 是一个线性模型，模型会在数据空间中生成一个线性分割平面，但是对于非线性可分的数据，这一个线性分割面显然无法正确分割这些数据。以下图为例（摘自上面的论文），A）为一组非线性训练数据的正负样本分布；对于该问题，LR会生成 B）中的分割平面，C) 图展示的 LS-PLM 模型 则取得了较好的效果。 &lt;/p&gt;
 &lt;p&gt;  &lt;img alt="LS-PIC" src="http://static.zybuluo.com/WuLiangchao/tzjd2ps2kobglypyw8f36dal/image_1cdhkuov7q2413hf3n14ecse0c.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;在CTR问题中，  &lt;strong&gt;划分场景分别建模&lt;/strong&gt;是一种常见的手法。例如，同一产品的PC/APP端，其用户的使用时间和习惯差异可能很大；比如PC可能更多是办公时间在看，而手机则是通勤时间或者临睡前使用更多。假设有hour作为特征，那么“hour=23”对于APP端更加有信息量，而对于PC可能意义不大。因此，区分PC/APP端分别建模可能提升效果。&lt;/p&gt;
 &lt;p&gt;LS-PLM 也是采用这个思想的，不够这里不是划分场景，而是划分数据，通过将数据划分不同的region、然后每个region分别建立 LR。&lt;/p&gt;
 &lt;p&gt;这里需要注意的是这里一个样本并不是被唯一分到了一个region，而是按权重分到了不同的region。其思想有点像 LDA(Latent Dirichlet allocation) 中一个单词会按照概率分到多个 topic 上。&lt;/p&gt;
 &lt;p&gt;论文中的公式如下&lt;/p&gt;
 &lt;p&gt;$$p(y=1|x) = g ( \sum_{j=1}^m \sigma(\mu_j^T x)\eta(w_j^Tx))  $$&lt;/p&gt;
 &lt;p&gt;公式中的符号定义如下：&lt;/p&gt;
 &lt;p&gt;参数定义如下：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;$m$ : region 的个数(超参数：一般是10~100)&lt;/li&gt;
  &lt;li&gt;$\Theta={\mu_1,\dots,\mu_m, w_1,\dots,w_m }$: 表示模型的参数，需要训练&lt;/li&gt;
  &lt;li&gt;$g(\cdot)$：为了让模型符合概率定义(概率和为1)的函数&lt;/li&gt;
  &lt;li&gt;$\sigma(\cdot)$：将样本分到 region 的函数&lt;/li&gt;
  &lt;li&gt;$\eta(\cdot)$：在 region 中划分样本的函数&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;前面提出的公式更像个框架，在论文中，只讨论了 $g(x) = x$, $\sigma$ = softmax ，$\eta$ = sigmoid 的情形，而且因此，上面的公式可写成如下的形式&lt;/p&gt;
 &lt;p&gt;$$p(y=1|x) = \sum_{i=1}^m \frac{e^{\mu  &lt;em&gt;i^Tx}}{\sum&lt;/em&gt;{j=1}^m e^{\mu_j^Tx}}\frac{1}{1+e^{-w_i^Tx}}$$&lt;/p&gt;
 &lt;p&gt;这个公式其实已经变成了通过多个 LR 模型进行加权求和的 bagging 模式，只是这里每个模型的权重是学习出来而不是事先确定的。&lt;/p&gt;
 &lt;p&gt;写出了概率函数, 后面的推导跟前面的 LR 其实是一样的，也是先通过极大似然估计得到 $\max$ 问题，添加负号后转为损失函数求 $\min$ 问题。这里不做详细的推导了。&lt;/p&gt;
 &lt;p&gt;在 LS-PLM 中也是需要添加正则项的，除了在 LR 中提到的 L1 正则化，论文还提出了 $L_{2,1}$ 正则项，表示如下&lt;/p&gt;
 &lt;p&gt;$$||\Theta||  &lt;em&gt;{2,1} = \sum&lt;/em&gt;{i=1}^d \sqrt {\sum  &lt;em&gt;{j=1}^m(\mu&lt;/em&gt;{ij}^2+w_{ij}^2)}$$&lt;/p&gt;
 &lt;p&gt;上式中的 $d$ 表示特征的维数，其中 $\sqrt {\sum  &lt;em&gt;{j=1}^m(\mu&lt;/em&gt;{ij}^2+w  &lt;em&gt;{ij}^2)}$ 表示对某一维特征的所有参数进行 L2 正则化，而外侧的 $\sum&lt;/em&gt;{i=1}^d$ 表示对所有的 feature 进行 L1 正则化，由于开方后的值必为正，因此这里也不用添加绝对值了。由于结合了 L1 和 L2 正则项，所以论文也将这个叫做$L_{2,1}$ 正则项。&lt;/p&gt;
 &lt;p&gt;由于损失函数和正则项都是光滑可导的，因此优化方面比带 L1 正则的 LR 更加简单，可选的优化方法也更多。&lt;/p&gt;
 &lt;p&gt;MLR 适用的场景跟 LR 一样，也是适用于高纬稀疏特征作为输入。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;-1" title="&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;"&gt;&lt;/a&gt;  &lt;strong&gt;开源实现&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;前面提到的腾讯的 PS Angel 实现了这个算法，具体可参考  &lt;a href="https://github.com/Tencent/angel/blob/master/docs/algo/mlr_on_angel.md" rel="external" target="_blank"&gt;这里&lt;/a&gt;；Angel 是用 Scala 开发的。也有一些个人开源的版本如   &lt;a href="https://github.com/CastellanZhang/alphaPLM" rel="external" target="_blank"&gt;alphaPLM&lt;/a&gt;，这个版本是用 C++ 写的，如果需要实现可以参考以上资料。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#GBDT-LR-Gradient-Boost-Decision-Tree-Logistic-Regression" title="GBDT+LR(Gradient Boost Decision Tree + Logistic Regression)"&gt;&lt;/a&gt;GBDT+LR(Gradient Boost Decision Tree + Logistic Regression)&lt;/h2&gt; &lt;p&gt;GBDT + LR 是 FaceBook 在这篇论文   &lt;a href="http://quinonero.net/Publications/predicting-clicks-facebook.pdf" rel="external" target="_blank"&gt;Practical Lessons from Predicting Clicks on Ads at Facebook&lt;/a&gt; 中提出的，其思想是借助 GBDT 帮我们做部分特征工程，然后将 GBDT 的 输出作为 LR 的输入。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#21407;&amp;#29702;-2" title="&amp;#21407;&amp;#29702;"&gt;&lt;/a&gt;  &lt;strong&gt;原理&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;我们前面提到的无论 LR 还是 MLR，都避免不了要做大量的特征工程。比如说构思可能的特征，将连续特征离散化，并对离散化的特征进行 One-Hot 编码，最后对特征进行二阶或者三阶的特征组合/交叉，这样做的目的是为了得到非线性的特征。但是特征工程存在几个难题：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;连续变量切分点如何选取？&lt;/li&gt;
  &lt;li&gt;离散化为多少份合理？&lt;/li&gt;
  &lt;li&gt;选择哪些特征交叉？&lt;/li&gt;
  &lt;li&gt;多少阶交叉，二阶，三阶或更多？&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;而 GBDT + LR 这个模型中，GBDT 担任了特征工程的工作，下面首先介绍一下 GBDT。&lt;/p&gt;
 &lt;p&gt;GBDT 最早在这篇论文   &lt;a href="https://statweb.stanford.edu/~jhf/ftp/trebst.pdf" rel="external" target="_blank"&gt;Greedy Function Approximation：A Gradient Boosting Machine&lt;/a&gt; 中提出；  &lt;br /&gt;GBDT 中主要有两个概念：GB(  &lt;a href="https://en.wikipedia.org/wiki/Gradient_boosting" rel="external" target="_blank"&gt;Gradient Boosting&lt;/a&gt;)和DT(Decision Tree)，Gradient Boosting 是集成学习中 boosting 的一种形式，Decision Tree 则是机器学习中的一类模型，这里不对这两者展开，只讲述在 GBDT 中用到的内容。关于决策树的介绍可参考这篇文章   &lt;a href="http://www.cnblogs.com/wxquare/p/5379970.html" rel="external" target="_blank"&gt;决策树模型 ID3/C4.5/CART算法比较&lt;/a&gt;。&lt;/p&gt;
 &lt;p&gt;在 GBDT 中采用的决策树是CART (Classification And Regression Tree)，将其当做回归树使用，这里的回归树是一棵在每个树节点进行分裂的时候，给节点设定其在某个特征的的值，若样本对应的特征的值大于这个给定的值的属于一个子树，小于这个给定的值的属于另一个子树。&lt;/p&gt;
 &lt;p&gt;那么，构建 CART 回归树是 的关键问题就在于选择具体的特征还有这个特征上具体的值了。选择的指标是  &lt;strong&gt;平方误差最小化准则&lt;/strong&gt;。对于任意一个切分，其平方误差计算方式如下&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;假设切分后左子树有 $m$ 个样本，右子树有 $n$ 个&lt;/li&gt;
  &lt;li&gt;计算左子树样本的目标值的均值为 $y   &lt;em&gt;m = \frac{1}{m}\sum&lt;/em&gt;{i=1}^{m}y_i$, 同样计算右子树样本的目标值的均值为 $y   &lt;em&gt;n = \frac{1}{n}\sum&lt;/em&gt;{j=1}^{n}y_j$&lt;/li&gt;
  &lt;li&gt;平方误差和为 $L = \sum_{i=1}^m(y_i - y   &lt;em&gt;m)^2 + \sum&lt;/em&gt;{j=1}^n(y_j - y_n)^2$&lt;/li&gt;
  &lt;li&gt;对于每一个可能的切分值，我们都可计算其平方误差和 $L$，选择使得 $L$ 最小的切分点即可。&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;上面便是 GBDT 中的 “DT” 部分，用于解决一个回归问题，也就是给定一组样本，我们可以通过上面的方式来构建出一棵 CART 来拟合这组样本。下面我们来讲一下 GBDT 中的 “GB” 部分。&lt;/p&gt;
 &lt;p&gt;简单来说，  &lt;strong&gt;gradient boosting 就是将若干个模型的输出进行叠加作为最终模型的输出。&lt;/strong&gt;如下图是一个简单的例子(图片来源于提出 xgb 的论文：  &lt;a href="https://arxiv.org/pdf/1603.02754.pdf" rel="external" target="_blank"&gt;XGBoost: A Scalable Tree Boosting System&lt;/a&gt;)&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="xgboost" src="http://static.zybuluo.com/WuLiangchao/di65wcw7az2e187qfsghn6xw/gbdt.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;下式就是叠加了 $T$ 个 $f_t(x)$ 模型作为最终的模型，$f_t(x)$ 在 GBDT 中就是一棵 CART，当然 $f_t(x)$ 不限于树模型。&lt;/p&gt;
 &lt;p&gt;$$F(x) = \sum_{t=1}^Tf_t(x)$$&lt;/p&gt;
 &lt;p&gt;在构建每棵树的时候，输入的样本不同的地方在于每个样本的目标值 $y$；如构建第 $k$ 棵树，对于原始样本 $(x_i, y_i)$, 其目标值变为 &lt;/p&gt;
 &lt;p&gt;$$y_{ik} = y  &lt;em&gt;i - \sum&lt;/em&gt;{t=1}^{k-1}f_t(x_i)$$&lt;/p&gt;
 &lt;p&gt;即输入第 $k$ 棵树的样本变为 $(x  &lt;em&gt;i, y&lt;/em&gt;{ik})$，所以在  &lt;strong&gt;构建第 $k$ 棵树的时候，实际上是在拟合前 $k-1$ 棵树的输出值的和与样本真实值的残差。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;回到我们的 GBDT + LR 模型，首先通过前面提到的 GBDT 训练出一批树模型，然后样本输入每棵树后最终都会落到一个具体的叶子节点上，那我们就将这个节点标为 1，其他叶子节点标为 0，  &lt;strong&gt;这样每棵树输出的就相当于是一个 one-hot 编码的特征&lt;/strong&gt;。如下图是摘自 FaceBook 原始论文的图，里面有两棵树，假如输入 $x$ 在第一棵树中落入第一个叶子节点，在第二棵树种落入第二个叶子节点，那么输入 LR 的特征为 [1, 0, 0, 0, 1].&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="GBDT + LR" src="http://static.zybuluo.com/WuLiangchao/yapne7qwulzl0j4kfozdixnx/image_1cdjf062nsovu4v1d3bbsn1rk49.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;GBDT+LR 方案中每棵决策树从根节点到叶节点的路径，会经过不同的特征，此路径就是特征组合，而且包含了二阶，三阶甚至更多，因此输出的 one-hot 特征是原始特征进行交叉后的结果。而且每一维的特征其实还是可以追溯出其含义的，因为从根节点到叶子节点的路径是唯一的，因此落入到某个叶子节点表示这个特征满足了这个路径中所有节点判断条件。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;GBDT 适用的问题刚好与 LR 相反，GBDT 不适用于高纬稀疏特征，因为这样很容易导致训练出来的树的数量和深度都比较大从而导致过拟合。因此一般输入GBDT 的特征都是连续特征。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;在 CTR 预估中，会存在大量的 id 特征，对于这种离散特征，一般有两种做法  &lt;br /&gt;1)   &lt;strong&gt;离散特征不直接输入到 GBDT 中进行编码&lt;/strong&gt;，而是做 one-hot 编码后直接输入到 LR 中即可；对于连续特征，先通过 GBDT 进行离散化和特征组合输出 one-hot 编码的特征，最后结合这两种 one-hot 特征直接输入到 LR。大致框架如下所示&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="Real GBDT" src="http://static.zybuluo.com/WuLiangchao/u51y56z4kpc6bl1o1p9svd5w/image_1cdk1jok51p5u13mgv2d1p6d1hll4c.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;2)   &lt;strong&gt;将离散的特征也输入 GBDT 进行编码&lt;/strong&gt;，但是只保留那些出现频率高的离散特征，这样输入 GBDT 中的 one-hot 特征的维度会遍地，同时通过 GBDT 也对原始的 one-hot 特征进行了组合和交叉。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#19968;&amp;#20123;&amp;#38382;&amp;#39064;-1" title="&amp;#19968;&amp;#20123;&amp;#38382;&amp;#39064;"&gt;&lt;/a&gt;  &lt;strong&gt;一些问题&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;  &lt;strong&gt;1. GBDT 中的 gradient 在哪里体现了？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;推导到现在，好像也没有提及到 gradient，其实前面  &lt;strong&gt;拟合残差时已经用到了 gradient 的信息&lt;/strong&gt;。&lt;/p&gt;
 &lt;p&gt;首先，我们要转换一下思维，我们一般在优化中使用的 gradient descent 都是对某个参数进行的，或者说是在参数空间中进行的，但是除了参数空间，还可以在函数空间中进行。如下图所示对比了两种方式(下面两张图均摘自  &lt;a href="http://wepon.me/files/gbdt.pdf" rel="external" target="_blank"&gt;GBDT算法原理与系统设计简介&lt;/a&gt;)&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="gredient descent v.s. gradient boosting" src="http://static.zybuluo.com/WuLiangchao/gcr30cnxcx1bs353a8uv0f3d/image_1cdjtahckkop2e81umc1l651lgi32.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;在函数空间中，是对函数直接进行求导的，因此 GBDT 算法的流程如下&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="GBDT" src="http://static.zybuluo.com/WuLiangchao/z54fgvreznnto9vfbfw9vcn4/image_1cdkhqh8gpjrb9n1mi11o6b16tj56.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;上图中的 $\tilde{y_i}$ 就是我们前面说的第 $i$ 个样本的残差，当损失函数为平方损失即 $$L(y,F(x)) = \frac{1}{2}(y-F(x))^2$$&lt;/p&gt;
 &lt;p&gt;对 $F(x)$ 求导得出的残差为&lt;/p&gt;
 &lt;p&gt;$$\tilde{y_i} = y_i - F(x_i)$$&lt;/p&gt;
 &lt;p&gt;这正是我们前面说的样本的真实值与前面建的树的输出和的差。  &lt;strong&gt;如果损失函数改变，这个残差值也会进行相应的改变。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2. GBDT 怎么处理分类问题？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;上面我们讲的 GBDT 是处理回归问题的，但是对于 CTR 预估这一类问题，从大分类上其实还是一个分类问题。那 GBDT 是怎么处理这个问题？&lt;/p&gt;
 &lt;p&gt;在回归问题中，GBDT每一轮迭代都构建了一棵树，实质是构建了一个函数 $f$，当输入为x时，树的输出为 $f(x)$。&lt;/p&gt;
 &lt;p&gt;在多分类问题中，假设有 $k$ 个类别，那么每一轮迭代实质是构建了 $k$ 棵树，对某个样本 $x$ 的预测值为 $f  &lt;em&gt;{1}(x), f&lt;/em&gt;{2}(x), …, f_{k}(x)$,&lt;/p&gt;
 &lt;p&gt;在这里我们仿照多分类的逻辑回归，使用 softmax来产生概率，则属于某个类别 $j$ 的概率为&lt;/p&gt;
 &lt;p&gt;$$p  &lt;em&gt;{c} = \frac{\exp(f&lt;/em&gt;{j}(x))}{ \sum  &lt;em&gt;{i=1}^{k}{exp(f&lt;/em&gt;{k}(x))}}$$&lt;/p&gt;
 &lt;p&gt;通过上面的概率值，可以分别计算出样本在各个分类下的 log loss，根据上面 GBDT 在函数空间的求导，对 $f_1$ 到 $f_k$ 都可以算出一个梯度，也就是当前轮的残差，供下一轮迭代学习。也就是每一轮的迭代会同时产生 k 棵树。&lt;/p&gt;
 &lt;p&gt;最终做预测时，输入的 $x$ 会得到 $k$ 个输出值，然后通过softmax获得其属于各类别的概率即可。&lt;/p&gt;
 &lt;p&gt;更详细的推导可参考这篇文章：  &lt;a href="https://zhuanlan.zhihu.com/p/25257856" rel="external" target="_blank"&gt;当我们在谈论GBDT：Gradient Boosting 用于分类与回归&lt;/a&gt;&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;-2" title="&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;"&gt;&lt;/a&gt;  &lt;strong&gt;开源实现&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;直接实现 GBDT + LR 的开源方案不多，但是由于两者的耦合关系并不强，因此可以先训练 GBDT，然后将原始特征通过 GBDT 转换后送入到 LR 中，GBDT 有多个高效的实现，如   &lt;a href="https://github.com/dmlc/xgboost" rel="external" target="_blank"&gt;xgboost&lt;/a&gt;，  &lt;a href="https://github.com/Microsoft/LightGBM" rel="external" target="_blank"&gt;LightGBM&lt;/a&gt;。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#FM-Factorization-Machine" title="FM(Factorization Machine)"&gt;&lt;/a&gt;FM(Factorization Machine)&lt;/h2&gt; &lt;p&gt;FM（Factorization Machine）是于2010年在论文   &lt;a href="http://www.algo.uni-konstanz.de/members/rendle/pdf/Rendle2010FM.pdf" rel="external" target="_blank"&gt;Factorization Machines&lt;/a&gt; 中提出，旨在解决稀疏数据下的特征组合问题。其思想是对组合特征的参数所构成的参数矩阵进行矩阵分解，从而得到每个原始特征的隐向量表示，更新特征的隐向量对数据的稀疏性具有鲁棒性。关于 FM 和 FFM ，美团点评这篇文章：  &lt;a href="https://tech.meituan.com/deep-understanding-of-ffm-principles-and-practices.html" rel="external" target="_blank"&gt;深入FFM原理与实践&lt;/a&gt; 其实已经写得很详细了，本文主要参考该文章进行修改。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#21407;&amp;#29702;-3" title="&amp;#21407;&amp;#29702;"&gt;&lt;/a&gt;  &lt;strong&gt;原理&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;FM 可以认为是在 LR 的基础上加入特征的二阶组合，即最多有两个特征相乘，则模型可表示成如下形式&lt;/p&gt;
 &lt;p&gt;$$y(\mathbf{x}) = w  &lt;em&gt;0+ \sum&lt;/em&gt;{i=1}^n w_i x  &lt;em&gt;i + \sum&lt;/em&gt;{i=1}^n \sum  &lt;em&gt;{j=i+1}^n w&lt;/em&gt;{ij} x_i x_j $$&lt;/p&gt;
 &lt;p&gt;从模型也可以看出，其实 FM 是在 LR 基础上增加了最后的二阶交叉项。&lt;/p&gt;
 &lt;p&gt;从上面的公式可以看出，组合特征的参数一共有 $\frac{n(n−1)}{2}$ 个，任意两个参数都是独立的。然而，在数据稀疏性普遍存在的实际应用场景中，  &lt;strong&gt;二次项参数的训练是很困难的,原因是每个参数 $w_{ij}$ 的训练需要大量 $x_i$ 和 $x_j$ 都非零的样本&lt;/strong&gt;；由于样本数据本来就比较稀疏，满足 $x_i$ 和 $x  &lt;em&gt;j$ 都非零的样本将会非常少。训练样本的不足，则会导致参数 $w&lt;/em&gt;{ij}$ 不准确，最终将严重影响模型的性能。&lt;/p&gt;
 &lt;p&gt;如何解决这个问题？FM 中借鉴了矩阵分解的思想，在推荐系统中，会对 user-item 矩阵进行矩阵分解，从而每个 user 和每个 item 都会得到一个隐向量。如下图所示&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="matrix" src="http://static.zybuluo.com/WuLiangchao/hysz4i4hthr06bd1zjhi27t6/image_1cdkb5qkk2ie1njv17ekj5s131k4p.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;类似地，所有二次项参数 $w  &lt;em&gt;{ij}$ 可以组成一个对称阵 $W$，那么这个矩阵就可以分解为 $W=V^TV$，$V$ 的第 $j$ 列便是第 $j$ 维特征的隐向量。换句话说，每个参数可表示成两个隐向量的内积的形式。即 $w&lt;/em&gt;{ij}=$，$v_i$ 表示第 $i$ 维特征的隐向量，这就是FM模型的核心思想。因此，可以将上面的方程改写成如下形式&lt;/p&gt;
 &lt;p&gt;$$y(\mathbf{x}) = w  &lt;em&gt;0+ \sum&lt;/em&gt;{i=1}^n w_i x  &lt;em&gt;i + \sum&lt;/em&gt;{i=1}^n \sum_{j=i+1}^n &lt;/p&gt;
 &lt;p&gt;x_i x_j $$&lt;/p&gt;
 &lt;p&gt;假设隐向量的长度为 $k(k&amp;lt;&amp;lt;n)$，二次项的参数数量减少为 $kn$个，远少于多项式模型的参数数量。另外，参数因子化使得 $x_hx_i$ 的参数和 $x_ix_j$ 的参数不再是相互独立的，因此我们可以在样本稀疏的情况下相对合理地估计FM的二次项参数。&lt;/p&gt;
 &lt;p&gt;具体来说，$x_hx_i$ 和 $x_ix_j$ 的系数分别为 $$ 和 $$，它们之间有共同项 $v_i$。也就是说，  &lt;strong&gt;所有包含 $x_i$ 的非零组合特征（即存在某个$j≠i$，使得 $x_ix_j≠0$）的样本都可以用来学习隐向量 $v_i$，这很大程度上避免了数据稀疏性造成的影响&lt;/strong&gt;。而在多项式模型中，$w  &lt;em&gt;{hi}$ 和 $w&lt;/em&gt;{ij}$ 是相互独立的。&lt;/p&gt;
 &lt;p&gt;另外，原始论文还对特征交叉项计算的时间复杂度做了优化，具体见如下公式&lt;/p&gt;
 &lt;p&gt;$$\sum  &lt;em&gt;{i=1}^n \sum&lt;/em&gt;{j=i+1}^n \langle \mathbf{v}_i, \mathbf{v}_j \rangle x_i x  &lt;em&gt;j = \frac{1}{2} \sum&lt;/em&gt;{f=1}^k \left(\left( \sum  &lt;em&gt;{i=1}^n v&lt;/em&gt;{i, f} x  &lt;em&gt;i \right)^2 - \sum&lt;/em&gt;{i=1}^n v_{i, f}^2 x_i^2 \right)$$&lt;/p&gt;
 &lt;p&gt;从公式可知，原来的计算复杂度为 $O(kn^2)$，而改进后的时间复杂度为 $O(kn)$&lt;/p&gt;
 &lt;p&gt;在 CTR 预估中，对 FM 的输出进行 sigmoid 变换后与 Logistics Regression 是一致的，因此损失函数的求解方法以及优化算法都基本一致，这里不再详细展开。&lt;/p&gt;
 &lt;p&gt;由于 FM 可以看做是 LR 基础上加上二阶特征组合的模型，同时模型本身对稀疏性有较好的鲁棒性，因此 FM 适用范围跟LR一样，都  &lt;strong&gt;适用于输入的特征是高纬度稀疏特征&lt;/strong&gt;。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;-3" title="&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;"&gt;&lt;/a&gt;  &lt;strong&gt;开源实现&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;FM 在 github 上有单机版本的开源实现   &lt;a href="http://ibayer.github.io/fastFM/" rel="external" target="_blank"&gt;fastFM&lt;/a&gt;和  &lt;a href="https://github.com/coreylynch/pyFM" rel="external" target="_blank"&gt;pyFM&lt;/a&gt;， fastFM 是一个学术项目，发表了相关论文   &lt;a href="https://arxiv.org/abs/1505.00641" rel="external" target="_blank"&gt;fastFM: A Library for Factorization Machines&lt;/a&gt;, 对 FM 进行了拓展；同时我们前面提到的腾讯的PS Angel 中也实现了这个算法，可参考  &lt;a href="https://github.com/Tencent/angel/blob/master/docs/algo/fm_on_angel.md" rel="external" target="_blank"&gt;这里&lt;/a&gt;。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#FFM-Field-aware-Factorization-Machine" title="FFM(Field-aware Factorization Machine)"&gt;&lt;/a&gt;FFM(Field-aware Factorization Machine)&lt;/h2&gt; &lt;p&gt;FFM 发表于论文   &lt;a href="https://www.csie.ntu.edu.tw/~cjlin//papers/ffm.pdf" rel="external" target="_blank"&gt;Field-aware Factorization Machines for CTR Prediction&lt;/a&gt;， 是台大的学生在参加 2012 KDD Cup 时提出的，这个论文借鉴了论文   &lt;a href="https://kaggle2.blob.core.windows.net/competitions/kddcup2012/2748/media/Opera.pdf" rel="external" target="_blank"&gt;Ensemble of Collaborative Filtering and Feature Engineered Models for Click Through Rate Prediction&lt;/a&gt; 中的 field 的 概念，从而提出了 FM 的升级版模型 FFM。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#21407;&amp;#29702;-4" title="&amp;#21407;&amp;#29702;"&gt;&lt;/a&gt;  &lt;strong&gt;原理&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;  &lt;strong&gt;通过引入field的概念，FFM把相同性质的特征归于同一个field。简单来说，同一个categorical特征经过One-Hot编码生成的数值特征都可以放到同一个field&lt;/strong&gt;，包括用户性别、职业、品类偏好等。&lt;/p&gt;
 &lt;p&gt;在FFM中，每一维特征 $x_i$，针对其它特征的每一种 field $f  &lt;em&gt;j$，都会学习一个隐向量 $v&lt;/em&gt;{i,f_j}$。因此，  &lt;strong&gt;隐向量不仅与特征相关，也与field相关。也就是说，假设有 $f$ 个 field，那么每个特征就有 $f$ 个隐向量，与不同的 field 的特征组合时使用不同的隐向量&lt;/strong&gt;，而原来的 FM 中每个特征只有一个隐向量。&lt;/p&gt;
 &lt;p&gt;实际上，FM 可以看作 FFM 的特例，是把所有特征都归属到一个 field 时的FFM模型。根据FFM的field敏感特性，同样可以导出其模型方程如下&lt;/p&gt;
 &lt;p&gt;$$y(\mathbf{x}) = w  &lt;em&gt;0 + \sum&lt;/em&gt;{i=1}^n w_i x  &lt;em&gt;i + \sum&lt;/em&gt;{i=1}^n \sum  &lt;em&gt;{j=i+1}^n \langle \mathbf{v}&lt;/em&gt;{i, f  &lt;em&gt;j}, \mathbf{v}&lt;/em&gt;{j, f_i} \rangle x_i x_j $$&lt;/p&gt;
 &lt;p&gt;其中，$f_j$ 是第 $j$ 个特征所属的 field。如果隐向量的长度为 $k$，那么FFM的二次参数有 $nfk$ 个，远多于FM模型的 $nk$ 个。此外，由于隐向量与 field 相关，FFM二次项并不能够化简，其预测复杂度是 $O(kn^2)$。&lt;/p&gt;
 &lt;p&gt;其实，FFM 是在 FM 的基础上进行了更细致的分类，增加了参数的个数使得模型更复杂，能够拟合更复杂的数据分布。但是损失函数的推导以及优化的算法跟前面的 FM 还有 LR 都是一样的，因此这里不再赘述。&lt;/p&gt;
 &lt;p&gt;FFM 适用的场景跟 FM 和 LR 一样，  &lt;strong&gt;适用于输入的特征是高维稀疏特征&lt;/strong&gt;。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;-4" title="&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;"&gt;&lt;/a&gt;  &lt;strong&gt;开源实现&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;FFM 最早的开源实现是台大提供的   &lt;a href="https://github.com/guestwalk/libffm" rel="external" target="_blank"&gt;libffm&lt;/a&gt;，去年开源的   &lt;a href="https://github.com/aksnzhy/xlearn" rel="external" target="_blank"&gt;xlearn&lt;/a&gt; 中也提供了该算法的实现，提供的 api 比 libffm 更加友好。&lt;/p&gt;
 &lt;p&gt;另外，由于 FM/FFM  可以看做是 LR 加了特征交叉的增强版本，对输入的特征的特点要求一致，因此上面的 GBDT+LR 也可以直接套到 GBDT+FM/FFM 上，值得一提的是，还是台大的学生，在 2014 由 Criteo 举办的比赛上，通过 GBDT+FFM 的方案夺冠，其实现细节可参考   &lt;a href="https://github.com/guestwalk/kaggle-2014-criteo" rel="external" target="_blank"&gt;kaggle-2014-criteo&lt;/a&gt;。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#&amp;#23567;&amp;#32467;" title="&amp;#23567;&amp;#32467;"&gt;&lt;/a&gt;小结&lt;/h2&gt; &lt;p&gt;在非深度学习中，可以看到主流的几个模型基本都是基于 LR 进行的拓展或将 LR 与其他模型结合。原因是 LR 模型简单，具有良好的理论基础，可解释性强，能够获取各个特征的重要性，且能够直接输出概率值。但是应用 LR 过程中无法避免且最为重要的一点就是人工特征工程，特征决定了上限，虽然 FM/FMM 和 GBDT+LR 在一定程度上起到了自动特征工程的作用，但是需要人工特征还是占主要部分。&lt;/p&gt;
 &lt;p&gt;后面要讲的深度学习的方法在一定程度上能够缓解这个问题，因为深度学习能够通过模型自动学习出有效特征，因此，深度学习也被归类为表示学习( Representation Learning)的一种;但是，没有免费午餐的，特征工程的便利性带来的是特征的不可解释性，所以怎么选取还是要根据具体的需求和业务场景。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>机器学习 机器学习 计算广告</category>
      <guid isPermaLink="true">https://itindex.net/detail/58525-ctr-%E6%A8%A1%E5%9E%8B-%E7%AE%80%E4%BB%8B</guid>
      <pubDate>Sun, 15 Jul 2018 21:53:19 CST</pubDate>
    </item>
    <item>
      <title>CTR 预估模型简介--深度学习篇</title>
      <link>https://itindex.net/detail/58521-ctr-%E6%A8%A1%E5%9E%8B-%E7%AE%80%E4%BB%8B</link>
      <description>&lt;p&gt;本文主要介绍 CTR 预估中一些深度学习模型，包括 FNN、Wide&amp;amp;Deep、PNN、DIN、 Deep&amp;amp;Cross等。每个模型会简单介绍其原理、论文出处以及其一些开源实现。&lt;/p&gt;
 &lt;a&gt;&lt;/a&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#FNN-Factorization-machine-supported-Neural-Network" title="FNN(Factorization-machine supported Neural Network)"&gt;&lt;/a&gt;FNN(Factorization-machine supported Neural Network)&lt;/h2&gt; &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#27169;&amp;#22411;&amp;#32467;&amp;#26500;" title="&amp;#27169;&amp;#22411;&amp;#32467;&amp;#26500;"&gt;&lt;/a&gt;  &lt;strong&gt;模型结构&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;FNN 是伦敦大学于 2016 在一篇论文中发表的，模型的结构如下&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="FNN" src="http://static.zybuluo.com/WuLiangchao/iqi82qjxr0zlj7ptxuvrcxtf/image_1chv050jnp9ada415od111t1ae39.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;FNN 假设输入数据的格式是离散的类别特征(表示为 one-hot 编码)，且每个特征属于一个 field，通过 embedding 层将高纬稀疏特征映射成低维稠密特征后，再作为多层感知机(MLP)的输入。&lt;/p&gt;
 &lt;p&gt;一般来说，embedding 层的参数可以随机初始化，但是在 FNN 中，初始化 embedding 是采用通过 FM 预训练得到的每个特征的隐向量，  &lt;strong&gt;这样初始化的好处是将预训练的向量作为初始化参数时，能够让模型的参数在初始化的时候就处于较优的位置(训练的目的其实就是为了得到最优的模型参数)，能够加快收敛的过程&lt;/strong&gt;，至于效果方面，则不一定会优于随机初始化的情况，因为随机初始化经过多轮的迭代也可能会收敛同样的效果。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#30456;&amp;#20851;&amp;#35770;&amp;#25991;" title="&amp;#30456;&amp;#20851;&amp;#35770;&amp;#25991;"&gt;&lt;/a&gt;  &lt;strong&gt;相关论文&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;提出 FNN 的论文   &lt;a href="https://arxiv.org/abs/1601.02376" rel="external" target="_blank"&gt;Deep Learning over Multi-field Categorical Data: A Case Study on User Response Prediction&lt;/a&gt;是  &lt;a href="http://wnzhang.net/" rel="external" target="_blank"&gt;张伟楠&lt;/a&gt;博士在伦敦大学时发表的，张伟楠博士还有很多与 RTB 相关的论文，具体可参看其主页。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;" title="&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;"&gt;&lt;/a&gt;  &lt;strong&gt;开源实现&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;论文作者在 github 上的   &lt;a href="https://github.com/wnzhang/deep-ctr" rel="external" target="_blank"&gt;deep-ctr&lt;/a&gt; 这个仓库中提供了 FNN 的代码，但是是 Theano 实现的；后来作者又将代码更新为 Tensorflow 框架实现的，详见   &lt;a href="https://github.com/Atomu2014/product-nets" rel="external" target="_blank"&gt;product-nets&lt;/a&gt;，这个仓库也包含了后面要介绍的 PNN 的实现代码。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#Wide-amp-Deep" title="Wide&amp;Deep"&gt;&lt;/a&gt;Wide&amp;amp;Deep&lt;/h2&gt; &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#27169;&amp;#22411;&amp;#32467;&amp;#26500;-1" title="&amp;#27169;&amp;#22411;&amp;#32467;&amp;#26500;"&gt;&lt;/a&gt;  &lt;strong&gt;模型结构&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;Wide &amp;amp; Deep 是 Google 在2016年6月中发布的。模型结合了传统的特征工程与深度模型：既有 Wide 的 LR 模型，也有 Deep 的 NN 模型。&lt;/p&gt;
 &lt;p&gt;其结构如下所示&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="model structure" src="http://static.zybuluo.com/WuLiangchao/upxjqo293b17g49fsfhu2g9z/image_1ce0lvnsf1pdvp3r3gfg0n1ltv33.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;wide 部分其实就是 LR，deep部分其实就是 FNN，只是 deep 部分中的 embedding 层不用 FM 训练得到的隐向量初始化。根据论文的描述，wide 部分主要负责memorization， deep 部分主要负责 generalization；memorization 主要指的是记住出现过的样本，可以理解为拟合训练数据的能力，generalization 则是泛化能力。&lt;/p&gt;
 &lt;p&gt;根据论文的实验，wide &amp;amp; deep 比起单纯的 wide 或 deep 都要好，但是根据我后面的实验以及网上的一些文章，wide 部分仍然需要人工设计特征，在特征设计不够好的情况下，wide&amp;amp;deep 整个模型的效果并不如单个的 deep 模型。&lt;/p&gt;
 &lt;p&gt;Wide&amp;amp;Deep 中还允许输入连续的特征，这点与 FNN 不同，连续特征可以直接作为 Wide 部分或 Deep 部分的输入而无需 embedding 的映射，具体如下图所示。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="input feature" src="http://static.zybuluo.com/WuLiangchao/zset6o1l78rfd37edrjwopc3/image_1ce0lpmmdcus1aa7upklnatjg9.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#30456;&amp;#20851;&amp;#35770;&amp;#25991;-1" title="&amp;#30456;&amp;#20851;&amp;#35770;&amp;#25991;"&gt;&lt;/a&gt;  &lt;strong&gt;相关论文&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;Wide&amp;amp;Deep 是 Google 在论文   &lt;a href="https://arxiv.org/abs/1606.07792" rel="external" target="_blank"&gt;Wide &amp;amp; Deep Learning for Recommender Systems&lt;/a&gt; 中提出的，论文原来是用于 Google Play 的推荐中，但是推荐和CTR实际上是同一类的问题：排序问题，所以也可以迁移到CTR预估的领域。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;-1" title="&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;"&gt;&lt;/a&gt;  &lt;strong&gt;开源实现&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;由于 Wide&amp;amp;Deep 是 google 提出的，因此在自家的框架 Tensorflow 中提供了 Wide&amp;amp;Deep API，具体的使用方法可参考官方的文档   &lt;a href="https://www.tensorflow.org/tutorials/wide_and_deep" rel="external" target="_blank"&gt;TensorFlow Wide &amp;amp; Deep Learning Tutorial&lt;/a&gt;。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#PNN-Product-based-Neural-Networks" title="PNN(Product-based Neural Networks)"&gt;&lt;/a&gt;PNN(Product-based Neural Networks)&lt;/h2&gt; &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#27169;&amp;#22411;&amp;#32467;&amp;#26500;-2" title="&amp;#27169;&amp;#22411;&amp;#32467;&amp;#26500;"&gt;&lt;/a&gt;  &lt;strong&gt;模型结构&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;PNN 是上海交大在2016年发表的，FNN 是在 PNN 的基础上进行了改进，就是增加了特征的二阶交叉项。因此，FNN 和 PNN 的关系，类似于 LR 和 FM 的关系，只是 FNN 和 PNN 均是对原始特征进行了 embedding 映射。PNN 模型的结构如下所示&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="PNN" src="http://static.zybuluo.com/WuLiangchao/m90vmx19zwuk7owy7ozs43hb/image_1chv5blf02te19u2qo0a1721i19.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;特征经过 embedding 层映射后，有两种乘积的操作，第一种是跟1做外积，实际上就是将映射后的特征进行拼接, 得到了上图中的 z 向量部分；第二种是与其他特征分别两两进行内积，得到了上图中的 p 向量部分，这个操作其实就相当于进行了特征交叉，只是这种交叉是在 embedding 映射后。再后面的结构其实又是一个多层感知机了。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#30456;&amp;#20851;&amp;#35770;&amp;#25991;-2" title="&amp;#30456;&amp;#20851;&amp;#35770;&amp;#25991;"&gt;&lt;/a&gt;  &lt;strong&gt;相关论文&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;PNN 是在上海交大于2016年在这篇论文   &lt;a href="https://arxiv.org/abs/1611.00144" rel="external" target="_blank"&gt;Product-based Neural Networks for User Response Prediction&lt;/a&gt; 中提出。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;-2" title="&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;"&gt;&lt;/a&gt;  &lt;strong&gt;开源实现&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;PNN 的作者在 github 上的   &lt;a href="https://github.com/Atomu2014/product-nets" rel="external" target="_blank"&gt;product-nets&lt;/a&gt; 上开源了其代码，通过  Tensorflow 实现，代码里面也包含了 FNN，DeepFM 等一些其他模型的实现。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#DeepFM" title="DeepFM"&gt;&lt;/a&gt;DeepFM&lt;/h2&gt; &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#27169;&amp;#22411;&amp;#32467;&amp;#26500;-3" title="&amp;#27169;&amp;#22411;&amp;#32467;&amp;#26500;"&gt;&lt;/a&gt;  &lt;strong&gt;模型结构&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;DeepFM 是华为诺亚方舟实验室在 2017 提出的用于 CTR 预估的模型，DeepFM 其实就是模仿 Wide&amp;amp;Deep，只是将 Wide 部分替换成了 FM，所以创新性并不算大。其结构如下所示，&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="DeepFM" src="http://static.zybuluo.com/WuLiangchao/zlq1735izhys7x8pgdqarotw/image_1ceiplk5lcfkbn9cia1p5j12pc2m.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#30456;&amp;#20851;&amp;#35770;&amp;#25991;-3" title="&amp;#30456;&amp;#20851;&amp;#35770;&amp;#25991;"&gt;&lt;/a&gt;  &lt;strong&gt;相关论文&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;DeepFM 是在这篇论文中提出的   &lt;a href="https://arxiv.org/abs/1703.04247" rel="external" target="_blank"&gt;DeepFM: A Factorization-Machine based Neural Network for CTR Prediction&lt;/a&gt; &lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;-3" title="&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;"&gt;&lt;/a&gt;  &lt;strong&gt;开源实现&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;作者没有公开源码，上面提到的   &lt;a href="https://github.com/Atomu2014/product-nets" rel="external" target="_blank"&gt;product-nets&lt;/a&gt; 提供了这个模型的实现代码，同时   &lt;a href="https://github.com/ChenglongChen/tensorflow-DeepFM" rel="external" target="_blank"&gt;tensorflow-DeepFM&lt;/a&gt; 也提供了一个 tensorflow 实现的版本，star 数是 github 上较高的了。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#DIN-Deep-Interest-Network" title="DIN(Deep Interest Network)"&gt;&lt;/a&gt;DIN(Deep Interest Network)&lt;/h2&gt; &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#27169;&amp;#22411;&amp;#32467;&amp;#26500;-4" title="&amp;#27169;&amp;#22411;&amp;#32467;&amp;#26500;"&gt;&lt;/a&gt;  &lt;strong&gt;模型结构&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;从之前提到的几个模型可知，CTR预估中的深度学习模型的基本思路是将原始的高维稀疏特征映射到一个低维空间中，也即对原始特征做了embedding操作，之后一起通过一个全连接网络学习到特征间的交互信息和最终与CTR之间的非线性关系。这里值得注意的一点是，在对用户历史行为数据进行处理时，每个用户的历史点击个数是不相等的，我们需要把它们编码成一个固定长的向量。以往的做法是，对每次历史点击做相同的embedding操作之后，将它们做一个求和或者求最大值的操作，类似经过了一个pooling层操作。提出 DIN 的论文认为这个操作损失了大量的信息，于是引入了 attention 机制(其实就是一种加权求和)。&lt;/p&gt;
 &lt;p&gt;DIN 是阿里妈妈在 2017 年提出的，其模型的结构如下所示&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="Deep Interest Network" src="http://static.zybuluo.com/WuLiangchao/maci4t6ldxomke215fgql2my/image_1ci11abueoek1so1rhpeo2a3639.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Activation Unit 的结构如下所示&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="Activation Unit" src="http://static.zybuluo.com/WuLiangchao/zua5wju98s55ychlt3pvde4k/image_1ci11cdgs9ol13jn1iv113icqpc3m.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;DIN模型在对用户的表示计算上引入了attention network (也即图中的Activation Unit) 。DIN把用户特征、用户历史行为特征进行embedding操作，视为对用户兴趣的表示，之后通过attention network，对每个兴趣表示赋予不同的权值。  &lt;strong&gt;这个权值是由用户的兴趣和待估算的广告进行匹配计算得到的&lt;/strong&gt;，如此模型结构符合了之前的两个观察——用户兴趣的多样性以及部分对应。attention network 的计算公式如下， $V_u$ 代表用户表示向量， $V_i$ 代表用户兴趣表示向量， $V_a$ 代表广告表示向量，$w_i$ 表示各个用户兴趣表示向量的权重，$g$ 是 Activation Unit 的逻辑，论文中提出了一种如上图的 Activation Unit 所示，当然也可自行设计新的 Activation 方法。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="DIN" src="http://static.zybuluo.com/WuLiangchao/msyqg4lv9ug5v4qd3cxk2q4l/image_1cfp1c0l61j781euse3uul14mh13.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#30456;&amp;#20851;&amp;#35770;&amp;#25991;-4" title="&amp;#30456;&amp;#20851;&amp;#35770;&amp;#25991;"&gt;&lt;/a&gt;  &lt;strong&gt;相关论文&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;DIN 是在论文   &lt;a href="https://arxiv.org/abs/1706.06978" rel="external" target="_blank"&gt;Deep Interest Network for Click-Through Rate Prediction&lt;/a&gt; 中提出的。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;-4" title="&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;"&gt;&lt;/a&gt;  &lt;strong&gt;开源实现&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;论文作者在 github 上的仓库   &lt;a href="https://github.com/zhougr1993/DeepInterestNetwork" rel="external" target="_blank"&gt;DeepInterestNetwork&lt;/a&gt; 开源了其代码，通过 Tensorflow 实现。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#Deep-amp-Cross" title="Deep&amp;Cross"&gt;&lt;/a&gt;Deep&amp;amp;Cross&lt;/h2&gt; &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#27169;&amp;#22411;&amp;#32467;&amp;#26500;-5" title="&amp;#27169;&amp;#22411;&amp;#32467;&amp;#26500;"&gt;&lt;/a&gt;  &lt;strong&gt;模型结构&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;PNN 进行了特征的二阶交叉，目前是为了获得信息量更多的特征，除了二阶，三阶四阶甚至更高阶的特征会更加有区分度；Deep&amp;amp;Cross 就是一个能够进行任意高阶交叉的神经网络。&lt;/p&gt;
 &lt;p&gt;Deep&amp;amp;Cross 是 StandFord 和 Google 与 2017年 提出的，类似于 Wide&amp;amp;Deep，模型也是由两部分组成，分别是 Deep network 和 Cross network，该模型结构如下所示&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="Deep &amp; Cross" src="http://static.zybuluo.com/WuLiangchao/palaii8pgw2qn7qznc198kez/image_1cdm0f6me1dn61p6l2i511kqerd9.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;$x_i$ 表示可由如下公式确定&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="x0" src="http://static.zybuluo.com/WuLiangchao/ig3tah8jz7vsljvp7kicr8s0/image_1ci130kk62821o3t1r4t7d6ojc43.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="xl" src="http://static.zybuluo.com/WuLiangchao/ktbv7tz2k38q3ktlbomyo61a/image_1ci1318p11e2v15q8agn12c1po24g.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;从上面两条公式可知，Cross network 中的第 $l+1$ 层的神经元由最原始的输入和第 $l$ 层的神经元共同决定，因此第 $l$ 层相当于对原始特征进行了 $l$ 阶交叉。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#30456;&amp;#20851;&amp;#35770;&amp;#25991;-5" title="&amp;#30456;&amp;#20851;&amp;#35770;&amp;#25991;"&gt;&lt;/a&gt;相关论文&lt;/h3&gt; &lt;p&gt;Deep&amp;amp;Cross 是在这篇论文   &lt;a href="https://arxiv.org/abs/1708.05123" rel="external" target="_blank"&gt;Deep &amp;amp; Cross Network for Ad Click Predictions&lt;/a&gt; 中提出的。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;-5" title="&amp;#24320;&amp;#28304;&amp;#23454;&amp;#29616;"&gt;&lt;/a&gt;开源实现&lt;/h3&gt; &lt;p&gt;论文没有公开代码，  &lt;a href="https://github.com/shenweichen/DeepCTR" rel="external" target="_blank"&gt;DeepCTR&lt;/a&gt; 中提供了 Deep&amp;amp;Cross 的 tensorflow 实现，可供参考。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#&amp;#24635;&amp;#32467;" title="&amp;#24635;&amp;#32467;"&gt;&lt;/a&gt;总结&lt;/h2&gt; &lt;p&gt;在CTR预估中，模型适用传统方法还是深度学习方法，其实是一个  &lt;strong&gt;海量离散特征+简单模型&lt;/strong&gt; 和   &lt;strong&gt;少量连续特征+复杂模型&lt;/strong&gt; 的权衡。既可以离散化用线性模型，也可以用连续特征加深度学习。特征与模型往往是对偶的，前者容易，而且可以n个人一起并行做，有成功经验；后者目前看很赞，能走多远还须拭目以待。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>机器学习 机器学习 计算广告</category>
      <guid isPermaLink="true">https://itindex.net/detail/58521-ctr-%E6%A8%A1%E5%9E%8B-%E7%AE%80%E4%BB%8B</guid>
      <pubDate>Mon, 16 Jul 2018 22:36:46 CST</pubDate>
    </item>
    <item>
      <title>大规模机器学习框架的四重境界</title>
      <link>https://itindex.net/detail/58137-%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0-%E6%A1%86%E6%9E%B6-%E5%A2%83%E7%95%8C</link>
      <description>&lt;p&gt;文章为转载，原文链接见  &lt;a href="https://zhuanlan.zhihu.com/p/29968773" rel="external" target="_blank"&gt;这里&lt;/a&gt;，作者是   &lt;a href="https://www.zhihu.com/people/carbon-zhang/activities" rel="external" target="_blank"&gt;carbon zhang&lt;/a&gt;。这篇文章主要介绍了分布式机器学习中的若干重点概念和经典论文，包括数据并行和模型并行、分布式框架的流派、参数服务器以及同步协议的演进等，非常值得一看。&lt;/p&gt;
 &lt;a&gt;&lt;/a&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#&amp;#32972;&amp;#26223;" title="&amp;#32972;&amp;#26223;"&gt;&lt;/a&gt;背景&lt;/h2&gt; &lt;p&gt;自从google发表著名的 GFS、MapReduce、BigTable 三篇paper以后，互联网正式迎来了大数据时代。大数据的显著特点是大，哪里都大的大。本篇主要针对volume大的数据时，使用机器学习来进行数据处理过程中遇到的架构方面的问题做一个系统的梳理。&lt;/p&gt;
 &lt;p&gt;有了GFS我们有能力积累海量的数据样本，比如在线广告的曝光和点击数据，天然具有正负样本的特性，累积一两个月往往就能轻松获得百亿、千亿级的训练样本。这样海量的样本如何存储？用什么样的模型可以学习海量样本中有用的pattern？这些问题不止是工程问题，也值得每个做算法的同学去深入思考。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#31616;&amp;#21333;&amp;#27169;&amp;#22411;or&amp;#22797;&amp;#26434;&amp;#27169;&amp;#22411;" title="&amp;#31616;&amp;#21333;&amp;#27169;&amp;#22411;or&amp;#22797;&amp;#26434;&amp;#27169;&amp;#22411;"&gt;&lt;/a&gt;简单模型or复杂模型&lt;/h3&gt; &lt;p&gt;在深度学习概念提出之前，算法工程师手头能用的工具其实并不多，就LR、SVM、感知机等寥寥可数、相对固定的若干个模型和算法；那时候要解决一个实际的问题，算法工程师更多的工作主要是在  &lt;strong&gt;特征工程&lt;/strong&gt;方面。而特征工程本身并没有很系统化的指导理论（至少目前没有看到系统介绍特征工程的书籍），所以很多时候特征的构造技法显得光怪陆离，是否有用也取决于问题本身、数据样本、模型以及  &lt;strong&gt;运气&lt;/strong&gt;。&lt;/p&gt;
 &lt;p&gt;在特征工程作为算法工程师主要工作内容的时候，构造新特征的尝试往往很大部分都不能在实际工作中work。据我了解，国内几家大公司在特征构造方面的成功率在后期一般不会超过20%。也就是80%的新构造特征往往并没什么正向提升效果。如果给这种方式起一个名字的话，大概是  &lt;strong&gt;简单模型+复杂特征&lt;/strong&gt;；简单模型说的是算法比如LR、SVM本身并不服务，参数和表达能力基本呈现一种线性关系，易于理解。复杂特征则是指特征工程方面不断尝试使用各种奇技淫巧构造的可能有用、可能没用的特征，这部分特征的构造方式可能会有  &lt;strong&gt;各种trick，比如窗口滑动、离散化、归一化、开方、平方、笛卡尔积、多重笛卡尔积等等&lt;/strong&gt;；顺便提一句，因为  &lt;strong&gt;特征工程本身并没有特别系统的理论和总结，所以初入行的同学想要构造特征就需要多读paper，特别是和自己业务场景一样或类似的场景的paper，从里面学习作者分析、理解数据的方法以及对应的构造特征的技法；久而久之，有望形成自己的知识体系。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;深度学习概念提出以后，人们发现通过深度神经网络可以进行一定程度的表示学习（representation learning），例如在图像领域，通过CNN提取图像feature并在此基础上进行分类的方法，一举打破了之前算法的天花板，而且是以极大的差距打破。这给所有算法工程师带来了新的思路，既然深度学习本身有提取特征的能力，干嘛还要苦哈哈的自己去做人工特征设计呢？&lt;/p&gt;
 &lt;p&gt;深度学习虽然一定程度上缓解了特征工程的压力，但这里要强调两点：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;缓解并不等于彻底解决，除了图像这种特定领域，在个性化推荐等领域，深度学习目前还没有完全取得绝对的优势；究其原因，可能还是数据自身内在结构的问题，使得在其他领域目前还没有发现类似图像+CNN这样的完美CP。&lt;/li&gt;
  &lt;li&gt;深度学习在缓解特征工程的同时，也带来了模型复杂、不可解释的问题。算法工程师在网络结构设计方面一样要花很多心思来提升效果。概括起来，深度学习代表的   &lt;strong&gt;简单特征+复杂模型&lt;/strong&gt;是解决实际问题的另一种方式。&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;两种模式孰优孰劣还难有定论，以点击率预测为例，在计算广告领域往往以海量特征+LR为主流，根据VC维理论，LR的表达能力和特征个数成正比，因此海量的feature也完全可以使LR拥有足够的描述能力。而在个性化推荐领域，深度学习刚刚萌芽，目前google play采用了WDL的结构  &lt;a href="https://dl.acm.org/citation.cfm?id=2988454" rel="external" target="_blank"&gt;[1]&lt;/a&gt;，youtube采用了双重DNN的结构  &lt;a href="https://dl.acm.org/citation.cfm?id=2959190" rel="external" target="_blank"&gt;[2]&lt;/a&gt;。&lt;/p&gt;
 &lt;p&gt;不管是那种模式，当模型足够庞大的时候，都会出现模型参数一台机器无法存放的情况。比如百亿级 feature 的LR对应的权重w有好几十个G，这在很多单机上存储都是困难的，大规模神经网络则更复杂，不仅难以单机存储，而且参数和参数之间还有逻辑上的强依赖；要对超大规模的模型进行训练势必要借用分布式系统的技法，本文主要是系统总结这方面的一些思路。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#25968;&amp;#25454;&amp;#24182;&amp;#34892;vs&amp;#27169;&amp;#22411;&amp;#24182;&amp;#34892;" title="&amp;#25968;&amp;#25454;&amp;#24182;&amp;#34892;vs&amp;#27169;&amp;#22411;&amp;#24182;&amp;#34892;"&gt;&lt;/a&gt;数据并行vs模型并行&lt;/h3&gt; &lt;p&gt;数据并行和模型并行是理解大规模机器学习框架的基础概念，其缘起未深究，第一次看到是在姐夫（Jeff Dean）的blog里，当时匆匆一瞥，以为自己懂了。多年以后，再次开始调研这个问题的时候才想起长者的教训，年轻人啊，还是图样图森破。如果你和我一样曾经忽略过这个概念，今天不放复习一下。&lt;/p&gt;
 &lt;p&gt;这两个概念在  &lt;a href="https://www.zhihu.com/question/53851014" rel="external" target="_blank"&gt;这个问题&lt;/a&gt;中沐帅曾经给出了一个非常直观而经典的解释，可惜不知道什么原因，当我想引用时却发现已经被删除了。我在这里简单介绍下这个比喻：如果要修两栋楼，有一个工程队，怎么操作？第一个方案是将人分成两组，分别盖楼，改好了就装修；第二种做法是一组人盖楼，等第一栋楼盖好，另一组装修第一栋，然后第一组继续盖第二栋楼，改完以后等装修队装修第二栋楼。咋一看，第二种方法似乎并行度并不高，但第一种方案需要每个工程人员都拥有“盖楼”和“装修”两种能力，而第二个方案只需要每个人拥有其中一种能力即可。第一个方案和数据并行类似，第二个方案则道出了模型并行的精髓。&lt;/p&gt;
 &lt;p&gt;数据并行理解起来比较简单，当样本比较多的时候，为了使用所有样本来训练模型，我们不妨把数据分布到不同的机器上，然后每台机器都来对模型参数进行迭代，如下图所示&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="data parallel" src="http://static.zybuluo.com/WuLiangchao/e5tnz97ue7ln83yv5umsfs66/image_1c8cl0d5u1emv1j8v5p9ogc1dfl9.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;图片取材于TensorFlow的paper  &lt;a href="https://arxiv.org/abs/1603.04467" rel="external" target="_blank"&gt;[3]&lt;/a&gt;，图中ABC代表三台不同的机器，上面存储着不同的样本，模型 P 在各台机器上计算对应的增量，然后在参数存储的机器上进行汇总和更新，这就是数据并行。先忽略synchronous，这是同步机制相关的概念，在第三节会有专门介绍。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;数据并行概念简单，而且不依赖于具体的模型，因此数据并行机制可以作为框架的一种基础功能，对所有算法都生效。&lt;/strong&gt;与之不同的是，模型并行因为参数间存在依赖关系（其实数据并行参数更新也可能会依赖所有的参数，但区别在于往往是依赖于上一个迭代的全量参数。而模型并行往往是同一个迭代内的参数之间有强依赖关系，比如DNN网络的不同层之间的参数依照BP算法形成的先后依赖），无法类比数据并行这样直接将模型参数分片而破坏其依赖关系，所以  &lt;strong&gt;模型并行不仅要对模型分片，同时需要调度器来控制参数间的依赖关系&lt;/strong&gt;。而每个模型的依赖关系往往并不同，所以模型并行的调度器因模型而异，较难做到完全通用。关于这个问题，CMU的Erix Xing在  &lt;a href="https://www.jianshu.com/p/00736aa21dc8" rel="external" target="_blank"&gt;这里&lt;/a&gt;有所介绍，感兴趣的可以参考。&lt;/p&gt;
 &lt;p&gt;模型并行的问题定义可以参考姐夫的  &lt;a href="http://papers.nips.cc/paper/4687-large-scale-distributed-deep-networks" rel="external" target="_blank"&gt;[4]&lt;/a&gt;，这篇paper也是tensorflow的前身相关的总结，其中图&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="model parallel" src="http://static.zybuluo.com/WuLiangchao/qjqbt2cd5ideug6dmcnj3b7r/image_1c8cm9l72otn1vu6r7f1dbt13grm.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;解释了模型并行的物理图景，当一个超大神经网络无法存储在一台机器上时，我们可以切割网络存到不同的机器上，但是为了保持不同参数分片之间的依赖，如图中粗黑线的部分，则需要在不同的机器之间进行concurrent控制；同一个机器内部的参数依赖，即途中细黑线部分在机器内即可完成控制。&lt;/p&gt;
 &lt;p&gt;黑线部分如何有效控制呢？如下图所示&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="parameter rely" src="http://static.zybuluo.com/WuLiangchao/ep522jucrnwye84jbzfdspfa/image_1c8cmb1p01bq8jrq1u8ijg61dc813.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;在将模型切分到不同机器以后，我们  &lt;strong&gt;将参数和样本一起在不同机器间流转&lt;/strong&gt;，途中 ABC 代表模型的不同部分的参数；假设C依赖B，B依赖A，机器1上得到A的一个迭代后，将A和必要的样本信息一起传到机器2，机器2根据A和样本对P2更新得到，以此类推；当机器2计算B的时候，机器1可以展开A的第二个迭代的计算。了解  &lt;strong&gt;CPU流水线&lt;/strong&gt;操作的同学一定感到熟悉，是的，模型并行是通过数据流水线来实现并行的。想想那个盖楼的第二种方案，就能理解模型并行的精髓了。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="parameter rely controller" src="http://static.zybuluo.com/WuLiangchao/mycmuqst7mc519qu8pvpkc0m/image_1c8cmdj2u20adgtc5c6b1pkt2g.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;上图则是对控制模型参数依赖的调度器的一个示意图，实际框架中一般都会用DAG（有向无环图）调度技术来实现类似功能，未深入研究，以后有机会再补充说明。&lt;/p&gt;
 &lt;p&gt;理解了数据并行和模型并行对后面参数服务器的理解至关重要，但现在让我先荡开一笔，简单介绍下并行计算框架的一些背景信息。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#&amp;#24182;&amp;#34892;&amp;#31639;&amp;#27861;&amp;#28436;&amp;#36827;" title="&amp;#24182;&amp;#34892;&amp;#31639;&amp;#27861;&amp;#28436;&amp;#36827;"&gt;&lt;/a&gt;并行算法演进&lt;/h2&gt; &lt;h3&gt;  &lt;a href="http://wulc.github.io/#MapReduce&amp;#36335;&amp;#32447;" title="MapReduce&amp;#36335;&amp;#32447;"&gt;&lt;/a&gt;MapReduce路线&lt;/h3&gt; &lt;p&gt;从函数式编程中的受到启发，google发布了MapReduce  &lt;a href="https://dl.acm.org/citation.cfm?id=1327492" rel="external" target="_blank"&gt;[5]&lt;/a&gt;的分布式计算方式；通过将任务切分成多个叠加的Map+Reduce任务，来完成复杂的计算任务，示意图如下&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="MapReduce" src="http://static.zybuluo.com/WuLiangchao/4m13ula1j3qd4qunddpj3hk7/image_1c8cmk8hj1k1518k91j4eb19pv43t.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;MapReduce的主要问题有两个，一是原语的语义过于低级，直接使用其来写复杂算法，开发量比较大；另一个问题是依赖于磁盘进行数据传递，性能跟不上业务需求。&lt;/p&gt;
 &lt;p&gt;为了解决MapReduce的两个问题，Matei在  &lt;a href="https://dl.acm.org/citation.cfm?id=2228301" rel="external" target="_blank"&gt;[6]&lt;/a&gt;中提出了一种新的数据结构RDD，并构建了Spark框架。Spark框架在MR语义之上封装了DAG调度器，极大降低了算法使用的门槛。较长时间内spark几乎可以说是大规模机器学习的代表，直至后来沐帅的参数服务器进一步开拓了大规模机器学习的领域以后，spark才暴露出一点点不足。如下图&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="spark" src="http://static.zybuluo.com/WuLiangchao/2hsvlmnch236o5zm8h5o7tvx/image_1c8cmp3c8682rsf42j1l2r9cn4a.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;从图中可以看出，  &lt;strong&gt;Spark框架以Driver为核心，任务调度和参数汇总都在driver，而driver是单机结构，所以spark的瓶颈非常明显，就在Driver这里&lt;/strong&gt;。当模型规模大到一台机器存不下的时候，Spark就无法正常运行了。所以从今天的眼光来看，Spark只能称为一个中等规模的机器学习框架。剧透一句，公司开源的   &lt;a href="https://github.com/Tencent/angel" rel="external" target="_blank"&gt;Angel&lt;/a&gt; 通过修改Driver的底层协议将Spark扩展到了一个高一层的境界。后面还会再详细介绍这部分。&lt;/p&gt;
 &lt;p&gt;MapReduce不仅是一个框架，还是一种思想，google开创性的工作为我们找到了大数据分析的一个可行方向，时至今日，仍不过时。只是逐渐从业务层下沉到底层语义应该处于的框架下层。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#MPI&amp;#25216;&amp;#26415;" title="MPI&amp;#25216;&amp;#26415;"&gt;&lt;/a&gt;MPI技术&lt;/h3&gt; &lt;p&gt;沐帅在  &lt;a href="https://www.zhihu.com/question/55119470" rel="external" target="_blank"&gt;这个问题&lt;/a&gt;中对MPI的前景做了简要介绍；和Spark不同，MPI是类似socket的一种系统通信API，只是支持了消息广播等功能。因为对MPI研究不深入，这里简单介绍下优点和缺点吧；优点是系统级支持，性能杠杠的；缺点也比较多，一是和MR一样因为原语过于低级，用MPI写算法，往往代码量比较大。另一方面是基于MPI的集群，如果某个任务失败，往往需要重启整个集群，而MPI集群的任务成功率并不高。阿里在  &lt;a href="https://dl.acm.org/citation.cfm?id=3098029" rel="external" target="_blank"&gt;[7]&lt;/a&gt;中给出了下图：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="MPI" src="http://static.zybuluo.com/WuLiangchao/tyuuutsjdr46htlt31ki4mqr/image_1c8cn0o8l6q65bh90f17io8tr4n.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;从图中可以看出，MPI作业失败的几率接近五成。MPI也并不是完全没有可取之处，正如沐帅所说，在超算集群上还是有场景的。对于工业届依赖于云计算、依赖于commodity计算机来说，则显得性价比不够高。当然如果在参数服务器的框架下，对单组worker再使用MPI未尝不是个好的尝试，[7]中的鲲鹏系统正式这么设计的。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#&amp;#21442;&amp;#25968;&amp;#26381;&amp;#21153;&amp;#22120;" title="&amp;#21442;&amp;#25968;&amp;#26381;&amp;#21153;&amp;#22120;"&gt;&lt;/a&gt;参数服务器&lt;/h2&gt; &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#21382;&amp;#21490;&amp;#28436;&amp;#36827;" title="&amp;#21382;&amp;#21490;&amp;#28436;&amp;#36827;"&gt;&lt;/a&gt;历史演进&lt;/h3&gt; &lt;p&gt;沐帅在  &lt;a href="https://www.usenix.org/system/files/conference/osdi14/osdi14-paper-li_mu.pdf" rel="external" target="_blank"&gt;[8]&lt;/a&gt;中将参数服务器的历史划分为三个阶段，第一代参数服务器萌芽于沐帅的导师Smola的   &lt;a href="http://vldb.org/pvldb/vldb2010/papers/R63.pdf" rel="external" target="_blank"&gt;[9]&lt;/a&gt; ，如下图所示：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="parallel topic model" src="http://static.zybuluo.com/WuLiangchao/txand7p15ugs4p6aq60mjv7l/image_1c8cs9n1qgj71ohp8pgv6t7l454.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;这个工作中仅仅引入memcached来存放key-value数据，不同的处理进程并行对其进行处理。  &lt;a href="http://static.usenix.org/event/osdi10/tech/full_papers/Power.pdf" rel="external" target="_blank"&gt;[10]&lt;/a&gt;中也有类似的想法，第二代参数服务器叫application-specific参数服务器，主要针对特定应用而开发，其中最典型的代表应该是tensorflow的前身  &lt;a href="https://dl.acm.org/citation.cfm?id=2959190" rel="external" target="_blank"&gt;4&lt;/a&gt;。&lt;/p&gt;
 &lt;p&gt;第三代参数服务器，也即是通用参数服务器框架是由百度少帅李沐正式提出的，和前两代不同，第三代参数服务器从设计上就是作为一个通用大规模机器学习框架来定位的。要摆脱具体应用、算法的束缚，做一个通用的大规模机器学习框架，首先就要定义好框架的功能；而所谓框架，往往就是把大量重复的、琐碎的、做了一次就不想再来第二次的脏活、累活进行良好而优雅的封装，让使用框架的人可以只关注与自己的核心逻辑。第三代参数服务器要对那些功能进行封装呢？沐帅总结了这几点，我照搬如下：&lt;/p&gt;
 &lt;p&gt;1）  &lt;strong&gt;高效的网络通信&lt;/strong&gt;：因为不管是模型还是样本都十分巨大，因此对网络通信的高效支持以及高配的网络设备都是大规模机器学习系统不可缺少的；&lt;/p&gt;
 &lt;p&gt;2）  &lt;strong&gt;灵活的一致性模型&lt;/strong&gt;：不同的一致性模型其实是在模型收敛速度和集群计算量之间做tradeoff；要理解这个概念需要对模型性能的评价做些分析，暂且留到下节再介绍。&lt;/p&gt;
 &lt;p&gt;3）  &lt;strong&gt;弹性可扩展&lt;/strong&gt;：显而易见&lt;/p&gt;
 &lt;p&gt;4）  &lt;strong&gt;容灾容错&lt;/strong&gt;：大规模集群协作进行计算任务的时候，出现Straggler或者机器故障是非常常见的事，因此系统设计本身就要考虑到应对；没有故障的时候，也可能因为对任务时效性要求的变化而随时更改集群的机器配置。这也需要框架能在不影响任务的情况下能做到机器的热插拔。&lt;/p&gt;
 &lt;p&gt;5）  &lt;strong&gt;易用性&lt;/strong&gt;：主要针对使用框架进行算法调优的工程师而言，显然，一个难用的框架是没有生命力的。&lt;/p&gt;
 &lt;p&gt;在正式介绍第三代参数服务器的主要技术之前，先从另一个角度来看下大规模机器学习框架的演进&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="&amp;#26694;&amp;#26550;&amp;#28436;&amp;#36827;" src="http://static.zybuluo.com/WuLiangchao/c8ql9789uwfimlqk9c4pv24l/image_1c8csill81sv91pbo1songrh8rf5h.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;这张图可以看出，在参数服务器出来之前，人们已经做了多方面的并行尝试，不过往往只是针对某个特定算法或特定领域，比如 YahooLDA 是针对LDA算法的。当模型参数突破十亿以后，则可以看出参数服务器一统江湖，再无敌手。&lt;/p&gt;
 &lt;p&gt;首先我们看看第三代参数服务器的基本架构&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="parameter server" src="http://static.zybuluo.com/WuLiangchao/qoegneio9dloxx8k0o375qe6/image_1c8cskcg2r9olg5shi1l956du5u.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;上图的 resource manager 可以先放一放，因为实际系统中这部分往往是复用现有的资源管理系统，比如yarn、mesos或者k8s；底下的training data毋庸置疑的需要类似GFS的分布式文件系统的支持；剩下的部分就是参数服务器的核心组件了。&lt;/p&gt;
 &lt;p&gt;图中画了一个   &lt;strong&gt;server group&lt;/strong&gt; 和三个  &lt;strong&gt;worker group&lt;/strong&gt;；实际应用中往往也是类似，server group 用一个，而worker group按需配置；server manager 是server group中的管理节点，一般不会有什么逻辑，只有当有server node加入或退出的时候，为了维持一致性哈希而做一些调整。&lt;/p&gt;
 &lt;p&gt;Worker group中的task schedule则是一个简单的任务协调器，一个具体任务运行的时候，task schedule负责通知每个worker加载自己对应的数据，然后去server node上拉取一个要更新的参数分片，用本地数据样本计算参数分片对应的变化量，然后同步给server node；server node在收到本机负责的参数分片对应的所有worker的更新后，对参数分片做一次update。&lt;/p&gt;
 &lt;p&gt;这里存在的一个问题就是不同的worker同时并行运算的时候，可能因为网络、机器配置等外界原因，导致不同的worker的进度是不一样的，如何控制worker的同步机制是一个比较重要的课题。详见下节分解。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#21516;&amp;#27493;&amp;#21327;&amp;#35758;" title="&amp;#21516;&amp;#27493;&amp;#21327;&amp;#35758;"&gt;&lt;/a&gt;同步协议&lt;/h3&gt; &lt;p&gt;本节假设读者已经对随机梯度优化算法比较熟悉，如果不熟悉的同学请参考吴恩达经典课程机器学习中对SGD的介绍，或者我之前多次推荐过的书籍《最优化导论》。&lt;/p&gt;
 &lt;p&gt;我们先看一个单机算法的运行过程，  &lt;strong&gt;假设一个模型的参数切分成三个分片k1，k2，k3；比如你可以假设是一个逻辑回归算法的权重向量被分成三段。我们将训练样本集合也切分成三个分片s1，s2，s3；在单机运行的情况下，我们假设运行的序列是（k1，s1）、（k2，s1）、（k3、s1）、（k1、s2）、（k2、s2）、（k3、s2）&lt;/strong&gt;。。。看明白了吗？就是假设先用s1中的样本一次对参数分片k1、k2、k3进行训练，然后换s2；这就是典型的单机运行的情况，而我们知道这样的运行序列最后算法会收敛。&lt;/p&gt;
 &lt;p&gt;现在我们开始并行化，假设k1、k2、k3分布在三个server node上，s1、s2、s3分布在三个worker上，这时候如果我们还要保持之前的计算顺序，则会变成怎样？work1计算的时候，work2和worker3只能等待，同样worker2计算的时候，worker1和work3都得等待，以此类推；可以看出这样的并行化并没有提升性能；但是也算简单解决了超大规模模型的存储问题。&lt;/p&gt;
 &lt;p&gt;为了解决性能的问题，业界开始探索这里的一致性模型，最先出来的版本是前面提到的  &lt;a href="http://papers.nips.cc/paper/4687-large-scale-distributed-deep-networks" rel="external" target="_blank"&gt;9&lt;/a&gt;中的  &lt;strong&gt;ASP模式，就是完全不顾worker之间的顺序，每个worker按照自己的节奏走，跑完一个迭代就update&lt;/strong&gt;，然后继续，这应该是大规模机器学习中的freestyle了，如图所示&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="ASP" src="http://static.zybuluo.com/WuLiangchao/j4t07q23ntgaii3lp439f2t5/image_1c8ct5p651d7816sh1tk0n1p1g5s6b.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;ASP的优势是最大限度利用了集群的计算能力，所有的worker所在的机器都不用等待，但缺点也显而易见，除了少数几个模型，比如LDA，  &lt;strong&gt;ASP协议可能导致模型无法收敛&lt;/strong&gt;。也就是SGD彻底跑飞了，梯度不知道飞到哪里去了。&lt;/p&gt;
 &lt;p&gt;在ASP之后提出了另一种相对极端的同步协议BSP，Spark用的就是这种方式，如图所示&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="BSP" src="http://static.zybuluo.com/WuLiangchao/hp03ua59149pfckwineg8dcl/image_1c8ct7b2p1ps16ju7kfpfii6i6o.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;每个worker都必须在同一个迭代运行，只有一个迭代任务所有的worker都完成了，才会进行一次worker和server之间的同步和分片更&lt;/strong&gt;新。这个算法和严格一致的算法非常类似，区别仅仅在于单机版本的batch size在BSP的时候变成了有所有worker的单个batch size求和得到的总的butch size替换。毫无疑问，BSP的模式和单机串行因为仅仅是batch size的区别，所以在模型收敛性上是完全一样的。同时，因为每个worker在一个周期内是可以并行计算的，所以有了一定的并行能力。&lt;/p&gt;
 &lt;p&gt;以此协议为基础的spark在很长时间内成为机器学习领域实际的霸主，不是没有理由的。此种协议的缺陷之处在于，  &lt;strong&gt;整个worker group的性能由其中最慢的worker决定，这个worker一般称为straggler。&lt;/strong&gt;读过GFS文章的同学应该都知道straggler的存在是非常普遍的现象。&lt;/p&gt;
 &lt;p&gt;能否将ASP和BSP做一下折中呢？答案当然是可以的，这就是目前我认为最好的同步协议SSP；  &lt;strong&gt;SSP的思路其实很简单，既然ASP是允许不同worker之间的迭代次数间隔任意大，而BSP则只允许为0，那我是否可以取一个常数s&lt;/strong&gt;？如图所示&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="SSP" src="http://static.zybuluo.com/WuLiangchao/takvu3w9ps9mrhtrew4sjaix/image_1c8cta3iv13eb1o6istc1ue1v9e75.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;不同的worker之间允许有迭代的间隔，但这个间隔数不允许超出一个指定的数值s，图中s=3.&lt;/p&gt;
 &lt;p&gt;SSP协议的详细介绍参见  &lt;a href="http://papers.nips.cc/paper/4894-more-effective-distributed-ml-via-as" rel="external" target="_blank"&gt;[11]&lt;/a&gt;，CMU的大拿Eric Xing在其中详细介绍了SSP的定义，以及其收敛性的保证。理论推导证明常数s不等于无穷大的情况下，算法一定可以在若干次迭代以后进入收敛状态。其实在Eric提出理论证明之前，工业界已经这么尝试过了&lt;/p&gt;
 &lt;p&gt;顺便提一句，考察分布式算法的性能，一般会分为   &lt;strong&gt;statistical performance 和 hard performance 来看, 前者指不同的同步协议导致算法收敛需要的迭代次数的多少，后者是单次迭代所对应的耗时。&lt;/strong&gt;两者的关系和precision\recall关系类似，就不赘述了。有了SSP，BSP就可以通过指定s=0而得到。而ASP同样可以通过制定s=无穷大来达到。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#26680;&amp;#24515;&amp;#25216;&amp;#26415;" title="&amp;#26680;&amp;#24515;&amp;#25216;&amp;#26415;"&gt;&lt;/a&gt;核心技术&lt;/h3&gt; &lt;p&gt;除了参数服务器的架构、同步协议之外，本节再对其他技术做一个简要的介绍，详细的了解请直接阅读沐帅的博士论文和相关发表的论文。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;热备、冷备技术&lt;/strong&gt;：为了防止server node挂掉，导致任务中断，可以采用两个技术，一个是对参数分片进行热备，每个分片存储在三个不同的server node中，以master-slave的形式存活。如果master挂掉，可以快速从slave获取并重启相关task。&lt;/p&gt;
 &lt;p&gt;除了热备，还可以定时写入checkpoint文件到分布式文件系统来对参数分片及其状态进行备份。进一步保证其安全性。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;Server node管理&lt;/strong&gt;：可以使用一致性哈希技术来解决server node的加入和退出问题，如图所示&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="&amp;#19968;&amp;#33268;&amp;#24615;&amp;#21704;&amp;#24076;" src="http://static.zybuluo.com/WuLiangchao/4qzc5lmkryj9m67xjlpnn2h1/image_1c8ctft67143lni21uk71s2tgag7i.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;当有server node加入或退出的时候，server manager负责对参数进行重新分片或者合并。&lt;/strong&gt;注意在对参数进行分片管理的情况下，一个分片只需要一把锁，这大大提升了系统的性能，也是参数服务器可以实用的一个关键点。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#&amp;#22823;&amp;#35268;&amp;#27169;&amp;#26426;&amp;#22120;&amp;#23398;&amp;#20064;&amp;#30340;&amp;#22235;&amp;#37325;&amp;#22659;&amp;#30028;" title="&amp;#22823;&amp;#35268;&amp;#27169;&amp;#26426;&amp;#22120;&amp;#23398;&amp;#20064;&amp;#30340;&amp;#22235;&amp;#37325;&amp;#22659;&amp;#30028;"&gt;&lt;/a&gt;大规模机器学习的四重境界&lt;/h2&gt; &lt;p&gt;到这里可以回到我们的标题了，大规模机器学习的四重境界到底是什么呢？&lt;/p&gt;
 &lt;p&gt;这四重境界的划分是作者个人阅读总结的一种想法，并不是业界标准，仅供大家参考。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#22659;&amp;#30028;1&amp;#65306;&amp;#21442;&amp;#25968;&amp;#21487;&amp;#21333;&amp;#26426;&amp;#23384;&amp;#20648;&amp;#21644;&amp;#26356;&amp;#26032;" title="&amp;#22659;&amp;#30028;1&amp;#65306;&amp;#21442;&amp;#25968;&amp;#21487;&amp;#21333;&amp;#26426;&amp;#23384;&amp;#20648;&amp;#21644;&amp;#26356;&amp;#26032;"&gt;&lt;/a&gt;境界1：参数可单机存储和更新&lt;/h3&gt; &lt;p&gt;此种境界较为简单，但仍可以使用参数服务器，通过数据并行来加速模型的训练。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#22659;&amp;#30028;2&amp;#65306;&amp;#21442;&amp;#25968;&amp;#19981;&amp;#21487;&amp;#21333;&amp;#26426;&amp;#23384;&amp;#20648;&amp;#65292;&amp;#21487;&amp;#20197;&amp;#21333;&amp;#26426;&amp;#26356;&amp;#26032;" title="&amp;#22659;&amp;#30028;2&amp;#65306;&amp;#21442;&amp;#25968;&amp;#19981;&amp;#21487;&amp;#21333;&amp;#26426;&amp;#23384;&amp;#20648;&amp;#65292;&amp;#21487;&amp;#20197;&amp;#21333;&amp;#26426;&amp;#26356;&amp;#26032;"&gt;&lt;/a&gt;境界2：参数不可单机存储，可以单机更新&lt;/h3&gt; &lt;p&gt;此种情况对应的是一些简单模型，比如 sparse logistic regression；当feature的数量突破百亿的时候，LR的权重参数不太可能在一台机器上完全存下，此时必须使用参数服务器架构对模型参数进行分片。但是注意一点，SGD的更新公式可以分开到单个维度进行计算，但是单个维度也是需要使用到上一轮迭代的所有参数(即计算预测值$f(w)$)。而我们之所以对参数进行分片就是因为我们无法将所有参数存放到一台机器，现在单个worker有需要使用所有的参数才能计算某个参数分片的梯度，这不是矛盾吗？可能吗？&lt;/p&gt;
 &lt;p&gt;答案是可能的，因为  &lt;strong&gt;单个样本的feature具有很高的稀疏性（sparseness）&lt;/strong&gt;。例如一个百亿feature的模型，单个训练样本往往只在其中很小一部分feature上有取值，其他都为0（假设feature取值都已经离散化了）。&lt;/p&gt;
 &lt;p&gt;因此  &lt;strong&gt;计算$f(w)$的时候可以只拉取不为0的feature对应的那部分w即可&lt;/strong&gt;。有文章统计一般这个级别的系统，稀疏性往往在0.1%（or 0.01%，记得不是很准，大致这样）以下。这样的稀疏性，可以让单机没有任何阻碍的计算$f(w)$。&lt;/p&gt;
 &lt;p&gt;目前公司开源的angel和AILab正在做的系统都处于这个境界。而原生spark还没有达到这个境界，只能在中小规模的圈子里厮混。Angel改造的基于Angel的Spark则达到了这个境界。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#22659;&amp;#30028;3&amp;#65306;&amp;#21442;&amp;#25968;&amp;#19981;&amp;#21487;&amp;#21333;&amp;#26426;&amp;#23384;&amp;#20648;&amp;#65292;&amp;#19981;&amp;#21487;&amp;#21333;&amp;#26426;&amp;#26356;&amp;#26032;&amp;#65292;&amp;#20294;&amp;#26080;&amp;#38656;&amp;#27169;&amp;#22411;&amp;#24182;&amp;#34892;" title="&amp;#22659;&amp;#30028;3&amp;#65306;&amp;#21442;&amp;#25968;&amp;#19981;&amp;#21487;&amp;#21333;&amp;#26426;&amp;#23384;&amp;#20648;&amp;#65292;&amp;#19981;&amp;#21487;&amp;#21333;&amp;#26426;&amp;#26356;&amp;#26032;&amp;#65292;&amp;#20294;&amp;#26080;&amp;#38656;&amp;#27169;&amp;#22411;&amp;#24182;&amp;#34892;"&gt;&lt;/a&gt;境界3：参数不可单机存储，不可单机更新，但无需模型并行&lt;/h3&gt; &lt;p&gt;境界3顺延境界2二来，当百亿级feature且feature比较稠密的时候，就需要计算框架进入到这层境界了，此时  &lt;strong&gt;单个worker的能力有限，无法完整加载一个样本，也无法完整计算$f(w)$&lt;/strong&gt;。怎么办呢？其实很简单，学过线性代数的都知道，矩阵可以分块。  &lt;strong&gt;向量是最简单的矩阵，自然可以切成一段一段的来计算。只是调度器需要支持算符分段而已了。&lt;/strong&gt;&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#22659;&amp;#30028;4&amp;#65306;&amp;#21442;&amp;#25968;&amp;#19981;&amp;#21487;&amp;#21333;&amp;#26426;&amp;#23384;&amp;#20648;&amp;#65292;&amp;#19981;&amp;#21487;&amp;#21333;&amp;#26426;&amp;#26356;&amp;#26032;&amp;#65292;&amp;#38656;&amp;#35201;&amp;#27169;&amp;#22411;&amp;#24182;&amp;#34892;" title="&amp;#22659;&amp;#30028;4&amp;#65306;&amp;#21442;&amp;#25968;&amp;#19981;&amp;#21487;&amp;#21333;&amp;#26426;&amp;#23384;&amp;#20648;&amp;#65292;&amp;#19981;&amp;#21487;&amp;#21333;&amp;#26426;&amp;#26356;&amp;#26032;&amp;#65292;&amp;#38656;&amp;#35201;&amp;#27169;&amp;#22411;&amp;#24182;&amp;#34892;"&gt;&lt;/a&gt;境界4：参数不可单机存储，不可单机更新，需要模型并行&lt;/h3&gt; &lt;p&gt;进入到这个层次的计算框架，可以算是世界一流了。可以处理超大规模的神经网络。这也是最典型的应用场景。此时不仅模型的参数不能单机存储，而且同一个迭代内，模型参数之间还有强的依赖关系，可以参见姐夫对 distbelief 的介绍里的模型切分。&lt;/p&gt;
 &lt;p&gt;此时首先需要增加一个coordinator组件来进行模型并行的concurrent控制。同时参数服务器框架需要支持namespace切分，coordinator将依赖关系通过namespace来进行表示。&lt;/p&gt;
 &lt;p&gt;一般参数间的依赖关系因模型而已，所以较难抽象出通用的coordinator来，而必须以某种形式通过脚本parser来生产整个计算任务的DAG图，然后通过DAG调度器来完成。对这个问题的介绍可以参考  &lt;a href="https://www.jianshu.com/p/00736aa21dc8" rel="external" target="_blank"&gt;Erix Xing的分享&lt;/a&gt;。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#Tensorflow" title="Tensorflow"&gt;&lt;/a&gt;Tensorflow&lt;/h3&gt; &lt;p&gt;目前业界比较知名的深度学习框架有Caffee、MXNet、Torch、Keras、Theano等，但目前最炙手可热的应该是google发布的Tensorflow。这里单独拿出来稍微分解下。&lt;/p&gt;
 &lt;p&gt;前面不少图片引自此文，从TF的论文来看，TF框架本身是支持模型并行和数据并行的，内置了一个参数服务器模块，但从开源版本所曝光的API来看，TF无法用来10B级别feature的稀疏LR模型。原因是已经曝光的API只支持在神经网络的不同层和层间进行参数切分，而超大规模LR可以看做一个神经单元，TF不支持单个神经单元参数切分到多个参数服务器node上。&lt;/p&gt;
 &lt;p&gt;当然，以google的实力，绝对是可以做到第四重境界的，之所以没有曝光，可能是基于其他商业目的的考量，比如使用他们的云计算服务。&lt;/p&gt;
 &lt;p&gt;综上，个人认为如果能做到第四重境界，目前可以说的上是世界一流的大规模机器学习框架。仅从沐帅的ppt里看他曾经达到过，google内部应该也是没有问题的。第三重境界应该是国内一流，第二充应该是国内前列吧。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://wulc.github.io/#&amp;#20854;&amp;#20182;" title="&amp;#20854;&amp;#20182;"&gt;&lt;/a&gt;其他&lt;/h2&gt; &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#36164;&amp;#28304;&amp;#31649;&amp;#29702;" title="&amp;#36164;&amp;#28304;&amp;#31649;&amp;#29702;"&gt;&lt;/a&gt;资源管理&lt;/h3&gt; &lt;p&gt;本文没有涉及到的部分是资源管理，大规模机器学习框架部署的集群往往资源消耗也比较大，需要专门的资源管理工具来维护。这方面yarn和mesos都是佼佼者，细节这里也就不介绍了。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://wulc.github.io/#&amp;#35774;&amp;#22791;" title="&amp;#35774;&amp;#22791;"&gt;&lt;/a&gt;设备&lt;/h3&gt; &lt;p&gt;除了资源管理工具，本身部署大规模机器学习集群本身对硬件也还是有些要求的，虽然理论上来说，所有commodity机器都可以用来搭建这类集群，但是考虑到性能，我们建议尽量用高内存的机器+万兆及以上的网卡。没有超快速的网卡，玩参数传递和样本加载估计会比较苦逼。&lt;/p&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;p&gt;参考文献：&lt;/p&gt;
 &lt;p&gt;[1] Cheng H T, Koc L, Harmsen J, et al. Wide &amp;amp; deep learning for recommender systems[C]//Proceedings of the 1st Workshop on Deep Learning for Recommender Systems. ACM, 2016: 7-10.&lt;/p&gt;
 &lt;p&gt;[2] Covington P, Adams J, Sargin E. Deep neural networks for youtube recommendations[C]//Proceedings of the 10th ACM Conference on Recommender Systems. ACM, 2016: 191-198.&lt;/p&gt;
 &lt;p&gt;[3] Abadi M, Agarwal A, Barham P, et al. Tensorflow: Large-scale machine learning on heterogeneous distributed systems[J]. arXiv preprint arXiv:1603.04467, 2016.&lt;/p&gt;
 &lt;p&gt;[4] Dean J, Corrado G, Monga R, et al. Large scale distributed deep networks[C]//Advances in neural information processing systems. 2012: 1223-1231.&lt;/p&gt;
 &lt;p&gt;[5] Dean J, Ghemawat S. MapReduce: simplified data processing on large clusters[J]. Communications of the ACM, 2008, 51(1): 107-113.&lt;/p&gt;
 &lt;p&gt;[6] Zaharia M, Chowdhury M, Das T, et al. Resilient distributed datasets: A fault-tolerant abstraction for in-memory cluster computing[C]//Proceedings of the 9th USENIX conference on Networked Systems Design and Implementation. USENIX Association, 2012: 2-2.&lt;/p&gt;
 &lt;p&gt;[7] Zhou J, Li X, Zhao P, et al. KunPeng: Parameter Server based Distributed Learning Systems and Its Applications in Alibaba and Ant Financial[C]//Proceedings of the 23rd ACM SIGKDD International Conference on Knowledge Discovery and Data Mining. ACM, 2017: 1693-1702.&lt;/p&gt;
 &lt;p&gt;[8] Li M, Andersen D G, Park J W, et al. Scaling Distributed Machine Learning with the Parameter Server[C]//OSDI. 2014, 14: 583-598.&lt;/p&gt;
 &lt;p&gt;[9] Smola A, Narayanamurthy S. An architecture for parallel topic models[J]. Proceedings of the VLDB Endowment, 2010, 3(1-2): 703-710.&lt;/p&gt;
 &lt;p&gt;[10] Power R, Li J. Piccolo: Building Fast, Distributed Programs with Partitioned Tables[C]//OSDI. 2010, 10: 1-14.&lt;/p&gt;
 &lt;p&gt;[11] Ho Q, Cipar J, Cui H, et al. More effective distributed ml via a stale synchronous parallel parameter server[C]//Advances in neural information processing systems. 2013: 1223-1231.&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>机器学习 分布式 机器学习</category>
      <guid isPermaLink="true">https://itindex.net/detail/58137-%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0-%E6%A1%86%E6%9E%B6-%E5%A2%83%E7%95%8C</guid>
      <pubDate>Sat, 10 Mar 2018 12:00:37 CST</pubDate>
    </item>
    <item>
      <title>如何实现一个基本的微信文章分类器</title>
      <link>https://itindex.net/detail/58104-%E5%BE%AE%E4%BF%A1-%E6%96%87%E7%AB%A0-%E5%88%86%E7%B1%BB</link>
      <description>&lt;blockquote&gt;本文源地址：  &lt;a href="http://www.fullstackyang.com/archives/538.html"&gt;http://www.fullstackyang.com/...&lt;/a&gt;，转发请注明该地址或segmentfault地址，谢谢！&lt;/blockquote&gt;
 &lt;p&gt;微信公众号发布的文章和一般门户网站的新闻文本类型有所不同，通常不能用现有的文本分类器直接对这些文章进行分类，不过文本分类的原理是相通的，本文以微信公众号文章为对象，介绍朴素贝叶斯分类器的实现过程。&lt;/p&gt;
 &lt;p&gt;文本分类的科学原理和数学证明在网上有很多，这里就不做赘述，本文尽量使用通熟易懂的表述方式，简明扼要地梳理一下文本分类器的各个知识点。&lt;/p&gt;
 &lt;p&gt;参考了一下Github，发现少有Java 8风格的实现，所以这里的实现尽量利用Java 8的特性，相比之前优势有很多，例如stream在统计聚合等运算上比较方便，代码不仅简洁，而且更加语义化，另外在多线程并行控制上也省去不少的工作。&lt;/p&gt;
 &lt;p&gt;本项目的地址：  &lt;a href="https://github.com/fullstackyang/article-classifier"&gt;https://github.com/fullstacky...&lt;/a&gt;&lt;/p&gt;
 &lt;h1&gt;一、文本分类器的概述&lt;/h1&gt;
 &lt;p&gt;文本分类器可以看作是一个预测函数，在给定的文本时，在预定的类别集合中，判断该文本最可能属于哪个类。&lt;/p&gt;
 &lt;p&gt;这里需要注意两个问题：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;在文本中含有比较多的标点符号和停用词（的，是，了等），直接使用整段文本处理肯定会产生很多不必要的计算，而且计算量也非常大，因此需要把给定的文本有效地进行表示，也就是选择一系列的特征词来代表这篇文本，这些特征词既可以比较好地反应所属文本的内容，又可以对不同文本有比较好的区分能力。&lt;/li&gt;
  &lt;li&gt;在进行文本表示之后，如何对这些特征词进行预测，这就是分类器的算法设计问题了，比较常见的模型有朴素贝叶斯，基于支持向量机（SVM），K-近邻（KNN），决策树等分类算法。这里我们选择简单易懂的朴素贝叶斯算法。在机器学习中，朴素贝叶斯建模属于有监督学习，因此需要收集大量的文本作为训练语料，并标注分类结果&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;综上，实现一个分类器通常分为以下几个步骤：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;收集并处理训练语料，以及最后测试用的测试语料&lt;/li&gt;
  &lt;li&gt;在训练集上进行特征选择，得到一系列的特征项（词），这些特征项组成了所谓的特征空间&lt;/li&gt;
  &lt;li&gt;为了表示某个特征项在不同文档中的重要程度，计算该特征项的权重，常用的计算方法有TF-IDF，本文采用的是“经典”朴素贝叶斯模型，这里不考虑特征项的权重（当然，一定要做也可以）&lt;/li&gt;
  &lt;li&gt;训练模型，对于朴素贝叶斯模型来说，主要的是计算每个特征项在不同类别中的条件概率，这点下面再做解释。&lt;/li&gt;
  &lt;li&gt;预测文本，模型训练完成之后可以保存到文件中，在预测时直接读入模型的数据进行计算。&lt;/li&gt;
&lt;/ol&gt;
 &lt;h1&gt;二、准备训练语料&lt;/h1&gt;
 &lt;p&gt;这里需要的语料就是微信公众号的文章，我们可以抓取搜狗微信搜索网站（  &lt;a href="http://weixin.sogou.com/"&gt;http://weixin.sogou.com/&lt;/a&gt;）首页上已经分类好的文章，直接采用其分类结果，这样也省去了标注的工作。至于如何开发爬虫去抓取文章，这里就不再讨论了。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="QQ20180228-161923.png" src="https://segmentfault.com/img/bV4Fe6?w=936&amp;h=245" title="QQ20180228-161923.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;“热门”这类别下的文章不具有一般性，因此不把它当作一个类别。剔除“热门”类别之后，最终我们抓取了30410篇文章，总共20个类别，每个类别的文章数并不均衡，其中最多 的是“养生堂”类别，有2569篇文章，最少的是“军事”类别，有654篇，大体符合微信上文章的分布情况。在保存时，我们保留了文章的标题，公众号名称，文章正文。&lt;/p&gt;
 &lt;h1&gt;三、特征选择&lt;/h1&gt;
 &lt;p&gt;如前文所述，特征选择的目的是降低特征空间的维度，避免维度灾难。简单地说，假设我们选择了2万个特征词，也就是说计算机通过学习，得到了一张有2万个词的“单词表”，以后它遇到的所有文本可以够用这张单词表中的词去表示其内容大意。这些特征词针对不同的类别有一定的区分能力，举例来说，“歼击机”可能来自“军事”，“越位”可能来自“体育”，“涨停”可能来自“财经”等等，而通常中文词汇量要比这个数字大得多，一本常见的汉语词典收录的词条数可达数十万。&lt;/p&gt;
 &lt;p&gt;常见的特征选择方法有两个，信息增益法和卡方检验法。&lt;/p&gt;
 &lt;h2&gt;3.1 信息增益&lt;/h2&gt;
 &lt;p&gt;信息增益法的衡量标准是，这个特征项可以为分类系统带来多少信息量，所谓的信息增益就是该特征项包含的能够帮预测类别的信息量，这里所说的信息量可以用熵来衡量，计算信息增益时还需要引入条件熵的概念，公式如下&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="IG.png" src="https://segmentfault.com/img/bV4Ffm?w=967&amp;h=287" title="IG.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;可能有些见到公式就头大的小伙伴不太友好，不过这个公式虽然看起来有点复杂，其实在计算中还是比较简单的，解释一下：&lt;/p&gt;
 &lt;p&gt;P(Cj)：Cj类文档在整个语料中出现的概率；&lt;/p&gt;
 &lt;p&gt;P(ti)：语料中包含特征项ti的文档的概率，取反就是不包含特征项ti的文档的概率；&lt;/p&gt;
 &lt;p&gt;P(Cj|ti)：文档包含特征项ti且属于Cj类的条件概率，取反就是文档不包含特征项ti且属于Cj类的条件概率&lt;/p&gt;
 &lt;p&gt;上面几个概率值，都可以比较方便地从训练语料上统计得到。若还有不明白的小伙伴，推荐阅读这篇博客：  &lt;a href="http://www.blogjava.net/zhenandaci/archive/2009/03/24/261701.html"&gt;文本分类入门（十一）特征选择方法之信息增益&lt;/a&gt;&lt;/p&gt;
 &lt;h2&gt;3.2 卡方检验&lt;/h2&gt;
 &lt;p&gt;卡方检验，基于χ2统计量(CHI)来衡量特征项ti和类别Cj之间的相关联程度，CHI统计值越高，该特征项与该类的相关性越大，如果两者相互独立，则CHI统计值接近零。计算时需要根据一张相依表（contingency table），公式也比较简单：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="ct.png" src="https://segmentfault.com/img/bV4Ff0?w=1061&amp;h=183" title="ct.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="chi.png" src="https://segmentfault.com/img/bV4Fge?w=517&amp;h=90" title="chi.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;其中N就是文档总数，如果想继续讨论这个公式，推荐阅读这篇博客：  &lt;a href="http://www.deeplearn.me/1446.html"&gt;特征选择（3）-卡方检验&lt;/a&gt;&lt;/p&gt;
 &lt;h2&gt;3.3 算法实现&lt;/h2&gt;
 &lt;p&gt;不论何种方式都需要对每个特征项进行估算，然后根据所得的数值进行筛选，通常可以设定一个阈值，低于阈值的特征项可以直接从特征空间中移除，另外也可以按照数值从高到低排序，并指定选择前N个。这里我们采用后者，总共截取前2万个特征项。&lt;/p&gt;
 &lt;p&gt;特征选择实现类的代码如下，其中，不同特征选择方法需实现Strategy接口，以获得不同方法计算得到的估值，这里在截取特征项时为了避免不必要的麻烦，剔除了字符串长度为1的词。&lt;/p&gt;
 &lt;p&gt;Doc对象表示一篇文档，其中包含了该文档的所属分类，以及分词结果（已经滤掉了停用词等），即Term集合；&lt;/p&gt;
 &lt;p&gt;Term对象主要包含3个字段，词本身的字符串，词性（用于过滤），词频TF；&lt;/p&gt;
 &lt;p&gt;Feature表示特征项，一个特征项对应一个Term对象，还包含两个hashmap，一个用来统计不同类别下该特征项出现的文档数量（categoryDocCounter），另一个用来统计不同类别下该特征项出现的频度（categoryTermCounter）（对应朴素贝叶斯两种不同模型，下文详述）&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;统计时引入FeatureCounter对象，使用stream的reduce方法进行归约。主要的思想就是把每一个文档中的Term集合，映射为Term和Feature的键值对，然后再和已有的Map进行合并，合并时如果遇到相同的Term，则调用Feature的Merge方法，该方法会将双方term的词频，以及categoryDocCounter和categoryTermCounter中的统计结果进行累加。最终将所有文档全部统计完成返回Feature集合。&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;@AllArgsConstructor
public class FeatureSelection {

    interface Strategy {
        Feature estimate(Feature feature);
    }

    private final Strategy strategy;
    private final static int FEATURE_SIZE = 20000;

    public List&amp;lt;Feature&amp;gt; select(List&amp;lt;Doc&amp;gt; docs) {
        return createFeatureSpace(docs.stream())
                .stream()
                .map(strategy::estimate)
                .filter(f -&amp;gt; f.getTerm().getWord().length() &amp;gt; 1)
                .sorted(comparing(Feature::getScore).reversed())
                .limit(FEATURE_SIZE)
                .collect(toList());
    }

    private Collection&amp;lt;Feature&amp;gt; createFeatureSpace(Stream&amp;lt;Doc&amp;gt; docs) {

        @AllArgsConstructor
        class FeatureCounter {
            private final Map&amp;lt;Term, Feature&amp;gt; featureMap;

            private FeatureCounter accumulate(Doc doc) {
                Map&amp;lt;Term, Feature&amp;gt; temp = doc.getTerms().parallelStream()
                        .map(t -&amp;gt; new Feature(t, doc.getCategory()))
                        .collect(toMap(Feature::getTerm, Function.identity()));
                if (!featureMap.isEmpty())
                    featureMap.values().forEach(f -&amp;gt; temp.merge(f.getTerm(), f, Feature::merge));
                return new FeatureCounter(temp);
            }

            private FeatureCounter combine(FeatureCounter featureCounter) {
                Map&amp;lt;Term, Feature&amp;gt; temp = Maps.newHashMap(featureMap);
                featureCounter.featureMap.values().forEach(f -&amp;gt; temp.merge(f.getTerm(), f, Feature::merge));
                return new FeatureCounter(temp);
            }
        }

        FeatureCounter counter = docs.parallel()
                .reduce(new FeatureCounter(Maps.newHashMap()),
                        FeatureCounter::accumulate,
                        FeatureCounter::combine);
        return counter.featureMap.values();
    }
}
public class Feature {
...
    public Feature merge(Feature feature) {
        if (this.term.equals(feature.getTerm())) {
            this.term.setTf(this.term.getTf() + feature.getTerm().getTf());
            feature.getCategoryDocCounter()
                    .forEach((k, v) -&amp;gt; categoryDocCounter.merge(k, v, (oldValue, newValue) -&amp;gt; oldValue + newValue));
            feature.getCategoryTermCounter()
                    .forEach((k, v) -&amp;gt; categoryTermCounter.merge(k, v, (oldValue, newValue) -&amp;gt; oldValue + newValue));
        }
        return this;
    }
}&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;信息增益实现如下，在计算条件熵时，利用了stream的collect方法，将包含和不包含特征项的两种情况用一个hashmap分开再进行归约。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;@AllArgsConstructor
public class IGStrategy implements FeatureSelection.Strategy {

    private final Collection&amp;lt;Category&amp;gt; categories;

    private final int total;

    public Feature estimate(Feature feature) {
        double totalEntropy = calcTotalEntropy();
        double conditionalEntrogy = calcConditionEntropy(feature);
        feature.setScore(totalEntropy - conditionalEntrogy);
        return feature;
    }

    private double calcTotalEntropy() {
        return Calculator.entropy(categories.stream().map(c -&amp;gt; (double) c.getDocCount() / total).collect(toList()));
    }

    private double calcConditionEntropy(Feature feature) {
        int featureCount = feature.getFeatureCount();
        double Pfeature = (double) featureCount / total;

        Map&amp;lt;Boolean, List&amp;lt;Double&amp;gt;&amp;gt; Pcondition = categories.parallelStream().collect(() -&amp;gt; new HashMap&amp;lt;Boolean, List&amp;lt;Double&amp;gt;&amp;gt;() {{
                    put(true, Lists.newArrayList());
                    put(false, Lists.newArrayList());
                }}, (map, category) -&amp;gt; {
                    int countDocWithFeature = feature.getDocCountByCategory(category);
                    //出现该特征词且属于类别key的文档数量/出现该特征词的文档总数量
                    map.get(true).add((double) countDocWithFeature / featureCount);
                    //未出现该特征词且属于类别key的文档数量/未出现该特征词的文档总数量
                    map.get(false).add((double) (category.getDocCount() - countDocWithFeature) / (total - featureCount));
                },
                (map1, map2) -&amp;gt; {
                    map1.get(true).addAll(map2.get(true));
                    map1.get(false).addAll(map2.get(false));
                }
        );
        return Calculator.conditionalEntrogy(Pfeature, Pcondition.get(true), Pcondition.get(false));
    }
}&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;卡方检验实现如下，每个特征项要在每个类别上分别计算CHI值，最终保留其最大值&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;@AllArgsConstructor
public class ChiSquaredStrategy implements Strategy {

    private final Collection&amp;lt;Category&amp;gt; categories;

    private final int total;

    @Override
    public Feature estimate(Feature feature) {

        class ContingencyTable {
            private final int A, B, C, D;

            private ContingencyTable(Feature feature, Category category) {
                A = feature.getDocCountByCategory(category);
                B = feature.getFeatureCount() - A;
                C = category.getDocCount() - A;
                D = total - A - B - C;
            }
        }

        Double chisquared = categories.stream()
                .map(c -&amp;gt; new ContingencyTable(feature, c))
                .map(ct -&amp;gt; Calculator.chisquare(ct.A, ct.B, ct.C, ct.D))
                .max(Comparator.comparingDouble(Double::valueOf)).get();
        feature.setScore(chisquared);
        return feature;
    }
}&lt;/code&gt;&lt;/pre&gt;
 &lt;h1&gt;四、朴素贝叶斯模型&lt;/h1&gt;
 &lt;h2&gt;4.1 原理简介&lt;/h2&gt;
 &lt;p&gt;朴素贝叶斯模型之所以称之“朴素”，是因为其假设特征之间是相互独立的，在文本分类中，也就是说，一篇文档中出现的词都是相互独立，彼此没有关联，显然文档中出现的词都是有逻辑性的，这种假设在现实中几乎是不成立的，但是这种假设却大大简化了计算，根据贝叶斯公式，文档Doc属于类别Ci的概率为：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="nb.png" src="https://segmentfault.com/img/bV4Fjq?w=405&amp;h=86" title="nb.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;P(Ci|Doc)是所求的后验概率，我们在判定分类时，根据每个类别计算P(Ci|Doc)，最终把P(Ci|Doc)取得最大值的那个分类作为文档的类别。其中，P(Doc)对于类别Ci来说是常量，在比较大小时可以不用参与计算，而P(Ci)表示类别Ci出现的概率，我们称之为先验概率，这可以方便地从训练集中统计得出，至于P(Doc|Ci)，也就是类别的条件概率，如果没有朴素贝叶斯的假设，那么计算是非常困难的。&lt;/p&gt;
 &lt;p&gt;举例来说，假设有一篇文章，内容为“王者荣耀：两款传说品质皮肤将优化，李白最新模型海报爆料”，经过特征选择，文档可以表示为Doc=(王者荣耀，传说，品质，皮肤，优化，李白，最新，模型，海报，爆料)，那么在预测时需要计算P(王者荣耀，传说，品质，皮肤，优化，李白，最新，模型，海报，爆料|Ci)，这样一个条件概率是不可计算的，因为第一个特征取值为“王者荣耀”，第二个特征取值“传说”……第十个特征取值“爆料”的文档很可能为没有，那么概率就为零，而基于朴素贝叶斯的假设，这个条件概率可以转化为：&lt;/p&gt;
 &lt;p&gt;P(王者荣耀，传说，品质，皮肤，优化，李白，最新，模型，海报，爆料|Ci)=P(王者荣耀|Ci)  &lt;em&gt;P(传说|Ci)……&lt;/em&gt;P(爆料|Ci)&lt;/p&gt;
 &lt;p&gt;于是我们就可以统计这些特征词在每个类别中出现的概率了，在这个例子中，游戏类别中“王者荣耀”这个特征项会频繁出现，因此P(王者荣耀|游戏)的条件概率要明显高于其他类别，这就是朴素贝叶斯模型的朴素之处，粗鲁的聪明。&lt;/p&gt;
 &lt;h2&gt;4.2 多项式模型与伯努利模型&lt;/h2&gt;
 &lt;p&gt;在具体实现中，朴素贝叶斯又可以分为两种模型，多项式模型（Multinomial）和伯努利模型（Bernoulli），另外还有高斯模型，主要用于处理连续型变量，在文本分类中不讨论。&lt;/p&gt;
 &lt;p&gt;多项式模型和伯努利模型的区别在于对词频的考察，在多项式模型中文档中特征项的频度是参与计算的，这对于长文本来说是比较公平的，例如上面的例子，“王者荣耀”在游戏类的文档中频度会比较高，而伯努利模型中，所有特征词都均等地对待，只要出现就记为1，未出现就记为0，两者公式如下：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="nb-b-m.png" src="https://segmentfault.com/img/bV4Fjz?w=755&amp;h=238" title="nb-b-m.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;在伯努利模型计算公式中，N(Doc(tj)|Ci)表示Ci类文档中特征tj出现的文档数，|D|表示类别Ci的文档数，P(Ci)可以用类别Ci的文档数/文档总数来计算，&lt;/p&gt;
 &lt;p&gt;在多项式模型计算公式中，TF(ti,Doc)是文档Doc中特征ti出现的频度，TF(ti,Ci)就表示类别Ci中特征ti出现的频度，|V|表示特征空间的大小，也就是特征选择之后，不同（即去掉重复之后）的特征项的总个数，而P(Ci)可以用类别Ci中特征词的总数/所有特征词的总数，所有特征词的总数也就是所有特征词的词频之和。&lt;/p&gt;
 &lt;p&gt;至于分子和分母都加上一定的常量，这是为了防止数据稀疏而产生结果为零的现象，这种操作称为拉普拉斯平滑，至于背后的原理，推荐阅读这篇博客：  &lt;a href="https://zhuanlan.zhihu.com/p/24291822"&gt;贝叶斯统计观点下的拉普拉斯平滑&lt;/a&gt;&lt;/p&gt;
 &lt;h2&gt;4.3 算法实现&lt;/h2&gt;
 &lt;p&gt;这里使用了枚举类来封装两个模型，并实现了分类器NaiveBayesClassifier和训练器NaiveBayesLearner中的两个接口，其中Pprior和Pcondition是训练器所需的方法，前者用来计算先验概率，后者用来计算不同特征项在不同类别下的条件概率；getConditionProbability是分类器所需的方法，NaiveBayesKnowledgeBase对象是模型数据的容器，它的getPconditionByWord方法就是用于查询不同特征词在不同类别下的条件概率&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;public enum NaiveBayesModels implements NaiveBayesClassifier.Model, NaiveBayesLearner.Model {
    Bernoulli {
        @Override
        public double Pprior(int total, Category category) {
            int Nc = category.getDocCount();
            return Math.log((double) Nc / total);
        }

        @Override
        public double Pcondition(Feature feature, Category category, double smoothing) {
            int Ncf = feature.getDocCountByCategory(category);
            int Nc = category.getDocCount();
            return Math.log((double) (1 + Ncf) / (Nc + smoothing));
        }

        @Override
        public List&amp;lt;Double&amp;gt; getConditionProbability(String category, List&amp;lt;Term&amp;gt; terms, final NaiveBayesKnowledgeBase knowledgeBase) {
            return terms.stream().map(term -&amp;gt; knowledgeBase.getPconditionByWord(category, term.getWord())).collect(toList());
        }
    },
    Multinomial {
        @Override
        public double Pprior(int total, Category category) {
            int Nt = category.getTermCount();
            return Math.log((double) Nt / total);
        }

        @Override
        public double Pcondition(Feature feature, Category category, double smoothing) {
            int Ntf = feature.getTermCountByCategory(category);
            int Nt = category.getTermCount();
            return Math.log((double) (1 + Ntf) / (Nt + smoothing));
        }

        @Override
        public List&amp;lt;Double&amp;gt; getConditionProbability(String category, List&amp;lt;Term&amp;gt; terms, final NaiveBayesKnowledgeBase knowledgeBase) {
            return terms.stream().map(term -&amp;gt; term.getTf() * knowledgeBase.getPconditionByWord(category, term.getWord())).collect(toList());
        }
    };
}&lt;/code&gt;&lt;/pre&gt;
 &lt;h1&gt;五、训练模型&lt;/h1&gt;
 &lt;p&gt;根据朴素贝叶斯模型的定义，训练模型的过程就是计算每个类的先验概率，以及每个特征项在不同类别下的条件概率，NaiveBayesKnowledgeBase对象将训练器在训练时得到的结果都保存起来，训练完成时写入文件，启动分类时从文件中读入数据交由分类器使用，那么在分类时就可以直接参与到计算过程中。&lt;/p&gt;
 &lt;p&gt;训练器的实现如下：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;public class NaiveBayesLearner {
    ……
    ……
    public NaiveBayesLearner statistics() {
        log.info(&amp;quot;开始统计...&amp;quot;);
        this.total = total();
        log.info(&amp;quot;total : &amp;quot; + total);
        this.categorySet = trainSet.getCategorySet();
        featureSet.forEach(f -&amp;gt; f.getCategoryTermCounter().forEach((category, count) -&amp;gt; category.setTermCount(category.getTermCount() + count)));
        return this;
    }

    public NaiveBayesKnowledgeBase build() {
        this.knowledgeBase.setCategories(createCategorySummaries(categorySet));
        this.knowledgeBase.setFeatures(createFeatureSummaries(featureSet, categorySet));
        return knowledgeBase;
    }

    private Map&amp;lt;String, NaiveBayesKnowledgeBase.FeatureSummary&amp;gt; createFeatureSummaries(final Set&amp;lt;Feature&amp;gt; featureSet, final Set&amp;lt;Category&amp;gt; categorySet) {
        return featureSet.parallelStream()
                .map(f -&amp;gt; knowledgeBase.createFeatureSummary(f, getPconditions(f, categorySet)))
                .collect(toMap(NaiveBayesKnowledgeBase.FeatureSummary::getWord, Function.identity()));
    }

    private Map&amp;lt;String, Double&amp;gt; createCategorySummaries(final Set&amp;lt;Category&amp;gt; categorySet) {
        return categorySet.stream().collect(toMap(Category::getName, c -&amp;gt; model.Pprior(total, c)));
    }

    private Map&amp;lt;String, Double&amp;gt; getPconditions(final Feature feature, final Set&amp;lt;Category&amp;gt; categorySet) {
        final double smoothing = smoothing();
        return categorySet.stream()
                .collect(toMap(Category::getName, c -&amp;gt; model.Pcondition(feature, c, smoothing)));
    }

    private int total() {
        if (model == Multinomial)
            return featureSet.parallelStream().map(Feature::getTerm).mapToInt(Term::getTf).sum();//总词频数
        else if (model == Bernoulli)
            return trainSet.getTotalDoc();//总文档数
        return 0;
    }

    private double smoothing() {
        if (model == Multinomial)
            return this.featureSet.size();
        else if (model == Bernoulli)
            return 2.0;
        return 0.0;
    }

    public static void main(String[] args) {
        TrainSet trainSet = new TrainSet(System.getProperty(&amp;quot;user.dir&amp;quot;) + &amp;quot;/trainset/&amp;quot;);

        log.info(&amp;quot;特征选择开始...&amp;quot;);
        FeatureSelection featureSelection = new FeatureSelection(new ChiSquaredStrategy(trainSet.getCategorySet(), trainSet.getTotalDoc()));
        List&amp;lt;Feature&amp;gt; features = featureSelection.select(trainSet.getDocs());
        log.info(&amp;quot;特征选择完成,特征数:[&amp;quot; + features.size() + &amp;quot;]&amp;quot;);

        NaiveBayesModels model = NaiveBayesModels.Multinomial;
        NaiveBayesLearner learner = new NaiveBayesLearner(model, trainSet, Sets.newHashSet(features));
        learner.statistics().build().write(model.getModelPath());
        log.info(&amp;quot;模型文件写入完成,路径:&amp;quot; + model.getModelPath());
    }
}&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;在main函数中执行整个训练过程，首先执行特征选择，这里使用卡方检验法，然后将得到特征空间，朴素贝叶斯模型（多项式模型），以及训练集TrainSet对象作为参数，初始化训练器，接着训练器开始进行统计的工作，事实上有一部分的统计工作，在初始化训练集对象时，就已经完成了，例如总文档数，每个类别下的文档数等，这些可以直接拿过来使用，最终将数据都装载到NaiveBayesKnowledgeBase对象当中去，并写入文件，格式为第一行是不同类别的先验概率，余下每一行对应一个特征项，每一列对应不同类别的条件概率值。&lt;/p&gt;
 &lt;h1&gt;六，测试模型&lt;/h1&gt;
 &lt;p&gt;分类器预测过程就相对于比较简单了，通过NaiveBayesKnowledgeBase读入数据，然后将指定的文本进行分词，匹配特征项，然后计算在不同类别下的后验概率，返回取得最大值对应的那个类别。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;public class NaiveBayesClassifier {
    ……
    private final Model model;

    private final NaiveBayesKnowledgeBase knowledgeBase;

    public NaiveBayesClassifier(Model model) {
        this.model = model;
        this.knowledgeBase = new NaiveBayesKnowledgeBase(model.getModelPath());
    }

    public String predict(String content) {
        Set&amp;lt;String&amp;gt; allFeatures = knowledgeBase.getFeatures().keySet();
        List&amp;lt;Term&amp;gt; terms = NLPTools.instance().segment(content).stream()
                .filter(t -&amp;gt; allFeatures.contains(t.getWord())).distinct().collect(toList());

        @AllArgsConstructor
        class Result {
            final String category;
            final double probability;
        }

        Result result = knowledgeBase.getCategories().keySet().stream()
                .map(c -&amp;gt; new Result(c, Calculator.Ppost(knowledgeBase.getCategoryProbability(c),
                        model.getConditionProbability(c, terms, knowledgeBase))))
                .max(Comparator.comparingDouble(r -&amp;gt; r.probability)).get();
        return result.category;
    }
}&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;在实际测试时，我们又单独抓取了搜狗微信搜索网站上的文章，按照100篇一组，一共30组进行分类的测试，最终结果每一组的准确率均在90%以上，最高达98%，效果良好。当然正规的评测需要同时评估准确率和召回率，这里就偷懒不做了。&lt;/p&gt;
 &lt;p&gt;另外还需要说明一点的是，由于训练集是来源于搜狗微信搜索网站的文章，类别仅限于这20个，这不足以覆盖所有微信公众号文章的类别，因此在测试其他来源的微信文章准确率一定会有所影响。当然如果有更加丰富的微信文章训练集的话，也可以利用这个模型重新训练，那么效果也会越来越好。&lt;/p&gt;
 &lt;h1&gt;七、参考文献与引用&lt;/h1&gt;
 &lt;ol&gt;
  &lt;li&gt;宗成庆. 统计自然语言处理[M]. 清华大学出版社, 2013.&lt;/li&gt;
  &lt;li&gt;T.M.Mitchell. 机器学习[M]. 机械工业出版社, 2003.&lt;/li&gt;
  &lt;li&gt;吴军. 数学之美[M]. 人民邮电出版社, 2012.&lt;/li&gt;
  &lt;li&gt;Raoul-Gabriel Urma, Mario Fusco, Alan Mycroft. Java 8 实战[M]. 人民邮电出版社, 2016.&lt;/li&gt;
  &lt;li&gt;Ansj中文分词器，   &lt;a href="https://github.com/NLPchina/ansj_seg"&gt;https://github.com/NLPchina/a...&lt;/a&gt;
&lt;/li&gt;
  &lt;li&gt;HanLP中文分词器，   &lt;a href="https://github.com/hankcs/HanLP"&gt;https://github.com/hankcs/HanLP&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>朴素贝叶斯 文本分类 机器学习 java</category>
      <guid isPermaLink="true">https://itindex.net/detail/58104-%E5%BE%AE%E4%BF%A1-%E6%96%87%E7%AB%A0-%E5%88%86%E7%B1%BB</guid>
      <pubDate>Thu, 01 Mar 2018 16:25:15 CST</pubDate>
    </item>
    <item>
      <title>做 ML 有关的工作，需要哪些技能？</title>
      <link>https://itindex.net/detail/58004-ml-%E5%B7%A5%E4%BD%9C-%E9%9C%80%E8%A6%81</link>
      <description>&lt;p&gt;【导读】：有位网友在 Quora 上提问：做与机器学习有关的工作，需要些什么技能呢？&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;我是一个自学者，现在家中自学线性代数。希望有天能在机器学习领域工作，但是我不太确定：&lt;/p&gt;
  &lt;ul&gt;
   &lt;li&gt;a) 这种工作/面试需要什么技术上的技能？&lt;/li&gt;
   &lt;li&gt;b) 有没有什么（做这种工作）必须的相关工作经历&lt;/li&gt;
&lt;/ul&gt;
  &lt;p&gt;比起空想，我至少开始行动了。任何的建议/指导对我来说都很有用。十分感谢！&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;  &lt;strong&gt;本文摘编了 2 位 Quora 网友的回答。&lt;/strong&gt;&lt;/p&gt;
 &lt;h3&gt;Jonathan A. Cox（机器学习和应用物理研究人员）的回答：&lt;/h3&gt;
 &lt;p&gt;这里有很多回复都提到了一些创业公司常用的工具。值得注意的是，在你 35 岁以上的职业生涯中，这些工具会变得完全不同。所以最好的办法是熟悉基本概念和数学。&lt;/p&gt;
 &lt;p&gt;其次，机器学习领域太过繁杂，根本不可能真正深入理解几十种不同的算法。我觉得你可以先深究几个最有前景的算法。说是这么说，你也要很了解一些基础的东西才行，比如说朴素贝叶斯、支持向量机、感知器、决策树。&lt;/p&gt;
 &lt;p&gt;最后，很多创业公司，或者非研究领域的人会告诉你要专注于那些快要老掉牙的算法（比如支持向量机 (SVM)）和大数据的数据库工具（比如说Hadoop）。考虑到你刚刚起步，我觉得你可以专注于那些很快会发展起来的架构：神经网络，单指令流多数据流（SIMD）向量计算（比如说GPU)。神经网络最近 10 年内开始革命性地颠覆自然语言处理、机器视觉、语音识别和其他领域。鉴于它的强大，这个影响还会继续下去。&lt;/p&gt;
 &lt;p&gt;综上，我会选择诸如 GPU 加速这类算法来自学和实现，再加一些分布式数据库的知识。以下是值得考虑的算法：&lt;/p&gt;
 &lt;p&gt;1. 深度置信网或者层叠降噪自动编码机 / Deep Belief Nets or Stacked Denoising Autoencoders  &lt;br /&gt;
2. 卷积神经网络（CNN）  &lt;br /&gt;
3. 长短记忆（LSTM）时间递归神经网络  &lt;br /&gt;
4. 结构递归神经网络（通常用于自然语言处理）  &lt;br /&gt;
5. word2vec 神经网络以及相关通过上下文学习词语的相关算法&lt;/p&gt;
 &lt;p&gt;另外一个很有前景的领域是强化学习，尤其是 Deep Mind 的 Q-learning。当你用到延迟奖励功能（比如玩 Atari 游戏时的得分），并且不能在你的网络中直接计算参数梯度时，你会用到强度学习。当你熟悉了上述的算法之后，你大约会想探索一下这个领域。这个领域跟机器人以及，更现实一点，关于人和动物是怎么学习的模拟，很有关联。&lt;/p&gt;
 &lt;p&gt;补充：我现在有这样的想法：过去的一年中，结合不同的神经算法，得到一个“深度“系统的方法逐渐显露出重大进步；因为这个方法的整体大于它的组成部分。鉴于奠基石已经铺下，我预计最显著的进步很可能会落在这个方向。一定程度上，细致整顿整个系统之前训练好个体模块，以及更快的计算群让这种进步成为了可能。（然而我们仍会在单个的算法中看到进展。）&lt;/p&gt;
 &lt;p&gt;换句话说，现在结合了卷积神经网络和多层感知器（MLP）的 LSTM，可以做到用句子描述图像之类的事情（来源：Goolge/Stanford）。类似地，已经有了具备强化学习能力的卷积神经网络和 MLP 组成的 Atari 游戏竞技网络。&lt;/p&gt;
 &lt;p&gt;往这个清单加上新东西同样很容易：把通过 word2vec/GloVe/skip-gram 训练出来的词向量输送给这样的复合网络，或者甚至是用递归神经网络训练的情感分析层。&lt;/p&gt;
 &lt;p&gt;有了神经网络和反向传播，在任意几何体上结合/叠加层，以及把组合作为整体系统来训练都变得容易了，它们可以扩展出强大的能力。&lt;/p&gt;
 &lt;p&gt;由许多不同种类的神经网络组成，用各种技术连接在一起，单独训练，协同微调，这些混合系统将继续向我们展示充满戏剧性的新能力。&lt;/p&gt;
 &lt;h3&gt;二、  &lt;a href="https://www.quora.com/profile/Joseph-Misiti"&gt;Joseph Misiti&lt;/a&gt; 的回答分享，4400+ 顶&lt;/h3&gt;
 &lt;p&gt;个人认为，以下是部分必需技能：&lt;/p&gt;
 &lt;p&gt;更新：我在 Github 创建了一个 repo，里面有上百的资源链接能够帮你起步。&lt;/p&gt;
 &lt;p&gt;https://github.com/josephmisiti/awesome-machine-learning&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;1. Python/C++/R/Java&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;假如你想在机器学习领域里找一份工作，你大概迟早要学会这些语言。Python 的 Numpy and Scipy 库 [2] 非常有用，因为这些库不仅有跟 MATLAB 相似的功能，而且将其整合进Web Service，或者在 Hadoop（见下）里使用的时候也十分简单。在加快代码运行速度的时候会用到C++。R [3] 在统计和画图的时候非常好用。Hadoop [4] 是用 Java写的，所以当你实现 mappers 和 reducers 的时候可能需要用到 Java（虽然你也可以通过 Hadoop streaming  [5] 使用脚本语言）。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2. 概率和统计&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;学习算法里的很大一部分都基于概率和统计理论，比如说朴素贝叶斯 [6]、高斯混合模型 [7]、隐Markov模型 [8]。 想要理解这些模型，就需要扎实的概率和统计功底。测度论 [9] 怎么拼命学都不过分。把统计作为模型的评价指标，比方说混淆矩阵、ROC 曲线、p值等等。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3. 应用数学和算法&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;对于 SVM [10] 这些判别模型，就需要算法理论的扎实基础了。即便你不需要从头开始实现SVM，学习算法理论也可以帮助你理解算法。需要学习的东西有凸优化 [11]，梯度下降 [12]，二次规划 [13]，拉格朗日 [14]，偏微分方程等等。要习惯看求和符号 [16]。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4. 分布式计算&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;如今大部分的机器学习工作都要求使用大型数据集（见《数据科学》）[17]。处理这些数据并不能凭借一台机器，而是需要把工作分派给整一个集群。像 Apache Hadoop [4] 这样的项目和亚马逊的 EC2 [18] 这样的云服务就能以合理的成本方便地处理这些数据。虽然 Hadoop 把很多硬核的分布式计算问题隐去了，对 map-reduce [22] 和分布式文件系统 [19] 等方面有透彻的理解还是十分必要的。Apache Mahout [20] 和 Apache Whirr [21] 也很值得一看。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;5.精通 Unix/Linux 工具&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;除非你运气好，不然你总是需要修改你数据集的格式来载入到 R、Hadoop、HBase 等。Python这些脚本语言（使用 python 里的 re）也能用来完成这个事情，但是最好的方法大约还是掌握专门为此设计的 unix 工具：cat [24]、grep [25]、find [26]、awk [27]、sed [28]、sort [29]、cut [30]、tr [31]等。因为最有可能在基于 Linux 的机器上处理这些数据（我记得 Hadoop 并不能在 Windows 上运作），你一定可以用到这些工具。要学会热爱并尽可能地使用这些工具。这些工具让事情简单多了，比如这个例子 [1]。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;6.熟悉Hadoop的子项目&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;HBase、Zookeeper [32]、Hive [33]、Mahout 等。这些项目可以储存或读取数据，而且他们可扩展。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;7. 了解高级信号处理的技术&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;特征提取是机器学习最重要的部分之一。如果你的特性提取得不好，那不论你使用什么算法，性能都不会好。根据具体情况，你可能可以应用一些很酷炫的高级信号处理算法，比方说 wavelets [42]、shearlets [43]、curvelets [44]、contourlets [45]、bandlets [46]。了解并尝试应用时间-频率分析方法 [47]。如果你还没有了解傅里叶分析和卷积的话，这些东西也值得学习。后面提到的这俩是信号处理的基础知识。&lt;/p&gt;
 &lt;p&gt;最后，尽量多练习、多阅读。有空的时候可以读读像 Google Map-Reduce [34]、Google File System [35]、Google Big Table [36]、The Unreasonable Effectiveness of Data [37] 上的论文。网上也有很多关于机器学习的好书的免费资源，这些也应该多读。[38][39][40] 我找到了一个很好的课程，并且在转发到Github了 [41]。与其直接使用开源的扩展包，不如自己写一份并且对比结果。如果你能从头写一个支持向量机，你就会明白像支持向量、gamma、cost、hyperplanes 等的概念。载入数据并开始训练并不难，难的是理解这所有的概念。&lt;/p&gt;
 &lt;p&gt;祝好运！&lt;/p&gt;
 &lt;p&gt; &lt;/p&gt;
 &lt;p&gt;[1]   &lt;a href="http://radar.oreilly.com/2011/04/data-hand-tools.html"&gt;http://radar.oreilly.com/2011/04…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[2]   &lt;a href="http://numpy.scipy.org/"&gt;http://numpy.scipy.org/&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[3]   &lt;a href="http://www.r-project.org/"&gt;http://www.r-project.org/&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[4]   &lt;a href="http://hadoop.apache.org/"&gt;http://hadoop.apache.org/&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[5]   &lt;a href="http://hadoop.apache.org/common/docs/r0.15.2/streaming.html"&gt;http://hadoop.apache.org/common/…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[6]   &lt;a href="http://en.wikipedia.org/wiki/Naive_Bayes_classifier"&gt;http://en.wikipedia.org/wiki/Nai…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[7]   &lt;a href="http://en.wikipedia.org/wiki/Mixture_model"&gt;http://en.wikipedia.org/wiki/Mix…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[8]   &lt;a href="http://en.wikipedia.org/wiki/Hidden_Markov_model"&gt;http://en.wikipedia.org/wiki/Hid…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[9]   &lt;a href="http://en.wikipedia.org/wiki/Measure_(mathematics)"&gt;http://en.wikipedia.org/wiki/Mea…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[10]   &lt;a href="http://en.wikipedia.org/wiki/Support_vector_machine"&gt;http://en.wikipedia.org/wiki/Sup…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[11]   &lt;a href="http://en.wikipedia.org/wiki/Convex_optimization"&gt;http://en.wikipedia.org/wiki/Con…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[12]   &lt;a href="http://en.wikipedia.org/wiki/Gradient_descent"&gt;http://en.wikipedia.org/wiki/Gra…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[13]   &lt;a href="http://en.wikipedia.org/wiki/Quadratic_programming"&gt;http://en.wikipedia.org/wiki/Qua…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[14]   &lt;a href="http://en.wikipedia.org/wiki/Lagrange_multiplier"&gt;http://en.wikipedia.org/wiki/Lag…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[15]   &lt;a href="http://en.wikipedia.org/wiki/Partial_differential_equation"&gt;http://en.wikipedia.org/wiki/Par…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[16]   &lt;a href="http://en.wikipedia.org/wiki/Summation"&gt;http://en.wikipedia.org/wiki/Sum…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[17]   &lt;a href="http://radar.oreilly.com/2010/06/what-is-data-science.html"&gt;http://radar.oreilly.com/2010/06…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[18]   &lt;a href="http://aws.amazon.com/ec2/"&gt;http://aws.amazon.com/ec2/&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[19]   &lt;a href="http://en.wikipedia.org/wiki/Google_File_System"&gt;http://en.wikipedia.org/wiki/Goo…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[20]   &lt;a href="http://mahout.apache.org/"&gt;http://mahout.apache.org/&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[21]   &lt;a href="http://incubator.apache.org/whirr/"&gt;http://incubator.apache.org/whirr/&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[22]   &lt;a href="http://en.wikipedia.org/wiki/MapReduce"&gt;http://en.wikipedia.org/wiki/Map…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[23]   &lt;a href="http://hbase.apache.org/"&gt;http://hbase.apache.org/&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[24]   &lt;a href="http://en.wikipedia.org/wiki/Cat_(Unix)"&gt;http://en.wikipedia.org/wiki/Cat…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[25]   &lt;a href="http://en.wikipedia.org/wiki/Grep"&gt;http://en.wikipedia.org/wiki/Grep&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[26]   &lt;a href="http://en.wikipedia.org/wiki/Find"&gt;http://en.wikipedia.org/wiki/Find&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[27]   &lt;a href="http://en.wikipedia.org/wiki/AWK"&gt;http://en.wikipedia.org/wiki/AWK&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[28]   &lt;a href="http://en.wikipedia.org/wiki/Sed"&gt;http://en.wikipedia.org/wiki/Sed&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[29]   &lt;a href="http://en.wikipedia.org/wiki/Sort_(Unix)"&gt;http://en.wikipedia.org/wiki/Sor…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[30]   &lt;a href="http://en.wikipedia.org/wiki/Cut_(Unix)"&gt;http://en.wikipedia.org/wiki/Cut…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[31]   &lt;a href="http://en.wikipedia.org/wiki/Tr_(Unix)"&gt;http://en.wikipedia.org/wiki/Tr_…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[32]   &lt;a href="http://zookeeper.apache.org/"&gt;http://zookeeper.apache.org/&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[33]   &lt;a href="http://hive.apache.org/"&gt;http://hive.apache.org/&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[34]   &lt;a href="http://static.googleusercontent.com/external_content/untrusted_dlcp/labs.google.com/en/us/papers/mapreduce-osdi04.pdf"&gt;http://static.googleusercontent….&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[35]  &lt;a href="http://static.googleusercontent.com/external_content/untrusted_dlcp/labs.google.com/en/us/papers/gfs-sosp2003.pdf"&gt;http://static.googleusercontent….&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[36]  &lt;a href="http://static.googleusercontent.com/external_content/untrusted_dlcp/labs.google.com/en/us/papers/bigtable-osdi06.pdf"&gt;http://static.googleusercontent….&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[37]  &lt;a href="http://static.googleusercontent.com/external_content/untrusted_dlcp/research.google.com/en/us/pubs/archive/35179.pdf"&gt;http://static.googleusercontent….&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[38]   &lt;a href="http://www.ics.uci.edu/~welling/teaching/273ASpring10/IntroMLBook.pdf"&gt;http://www.ics.uci.edu/~welling/…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[39]   &lt;a href="http://www.stanford.edu/~hastie/local.ftp/Springer/OLD//ESLII_print4.pdf"&gt;http://www.stanford.edu/~hastie/…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[40]   &lt;a href="http://infolab.stanford.edu/~ullman/mmds.html"&gt;http://infolab.stanford.edu/~ull…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[41]   &lt;a href="https://github.com/josephmisiti/machine-learning-module"&gt;https://github.com/josephmisiti/…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[42]   &lt;a href="http://en.wikipedia.org/wiki/Wavelet"&gt;http://en.wikipedia.org/wiki/Wav…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[43]   &lt;a href="http://www.shearlet.uni-osnabrueck.de/papers/Smrus.pdf"&gt;http://www.shearlet.uni-osnabrue…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[44]   &lt;a href="http://math.mit.edu/icg/papers/FDCT.pdf"&gt;http://math.mit.edu/icg/papers/F…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[45]   &lt;a href="http://www.ifp.illinois.edu/~minhdo/publications/contourlet.pdf"&gt;http://www.ifp.illinois.edu/~min…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[46]   &lt;a href="http://www.cmap.polytechnique.fr/~mallat/papiers/07-NumerAlgo-MallatPeyre-BandletsReview.pdf"&gt;http://www.cmap.polytechnique.fr…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[47 ]  &lt;a href="http://en.wikipedia.org/wiki/Time%E2%80%93frequency_analysis"&gt;http://en.wikipedia.org/wiki/Tim…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[48]   &lt;a href="http://en.wikipedia.org/wiki/Fourier_analysis"&gt;http://en.wikipedia.org/wiki/Fou…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;[49 ]  &lt;a href="http://en.wikipedia.org/wiki/Convolution"&gt;http://en.wikipedia.org/wiki/Con…&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://blog.jobbole.com/113592/"&gt;做 ML 有关的工作，需要哪些技能？&lt;/a&gt;，首发于  &lt;a href="http://blog.jobbole.com"&gt;文章 - 伯乐在线&lt;/a&gt;。&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>IT技术 机器学习</category>
      <guid isPermaLink="true">https://itindex.net/detail/58004-ml-%E5%B7%A5%E4%BD%9C-%E9%9C%80%E8%A6%81</guid>
      <pubDate>Tue, 30 Jan 2018 23:08:32 CST</pubDate>
    </item>
    <item>
      <title>构建基于Spark的推荐引擎（Python）</title>
      <link>https://itindex.net/detail/57796-spark-%E6%8E%A8%E8%8D%90%E5%BC%95%E6%93%8E-python</link>
      <description>&lt;h1&gt;构建基于Spark的推荐引擎（Python）&lt;/h1&gt;
 &lt;blockquote&gt;推荐引擎背后的想法是预测人们可能喜好的物品并通过探寻物品之间的联系来辅助这个过程&lt;/blockquote&gt;
 &lt;p&gt;在学习Spark机器学习这本书时，书上用scala完成，自己不熟悉遂用pyshark完成，更深入的理解了spark对协同过滤的实现&lt;/p&gt;
 &lt;p&gt;在这里我们的推荐模型选用协同过滤这种类型，使用Spark的MLlib中推荐模型库中基于矩阵分解（matrix factorization）的实现。&lt;/p&gt;
 &lt;h2&gt;协同过滤（Collaborative Filtering）&lt;/h2&gt;
 &lt;blockquote&gt;协同过滤简单来说是利用某兴趣相投、拥有共同经验之群体的喜好来推荐用户感兴趣的信息，个人通过合作的机制给予信息相当程度的回应（如评分）并记录下来以达到过滤的目的进而帮助别人筛选信息，回应不一定局限于特别感兴趣的，特别不感兴趣信息的纪录也相当重要。&lt;/blockquote&gt;
 &lt;p&gt;很简单的例子来介绍就是日常我们生活中经常找电影会通过向和自己品味类似的朋友要求推荐，这就是协同过滤的思想&lt;/p&gt;
 &lt;h3&gt;基于用户的协同过滤推荐机制的基本原理&lt;/h3&gt;
 &lt;p&gt;基于用户或物品的方法的得分取决于若干用户或是物品之间依据相似度所构成的集合（即邻居），故它们也常被称为最邻近模型。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="&amp;#21327;&amp;#21516;&amp;#36807;&amp;#28388;" src="https://segmentfault.com/img/bV0AC2?w=287&amp;h=210" title="&amp;#21327;&amp;#21516;&amp;#36807;&amp;#28388;"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;矩阵分解&lt;/h2&gt;
 &lt;p&gt;这里我们要处理的数据是用户提供的自身偏好数据，即用户对物品的打分数据。&lt;/p&gt;
 &lt;p&gt;这些数据可以被转换成用户为行，物品为列的二维矩阵，即评分矩阵A（m*n）表示m个用户对n个物品的打分情况&lt;/p&gt;
 &lt;table&gt;
  &lt;tr&gt;
   &lt;th align="center"&gt;UI&lt;/th&gt;
   &lt;th align="center"&gt;i1&lt;/th&gt;
   &lt;th align="center"&gt;i2&lt;/th&gt;
   &lt;th&gt;i3&lt;/th&gt;
&lt;/tr&gt;

  &lt;tr&gt;
   &lt;td align="center"&gt;u1&lt;/td&gt;
   &lt;td align="center"&gt;3.0&lt;/td&gt;
   &lt;td align="center"&gt;3.0&lt;/td&gt;
   &lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td align="center"&gt;u2&lt;/td&gt;
   &lt;td align="center"&gt;?&lt;/td&gt;
   &lt;td align="center"&gt;2.0&lt;/td&gt;
   &lt;td&gt;4.0&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td align="center"&gt;u3&lt;/td&gt;
   &lt;td align="center"&gt;?&lt;/td&gt;
   &lt;td align="center"&gt;5.0&lt;/td&gt;
   &lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;p&gt;这个矩阵A很多元素都是空的，我们称其为缺失值（missing value）。&lt;/p&gt;
 &lt;p&gt;协同过滤提出了一种支持不完整评分矩阵的矩阵分解方法,不用对评分矩阵进行估值填充。&lt;/p&gt;
 &lt;p&gt;在推荐系统中，我们希望得到用户对所有物品的打分情况，如果用户没有对一个物品打分，那么就需要预测用户是否会对该物品打分，以及会打多少分。这就是所谓的矩阵补全（矩阵分解）&lt;/p&gt;
 &lt;p&gt;对于这个矩阵分解的方式就是找出两个低维度的矩阵，使得他们的乘积是原始的矩阵。&lt;/p&gt;
 &lt;p&gt;因此这也是一种降维技术。要找到和‘用户-物品’矩阵近似的k维矩阵，最终要求得出表示用户的m x k维矩阵，和一个表示物品的k x n维矩阵。  &lt;br /&gt;这两个矩阵也称作因子矩阵。&lt;/p&gt;
 &lt;p&gt;对于k的理解为对于每个产品，这里已电影为例，可以从k个角度进行评价，即k个特征值&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="https://segmentfault.com/img/bV0ADr?w=431&amp;h=362" title="&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;由于是对‘用户-物品’矩阵直接建模，用这些模型进行预测也相对直接，要计算给定用户对某个物品的预计评级，就从用户因子矩阵和物品因子矩阵分别选取对应的行与列，然后计算两者的点积。&lt;/p&gt;
 &lt;p&gt;假设对于用户A，该用户对一部电影的综合评分和电影的特征值存在一定的线性关系，&lt;/p&gt;
 &lt;p&gt;即电影的综合评分=(a1  &lt;em&gt;d1+a2&lt;/em&gt;d2+a3  &lt;em&gt;d3+a4&lt;/em&gt;d4)&lt;/p&gt;
 &lt;p&gt;其中a1-4为用户A的特征值，d1-4为之前所说的电影的特征值&lt;/p&gt;
 &lt;h3&gt;最小二乘法实现协同&lt;/h3&gt;
 &lt;blockquote&gt;最小二乘法（Alternating Least Squares, ALS）是一种求解矩阵分解问题的最优化方法。它功能强大、效果理想而且被证明相对容易并行化。这使得它很适合如Spark这样的平台。&lt;/blockquote&gt;
 &lt;p&gt;使用用户特征矩阵$ U(m*k) $ 中的第i个用户的特征向量$ u_i $ ,&lt;/p&gt;
 &lt;p&gt;和产品特征矩阵$ V(n*k) $第j个物品的特征向量$ v_i $来预测打分矩阵$ A(m*n) $中的$ a_{ij} $，&lt;/p&gt;
 &lt;p&gt;得出矩阵分解模型的损失函数如下&lt;/p&gt;
 &lt;p&gt;$$  \large C = \sum\limits_{(i,j)\in R}[(a_{ij} - u_iv_j^T)^2+\lambda(u_i^2+v_j^2)] $$&lt;/p&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;blockquote&gt;通常的优化方法分为两种：交叉最小二乘法（alternative least squares）和随机梯度下降法（stochastic gradient descent）。Spark使用的是交叉最小二乘法（ALS）来最优化损失函数。  &lt;br /&gt;算法的思想就是：我们先随机生成然后固定它求解，再固定求解，这样交替进行下去，直到取得最优解$ min(C) $&lt;/blockquote&gt;
 &lt;h2&gt;使用PySpark实现&lt;/h2&gt;
 &lt;p&gt;我们这里的数据集是Movielens 100k数据集，包含了多个用户对多部电影的10万次评级数据&lt;/p&gt;
 &lt;p&gt;  &lt;a href="https://files.grouplens.org/datasets/movielens/ml-100k.zip"&gt;下载地址&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;读取评级数据集，该数据包括用户ID，影片ID，星级和时间戳等字段，使用/t分隔&lt;/p&gt;
 &lt;p&gt;通过sc.textFile()读取数据为RDD,通过分隔符取前3个属性分别为用户ID，影片ID，星级&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;rawData = sc.textFile(&amp;apos;/home/null/hadoop/data/ml-100k/u.data&amp;apos;)
rawData.first()
type(rawData)&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;pyspark.rdd.RDD



&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;rawRatings = rawData.map(lambda line: line.split(&amp;quot;\t&amp;quot;)[:3])
rawRatings.first()&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;[&amp;apos;196&amp;apos;, &amp;apos;242&amp;apos;, &amp;apos;3&amp;apos;]



&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;# 导入spark中的mllib的推荐库
import pyspark.mllib.recommendation as rd&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;生成Rating类的RDD数据&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;# 由于ALS模型需要由Rating记录构成的RDD作为参数，因此这里用rd.Rating方法封装数据
ratings = rawRatings.map(lambda line: rd.Rating(int(line[0]), int(line[1]), float(line[2])))
ratings.first()&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;Rating(user=196, product=242, rating=3.0)


&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;训练ALS模型&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;rank: 对应ALS模型中的因子个数，即矩阵分解出的两个矩阵的新的行/列数，即$ A \approx UV^T , k &amp;lt;&amp;lt; m,n $m,n中的k&lt;/li&gt;
  &lt;li&gt;iterations: 对应运行时的最大迭代次数&lt;/li&gt;
  &lt;li&gt;lambda: 控制模型的正则化过程，从而控制模型的过拟合情况。&lt;/li&gt;
&lt;/ul&gt;
 &lt;pre&gt;  &lt;code&gt;# 训练ALS模型
model = rd.ALS.train(ratings, 50, 10, 0.01)
model&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;&amp;lt;pyspark.mllib.recommendation.MatrixFactorizationModel at 0x7f53cc29c710&amp;gt;



&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;# 对用户789预测其对电影123的评级
predictedRating = model.predict(789,123)
predictedRating&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;3.1740832151065774



&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;# 获取对用户789的前10推荐
topKRecs = model.recommendProducts(789,10)
topKRecs&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;[Rating(user=789, product=429, rating=6.302989890089658),
 Rating(user=789, product=496, rating=5.782039583864358),
 Rating(user=789, product=651, rating=5.665266358968961),
 Rating(user=789, product=250, rating=5.551256887914674),
 Rating(user=789, product=64, rating=5.5336980239740186),
 Rating(user=789, product=603, rating=5.468600343790217),
 Rating(user=789, product=317, rating=5.442052952711695),
 Rating(user=789, product=480, rating=5.414042111530209),
 Rating(user=789, product=180, rating=5.413309515550101),
 Rating(user=789, product=443, rating=5.400024900653429)]


&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;检查推荐内容&lt;/h3&gt;
 &lt;p&gt;这里首先将电影的数据读入，讲数据处理为电影ID到标题的映射&lt;/p&gt;
 &lt;p&gt;然后获取某个用户评级前10的影片同推荐这个用户的前10影片进行比较&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;#检查推荐内容
movies = sc.textFile(&amp;apos;/home/null/hadoop/data/ml-100k/u.item&amp;apos;)
movies.first()&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;&amp;apos;1|Toy Story (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Toy%20Story%20(1995)|0|0|0|1|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0&amp;apos;



&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;titles_data= movies.map(lambda line: line.split(&amp;quot;|&amp;quot;)[:2]).collect()
titles = dict(titles_data)
titles&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;moviesForUser = ratings.keyBy(lambda rating: rating.user).lookup(789)
type(moviesForUser)&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;list



&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;moviesForUser = sorted(moviesForUser,key=lambda r: r.rating, reverse=True)[0:10]
moviesForUser&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;[Rating(user=789, product=127, rating=5.0),
 Rating(user=789, product=475, rating=5.0),
 Rating(user=789, product=9, rating=5.0),
 Rating(user=789, product=50, rating=5.0),
 Rating(user=789, product=150, rating=5.0),
 Rating(user=789, product=276, rating=5.0),
 Rating(user=789, product=129, rating=5.0),
 Rating(user=789, product=100, rating=5.0),
 Rating(user=789, product=741, rating=5.0),
 Rating(user=789, product=1012, rating=4.0)]



&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;[(titles[str(r.product)], r.rating) for r in moviesForUser]&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;[(&amp;apos;Godfather, The (1972)&amp;apos;, 5.0),
 (&amp;apos;Trainspotting (1996)&amp;apos;, 5.0),
 (&amp;apos;Dead Man Walking (1995)&amp;apos;, 5.0),
 (&amp;apos;Star Wars (1977)&amp;apos;, 5.0),
 (&amp;apos;Swingers (1996)&amp;apos;, 5.0),
 (&amp;apos;Leaving Las Vegas (1995)&amp;apos;, 5.0),
 (&amp;apos;Bound (1996)&amp;apos;, 5.0),
 (&amp;apos;Fargo (1996)&amp;apos;, 5.0),
 (&amp;apos;Last Supper, The (1995)&amp;apos;, 5.0),
 (&amp;apos;Private Parts (1997)&amp;apos;, 4.0)]



&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;[(titles[str(r.product)], r.rating) for r in topKRecs]&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;[(&amp;apos;Day the Earth Stood Still, The (1951)&amp;apos;, 6.302989890089658),
 (&amp;quot;It&amp;apos;s a Wonderful Life (1946)&amp;quot;, 5.782039583864358),
 (&amp;apos;Glory (1989)&amp;apos;, 5.665266358968961),
 (&amp;apos;Fifth Element, The (1997)&amp;apos;, 5.551256887914674),
 (&amp;apos;Shawshank Redemption, The (1994)&amp;apos;, 5.5336980239740186),
 (&amp;apos;Rear Window (1954)&amp;apos;, 5.468600343790217),
 (&amp;apos;In the Name of the Father (1993)&amp;apos;, 5.442052952711695),
 (&amp;apos;North by Northwest (1959)&amp;apos;, 5.414042111530209),
 (&amp;apos;Apocalypse Now (1979)&amp;apos;, 5.413309515550101),
 (&amp;apos;Birds, The (1963)&amp;apos;, 5.400024900653429)]


&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;推荐模型效果的评估&lt;/h3&gt;
 &lt;h4&gt;均方差（Mean Squared Error,MSE）&lt;/h4&gt;
 &lt;blockquote&gt;定义为各平方误差的和与总数目的商，其中平方误差是指预测到的评级与真实评级的差值平方&lt;/blockquote&gt;
 &lt;p&gt;直接度量模型的预测目标变量的好坏&lt;/p&gt;
 &lt;h4&gt;均方根误差（Root Mean Squared Error,RMSE）&lt;/h4&gt;
 &lt;blockquote&gt;对MSE取其平方根，即预计评级和实际评级的差值的标准差&lt;/blockquote&gt;
 &lt;pre&gt;  &lt;code&gt;# evaluation metric
usersProducts = ratings.map(lambda r:(r.user, r.product))
predictions = model.predictAll(usersProducts).map(lambda r: ((r.user, r.product),r.rating))
predictions.first()&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;((316, 1084), 4.006135662882842)



&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;ratingsAndPredictions = ratings.map(lambda r: ((r.user,r.product), r.rating)).join(predictions)
ratingsAndPredictions.first()&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;((186, 302), (3.0, 2.7544572973050236))



&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;# 使用MLlib内置的评估函数计算MSE,RMSE
from pyspark.mllib.evaluation import RegressionMetrics
predictionsAndTrue = ratingsAndPredictions.map(lambda line: (line[1][0],line[1][3]))
predictionsAndTrue.first()&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;(3.0, 2.7544572973050236)



&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;# MSE
regressionMetrics = RegressionMetrics(predictionsAndTrue)
regressionMetrics.meanSquaredError&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;0.08509832708963357



&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;# RMSE
regressionMetrics.rootMeanSquaredError&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;0.2917161755707653


&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;参考：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="http://blog.csdn.net/u011239443/article/details/51752904"&gt;深入理解Spark ML：基于ALS矩阵分解的协同过滤算法与源码分析&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://www.zhihu.com/question/31509438/answer/52268608"&gt;如何解释spark mllib中ALS算法的原理？——知乎&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Maching Learning With Spark——人民邮电出版社&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>机器学习 大数据 python spark</category>
      <guid isPermaLink="true">https://itindex.net/detail/57796-spark-%E6%8E%A8%E8%8D%90%E5%BC%95%E6%93%8E-python</guid>
      <pubDate>Tue, 19 Dec 2017 18:29:13 CST</pubDate>
    </item>
    <item>
      <title>Java使用google的thumbnailator工具对图片压缩水印等做处理</title>
      <link>https://itindex.net/detail/57642-java-google-thumbnailator</link>
      <description>&lt;h1&gt;今天给大家分享一个非常好用的工具thumbnailator。Thumbnailator是一个非常好的图片开源工具&lt;/h1&gt; 

 &lt;h1&gt;使用方法：&lt;/h1&gt; 

 &lt;h1&gt;在pom中加入以下jar包&lt;/h1&gt; 
 &lt;pre&gt;&amp;lt;!-- 图片缩略图 图片压缩 水印 start--&amp;gt;
&amp;lt;dependency&amp;gt;
&amp;lt;groupId&amp;gt;net.coobird&amp;lt;/groupId&amp;gt;
&amp;lt;artifactId&amp;gt;thumbnailator&amp;lt;/artifactId&amp;gt;
&amp;lt;version&amp;gt;0.4.8&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;!-- 图片缩略图 图片压缩 水印 end--&amp;gt;&lt;/pre&gt; 

 &lt;h1&gt;然后压缩和水印 只需要一行代码搞定&lt;/h1&gt; 
 &lt;pre&gt;package com.shallowmemory.test;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Positions;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
/**
* Created by HONGLINCHEN on 2017/10/31 11:00
* 图片压缩
* @author HONGLINCHEN
* @since JDK 1.8
*/
public class ImgCompress {
public static void main(String[] args) throws IOException {
//压缩图片 第一个参数是原图路径 后面那个路径是压缩以后的输出路径
Thumbnails.of(&amp;quot;C:\\Users\\HONGLINCHEN\\Desktop\\23.jpg&amp;quot;).size(600,600).outputQuality(0.8f).toFile(&amp;quot;C:\\Users\\HONGLINCHEN\\Desktop\\2.jpg&amp;quot;);
//给图片加水印
BufferedImage watermarkImage = ImageIO.read(new File(&amp;quot;C:\\Users\\HONGLINCHEN\\Desktop\\1.jpg&amp;quot;));
//第一个参数是水印的位置；第二个参数是水印图片的缓存数据；第三个参数是透明度。
Thumbnails.of(&amp;quot;C:\\Users\\HONGLINCHEN\\Desktop\\23.jpg&amp;quot;).scale(0.8).watermark(Positions.BOTTOM_RIGHT, watermarkImage, 0.5f).toFile(&amp;quot;C:\\Users\\HONGLINCHEN\\Desktop\\3.jpg&amp;quot;);
}
}&lt;/pre&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Oracle学习总结</category>
      <guid isPermaLink="true">https://itindex.net/detail/57642-java-google-thumbnailator</guid>
      <pubDate>Tue, 07 Nov 2017 16:37:50 CST</pubDate>
    </item>
    <item>
      <title>一个完整推荐系统的设计实现-以百度关键词搜索推荐为例</title>
      <link>https://itindex.net/detail/57636-%E5%AE%8C%E6%95%B4-%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F-%E8%AE%BE%E8%AE%A1</link>
      <description>&lt;div&gt;在之前一篇博文中， 有同学在评论中问了个问题： 如何解决因式分解带来的推荐冷门，热门关键词的问题。 在回答这个问题的时候， 想到了近几年在做搜索推荐系统的过程中， 学术界和工业界的一些区别。 正好最近正在做技术规划， 于是写偏文章说下工业界完整推荐系统的设计。  &lt;strong&gt;结论是： 没有某种算法能够完全解决问题， 多重算法+交互设计， 才能解决特定场景的需求&lt;/strong&gt;。下文也对之前的一些博文进行梳理，构成一个完整工业界推荐系统所具有的方方面面（主要以百度关键词搜索推荐系统为例）&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;完整的推荐系统肯定不会只用一种推荐算法&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;在学术界， 一般说到推荐引擎， 我们都是围绕着某一种单独的算法的效果优化进行的， 例如按内容推荐， 协同过滤（包括item-based, user-based, SVD分解等），上下文推荐，Constraint-based推荐，图关系挖掘等。 很多比较牛的单个算法， 就能在某个指标上取得较好效果， 例如MAE，RMSE。。。不过有自己的优点， 每种算法也有自己的缺点， 例如按内容推荐主要推荐和用户历史结果相似的item，一般的item-based容易推荐热门item（被更多人投票过）。。。。   所以在工业界，例如各互联网公司， 都会使用多种算法进行互相配合， 取长补短， 配合产品提升效果。而且  &lt;strong&gt;在完整的推荐系统中，不仅有传统的Rating推荐， 还需要辅以非常多的挖掘， Ranking来达到预期效果&lt;/strong&gt;。&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;推荐系统3大件：User Profile、基础挖掘推荐、Ranking&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;在实践中， 一个完整的推荐系统会主要由3部分组成：&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;ol&gt;
  &lt;li&gt;User Profile&lt;/li&gt;
  &lt;li&gt;基础推荐挖掘算法&lt;/li&gt;
  &lt;li&gt;Ranking&lt;/li&gt;
&lt;/ol&gt;
 &lt;div&gt;此处之所以将Ranking单独列出来，是因为其在推荐任务中过于重要，直接决定了推荐的效果。&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;以下为整个推荐的数据流：&lt;/div&gt;
 &lt;div&gt;  &lt;a href="http://www.semocean.com/wp-content/uploads/2017/11/&amp;#25512;&amp;#33616;&amp;#31995;&amp;#32479;&amp;#31574;&amp;#30053;&amp;#26550;&amp;#26500;.jpg"&gt;   &lt;img alt="" height="603" src="http://www.semocean.com/wp-content/uploads/2017/11/&amp;#25512;&amp;#33616;&amp;#31995;&amp;#32479;&amp;#31574;&amp;#30053;&amp;#26550;&amp;#26500;.jpg" width="895"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;  &lt;strong&gt;User Profile&lt;/strong&gt;&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;A user profile is a representation of information about an individual user that is essential for the (intelligent) application we are considering user profile主要是用户（注册）信息，以及对用户反馈的信息进行处理，聚合，用于描述用户的特征； 是后续推荐和排序的基石。 一般情况下，user profile会包含以下具体内容：&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;ol&gt;
  &lt;li&gt;用户兴趣数据&lt;/li&gt;
  &lt;li&gt;用户的基础注册信息，背景信息：例如用户出生地，年龄，性别，星座，职业等。这些信息一般从用户注册信息中获取；例如高德，百度地图注册用户，淘宝注册用户等&lt;/li&gt;
  &lt;li&gt;用户行为反馈：包括显示的反馈(explicit)和隐藏(implicit)的反馈，显示的反馈包括用户的评分，点赞等操作，百度关键词搜索推荐工具上的点赞（正向显示反馈）和垃圾桶（负向显示反馈），淘宝上的评分；隐式反馈包括用户的浏览行为，例如在百度关键词搜索推荐上搜过那些词，淘宝上点击了那些页面，在高德上点击了那些POI等&lt;/li&gt;
  &lt;li&gt;用户交互偏好：例如用户喜欢使用哪些入口，喜欢哪些操作，以及从这些操作中分析出来的偏好，比如在高德地图上根据用户行为反馈分析出来的用户对美食的偏好：更喜欢火锅，粤菜，还是快餐&lt;/li&gt;
  &lt;li&gt;用户上下文信息：这些信息有些是分析出来的，例如在LBS中分析出来的用户的家在哪儿，公司在哪儿，经常活动的商圈，经常使用的路线等&lt;/li&gt;
&lt;/ol&gt;
 &lt;div&gt;user profile经常是一份维护好的数据，在使用的时候，会直接使用该数据，或是将该数据存储在KV系统中，供Online系统实时使用。 在搜索或是推荐的场景下，每次请求一般只会涉及到一次user profile的KV请求，所以online使用的时候，主要的实现困难是存储，以及快速KV的快速响应。&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;  &lt;strong&gt;基础挖掘推荐算法&lt;/strong&gt;&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;基础挖掘推荐算法， 主要使用传统推荐算法， 结合分析的item profile和user profile， 建立user和item的关系，此时并不会过多考虑其他因素，例如是否冷门/热门，最主要的就是建立user和item的关系。 在各种论文中狭义的推荐，主要就是指该部分内容。 主要围绕着Rating，以及Top N进行该处的Top N（更像是直接Rating值最高的Top N） 传统的推荐算法研究主要围着这块工作进行，现在已经有很多比较成熟的算法，这些算法相关的研究可参见博文：《  &lt;a href="http://semocean.com/%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F%E7%BB%8F%E5%85%B8%E8%AE%BA%E6%96%87%E6%96%87%E7%8C%AE%E5%8F%8A%E8%B5%84%E6%96%99/"&gt;推荐系统经典论文文献及资料&lt;/a&gt;》；其中也能找到业界较多成功推荐系统的实践分享 主要包含以下几类：&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;ol&gt;
  &lt;li&gt;Content Based推荐： 按内容推荐，主要的工作是user profile, item profile的提取和维护，然后研究各种相似度度量方法（具体相似度度量参见博文：《   &lt;a href="http://semocean.com/%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F%E4%B8%AD%E7%9A%84%E7%9B%B8%E4%BC%BC%E5%BA%A6%E5%BA%A6%E9%87%8F/"&gt;推荐系统中的相似度度量&lt;/a&gt;》）&lt;/li&gt;
  &lt;li&gt;协同过滤：相当于应用了用户的行为进行推荐（区别于Content based算法），比较经典的算法包括传统的item-based/user-based算法（参见博文：《   &lt;a href="http://semocean.com/%E5%8D%8F%E5%90%8C%E8%BF%87%E6%BB%A4%E4%B8%ADitem-based%E4%B8%8Euser-based%E9%80%89%E6%8B%A9%E4%BE%9D%E6%8D%AE/"&gt;协同过滤中item-based与user-based选择依据&lt;/a&gt;》，《   &lt;a href="http://semocean.com/collaborative-filtering%E6%A0%B9%E6%8D%AE%E8%BF%91%E9%82%BB%E6%8E%A8%E8%8D%90%E6%97%B6%E9%9C%80%E8%A6%81%E8%80%83%E8%99%91%E7%9A%843%E8%A6%81%E7%B4%A0/"&gt;collaborative-filtering根据近邻推荐时需要考虑的3要素&lt;/a&gt;》），SVD，SVD++(具体原理及源码参见博文：《   &lt;a href="http://semocean.com/%E5%9B%A0%E5%BC%8F%E5%88%86%E8%A7%A3%E5%AE%9E%E7%8E%B0%E5%8D%8F%E5%90%8C%E8%BF%87%E6%BB%A4-%E5%8F%8A%E6%BA%90%E7%A0%81%E5%AE%9E%E7%8E%B0/"&gt;SVD因式分解实现协同过滤-及源码实现&lt;/a&gt;》)&lt;/li&gt;
  &lt;li&gt;上下文相关推荐：和传统推荐相比， 考虑更多上下文因素，LBS， 移动场景下使用比较多（具体参见博文：《   &lt;a href="http://semocean.com/context-aware-recommendation/"&gt;context-aware-recommendation&lt;/a&gt;》）&lt;/li&gt;
  &lt;li&gt;基于图的关系挖掘推荐：主要是利用图论原理，根据item,user之间的数据，反馈关联关系，挖掘更深层次的关系进行推荐，该类方法一般效果都不错，当然资源要求也较高。具体参见博文：《   &lt;a href="http://semocean.com/%E7%BA%A7%E8%81%94%E4%BA%8C%E6%AD%A5%E5%9B%BE%E5%85%B3%E7%B3%BB%E6%8C%96%E6%8E%98%E5%85%B3%E9%94%AE%E8%AF%8D%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F/"&gt;级联二步图关系挖掘关键词推荐系统&lt;/a&gt;》，《   &lt;a href="http://semocean.com/%E9%A2%91%E7%B9%81%E4%BA%8C%E9%A1%B9%E9%9B%86%E5%90%88%E7%9A%84hadoop%E5%AE%9E%E7%8E%B0/"&gt;频繁二项集合的hadoop实现&lt;/a&gt;》《   &lt;a href="http://semocean.com/itemrankrandom-walk-based-scoring-algorithm-for-recommener-system/"&gt;itemrankrandom-walk-based-scoring-algorithm-for-recommener-system&lt;/a&gt;》&lt;/li&gt;
  &lt;li&gt;Constrainted-based推荐：根据限制性条件进行演绎推荐&lt;/li&gt;
&lt;/ol&gt;
 &lt;div&gt;在实际应用时，我们经常使用按内容推荐，item-based寻找从感知的角度比较靠谱的结果，使用SVD,SVD++，图关系寻找更深层次的联系结果。同时在推荐时，会结合很多因素来进行综合排序，例如关键词， 或是LBS中POI的热度等。具体可参见下文ranking部分。&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;  &lt;strong&gt;算法效果衡量&lt;/strong&gt;&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;以上这些算法， 我们在离线的时候，使用Cross-Validation方式，就可以分析出其效果，而且离线分析的时候，代价比较小，比较容易操作。当然，对于不同的问题会使用对应的指标进行衡量。 对于预测Rating准确性主要是用RMSE，或是MAE；具体可参见博文：《  &lt;a href="http://semocean.com/%E5%85%B3%E9%94%AE%E8%AF%8D%E6%90%9C%E7%B4%A2%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F%E4%B8%AD%E7%9A%84%E6%8E%A8%E8%8D%90%E5%87%86%E7%A1%AE%E6%80%A7%E5%BA%A6%E9%87%8F/"&gt;关键词搜索推荐系统中的推荐准确性度量&lt;/a&gt;》 如果是排序， 则更多使用NDCG，MAP,  MRR等指标；具体可参见博文：《  &lt;a href="http://semocean.com/%E4%BD%BF%E7%94%A8ndcg%E8%AF%84%E4%BC%B0%E5%85%B3%E9%94%AE%E8%AF%8D%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F%E7%9A%84%E7%9B%B8%E5%85%B3%E6%80%A7/"&gt;使用ndcg评估关键词推荐系统的相关性&lt;/a&gt;》 在具体应用场景中，对于特定推荐问题，会涉及到选用哪种算法的问题。推荐不像CTR预估这样的问题，目标比较单一，经常我们需要考虑多个指标，而且这些指标可能此消彼长，需要做权衡，例如需要考虑算法的准确性(accuracy)，同时也需要考虑算法的覆盖(coverage)，置信度（confidence）,新鲜度(novelty)和惊喜度(Serendipity)，同时还需要考虑推荐为系统带来的收益和效用(utility)。 这些指标经常需要权衡，而且经常提升某一个的时候会导致其它下降，所以有时候存在一定的主观性：我们到底看中哪一个指标？  而且这个问题可能随着系统，平台所处的阶段而不同。 例如在建立口碑的时候，我们可能不太关注coverage，而更关注accuracy，因为要让用户建立一种：该系统很准的认知；如果在系统已经比较成熟了，此时可能需要考虑novelty, serendipity的同时，还需要考虑utility：该推荐能为系统带来什么收益，例如对百度的变现有多大收益？ 对淘宝的销售有多少收益等 具体这些指标的选择可参见博文：《  &lt;a href="http://semocean.com/%E9%80%89%E6%8B%A9%E6%8E%A8%E8%8D%90%E7%AE%97%E6%B3%95%E6%97%B6%E9%9C%80%E8%A6%81%E8%80%83%E8%99%91%E5%BE%97%E5%9B%A0%E7%B4%A0/"&gt;选择推荐算法时需要考虑得因素&lt;/a&gt;》&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;  &lt;strong&gt;Ranking，此部分是成熟的搜索，推荐系统具有的核心逻辑&lt;/strong&gt;&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;比较简单的实现方法， 是直接对各种特征拍阈值进行线性加权，比较成熟的系统一般会使用机器学习的方式和综合个维特征， 学习出模型后进行排序， 例如使用Learning to rank技术。 该部分需要考虑的因素较多较为复杂。 和传统的推荐相比， 此处单独将Ranking拿出来。 基础推荐挖掘， 和传统的推荐部分比较类似，主要结合user profile， 挖掘哪些item适合推给哪些user。 但仅根据这些挖掘就直接进行推荐是不够的。 真实online推荐场景中， 需要考虑更多其他因素， 例如：相关性，推荐的上下文，CTR预估，以及商业业务规则。&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;ol&gt;
  &lt;li&gt;相关性： item与用户的相关性，这是大多数搜索和推荐任务的基石，例如在搜索中判定一个query和一个document的相关性，或是一个query 和 另一个query的相关性，或是在特征比较多的情况下， 一个user 和一个item 记录的相关性；实现方式可以很简单，例如传统的相似度度量方式（参见博文：《   &lt;a href="http://semocean.com/%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F%E4%B8%AD%E7%9A%84%E7%9B%B8%E4%BC%BC%E5%BA%A6%E5%BA%A6%E9%87%8F/"&gt;推荐系统中的相似度度量&lt;/a&gt;》），对于文本，业界使用简单的TF*IDF，或是BM25； 不过很多时候我们需要增加更多维度特征，包括推荐item本身的重要性，例如IDF，Pagerank(具体参见博文：《   &lt;a href="http://semocean.com/pagerank%E7%9A%84%E7%BB%8F%E6%B5%8E%E5%AD%A6%E6%95%88%E7%94%A8%E8%A7%A3%E9%87%8A/"&gt;pagerank的经济学效用解释&lt;/a&gt;》)，同时使用模型来提升相关性判断的准确性。使用模型的方式会更加复杂，但效果提升也非常明显。具体可参见博文：《   &lt;a href="http://semocean.com/%E9%9B%86%E6%88%90%E6%A0%91%E7%B1%BB%E6%A8%A1%E5%9E%8B%E5%8F%8A%E5%85%B6%E5%9C%A8%E6%90%9C%E7%B4%A2%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/"&gt;集成树类模型及其在搜索推荐系统中的应用&lt;/a&gt;》，《   &lt;a href="http://semocean.com/%E5%88%86%E7%B1%BB%E6%A8%A1%E5%9E%8B%E5%9C%A8%E5%85%B3%E9%94%AE%E8%AF%8D%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/"&gt;分类模型在关键词推荐系统中的应用&lt;/a&gt;》，《   &lt;a href="http://semocean.com/adaboost/"&gt;adaboost&lt;/a&gt;》&lt;/li&gt;
  &lt;li&gt;推荐的上下文：例如推荐产品的入口，交互方式， 不同的入口，甚至同一入口的不同交互方式， 推荐的结果有可能都需要不一样； 在LBS生活服务中， 请求发生的时间， 地点也是推荐需要重点考虑的上下文因素，例如饭点对餐饮item的提权； 异地情况下对酒店等结果的加权等&lt;/li&gt;
  &lt;li&gt;CTR预估：成熟的商业系统都会使用模型来完成CTR预估，或是转化预估&lt;/li&gt;
  &lt;li&gt;以及商业业务规则：例如黑白名单，或者强制调权。例如在百度关键词搜索推荐中，某些有比较高变现潜力的词， 就应该加权往前排； 比如在高德LBS服务中，有些海底捞的店点评评分较低， 但我们也应该往前排；或是在搜索引擎中，搜国家领导人的名字， 有些最相关的结果可能因为法律因素是需要屏蔽的&lt;/li&gt;
&lt;/ol&gt;
 &lt;div&gt;  &lt;strong&gt;算法评估&lt;/strong&gt;&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;很直接，离线调研的时候看就看算法的评估指标，参见博文：《  &lt;a href="http://semocean.com/%E5%85%B3%E9%94%AE%E8%AF%8D%E6%90%9C%E7%B4%A2%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F%E4%B8%AD%E7%9A%84%E6%8E%A8%E8%8D%90%E5%87%86%E7%A1%AE%E6%80%A7%E5%BA%A6%E9%87%8F/"&gt;关键词搜索推荐系统中的推荐准确性度量&lt;/a&gt;》，《  &lt;a href="http://semocean.com/%E4%BD%BF%E7%94%A8ndcg%E8%AF%84%E4%BC%B0%E5%85%B3%E9%94%AE%E8%AF%8D%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F%E7%9A%84%E7%9B%B8%E5%85%B3%E6%80%A7/"&gt;使用ndcg评估关键词推荐系统的相关性&lt;/a&gt;》 上线的时候，进行圈用户（圈定某两个user集合作为实验/对照用户组）实验， 或者圈请求实验（例如随机圈定5%流量进行实验），之后根据系统效果监控中的指标值判断实验效果。以下为一个典型的效果监控截图： 实验如果证明成功，达到预期效果，一般之后推广到全流量；反之，如果实验未达到预期效果，则需要分析什么地方有问题，如何改进，之后继续调整算法继续实验。当实验较多时，还会涉及较多工程问题，例如分层实验框架等。&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;  &lt;strong&gt;系统效果监控&lt;/strong&gt;&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;对于整个系统，需要建立晚上的效果监控平台进行效果的实时监控，以便发现用户的行为模型，系统的不足，分析后续的发力点等。一般这样的监控平台会使用Dashboard来完成，基本的框架是前段UI + 后端数据库。很多时候，离线统计策略在hadoop上处理统计日志计算指标，并将计算出来的指标存入数据库，前端UI访问数据库，拉出指定时间段内某些指标的值，并进行简单分析。 具体的监控指标，及指标体系的建立，可参见博文：《  &lt;a href="http://semocean.com/%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E%E5%8F%98%E7%8E%B0%E7%AD%96%E7%95%A5%E6%8C%87%E6%A0%87%E4%BD%93%E7%B3%BB/"&gt;搜索引擎变现策略指标体系&lt;/a&gt;》&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;  &lt;strong&gt;交互设计&lt;/strong&gt;&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;完整的产品包括便捷的交互和背后牛叉的算法。很多时候，要提升推荐的效果，需要算法和交互配合，才能达到理想的效果： 交互需要有健壮的算法产出结果；而算法也需要有配套的交互，才能达到预期效果，否则再牛叉的算法，对结果的影响也可能没那么明显。&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;一些交互的例子参见博文：&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;《  &lt;a href="http://semocean.com/%E5%85%B3%E9%94%AE%E8%AF%8D%E6%8E%A8%E8%8D%90%E5%B7%A5%E5%85%B7%E4%B8%AD%E7%9A%84%E7%94%A8%E6%88%B7%E5%BC%95%E5%AF%BC%E6%9C%BA%E5%88%B6/"&gt;关键词推荐工具中的用户引导机制之一：总述&lt;/a&gt;》&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;《  &lt;a href="http://semocean.com/%E5%85%B3%E9%94%AE%E8%AF%8D%E6%8E%A8%E8%8D%90%E5%B7%A5%E5%85%B7%E4%B8%AD%E7%9A%84%E7%94%A8%E6%88%B7%E5%BC%95%E5%AF%BC%E6%9C%BA%E5%88%B6%E4%B9%8B%E4%BA%8C%EF%BC%9Asuggestion%E6%9E%B6%E6%9E%84/"&gt;关键词推荐工具中的用户引导机制之二：suggestion架构&lt;/a&gt;》&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;《  &lt;a href="http://semocean.com/%E5%85%B3%E9%94%AE%E8%AF%8D%E6%8E%A8%E8%8D%90%E5%B7%A5%E5%85%B7%E4%B8%AD%E7%9A%84%E7%94%A8%E6%88%B7%E5%BC%95%E5%AF%BC%E6%9C%BA%E5%88%B6%E4%B9%8B%E4%B8%89%EF%BC%9A%E7%9B%B8%E5%85%B3%E6%90%9C%E7%B4%A2qu/"&gt;关键词推荐工具中的用户引导机制之三：相关搜索query技术&lt;/a&gt;》&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;《  &lt;a href="http://semocean.com/%E5%85%B3%E9%94%AE%E8%AF%8D%E6%8E%A8%E8%8D%90%E5%B7%A5%E5%85%B7%E4%B8%AD%E7%9A%84%E7%94%A8%E6%88%B7%E5%BC%95%E5%AF%BC%E6%9C%BA%E5%88%B6%E4%B9%8B%E5%9B%9B%EF%BC%9A%E7%A7%8D%E5%AD%90query%E6%8E%A8/"&gt;关键词推荐工具中的用户引导机制之四：种子query推荐&lt;/a&gt;》&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;说了那么多，中心就是想说明， 一个完整的推荐系统，远远不止是一两个rating算法能够覆盖的，而且此处还未涉及工程部分。&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;更多内容，也可直接访问： http://semocean.com&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>产品 推荐系统 搜索引擎 数据挖掘 机器学习</category>
      <guid isPermaLink="true">https://itindex.net/detail/57636-%E5%AE%8C%E6%95%B4-%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F-%E8%AE%BE%E8%AE%A1</guid>
      <pubDate>Wed, 17 Sep 2014 22:42:45 CST</pubDate>
    </item>
    <item>
      <title>用神经网络训练一个文本分类器</title>
      <link>https://itindex.net/detail/57344-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C-%E8%AE%AD%E7%BB%83-%E6%96%87%E6%9C%AC</link>
      <description>&lt;p&gt;理解聊天机器人的工作原理是非常重要的。聊天机器人内部一个非常重要的组件就是文本分类器。我们看一下文本分类器的神经网络（ANN）的内部工作原理。&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://jbcdn2.b0.upaiyun.com/2017/08/3f0fc87336415b5dec8a04dc523db2ac.png"&gt;   &lt;img alt="1-DpMaU1p85ZSgamwYDkzL-A" src="http://jbcdn2.b0.upaiyun.com/2017/08/3f0fc87336415b5dec8a04dc523db2ac.png"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;多层神经网络&lt;/p&gt;
 &lt;p&gt;我们将会使用2层网络（1个隐层）和一个“词包”的方法来组织我们的训练数据。文本分类有3个特点：模式匹配、算法、神经网络。虽然使用多项朴素贝叶斯算法的方法非常有效，但是它有3个致命的缺陷：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;这个算法输出一个分数而不是一个概率。我们可以使用概率来忽略特定阈值以下的预测结果。这类似于忽略收音机中的噪声。&lt;/li&gt;
  &lt;li&gt;这个算法从一个样本中学习一个分类中包含什么，而不是一个分类中不包含什么。一个分类中不包含什么的的学习模式往往也很重要。&lt;/li&gt;
  &lt;li&gt;不成比例的大训练集的分类将会导致扭曲的分类分数，迫使算法相对于分类规模来调整输出分数，这并不理想。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;和它“天真”的对手一样，这种分类器并不试图去理解句子的含义，而仅仅对它进行分类。事实上，所谓的“人工智能聊天机器人”并不理解语言，但那是  &lt;a href="https://medium.com/@gk_/the-ai-label-is-bullshit-559b171867ff#.cqbwy3eb7"&gt;另一个故事&lt;/a&gt;。&lt;/p&gt;
 &lt;p&gt;如果你刚接触人工神经网络，这是它的  &lt;a href="https://medium.com/@gk_/how-neural-networks-work-ff4c7ad371f7"&gt;工作原理&lt;/a&gt;。&lt;/p&gt;
 &lt;p&gt;理解分类算法，请看  &lt;a href="https://chatbotslife.com/text-classification-using-algorithms-e4d50dcba45"&gt;这里&lt;/a&gt;。&lt;/p&gt;
 &lt;p&gt;我们来逐个分析文本分类器的每个部分。我们将按照以下顺序：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;   &lt;strong&gt;引用需要的库&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;提供训练集&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;整理数据&lt;/li&gt;
  &lt;li&gt;迭代：编写代码+测试预测结果+调整模型&lt;/li&gt;
  &lt;li&gt;抽象&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;代码在这里，我们使用ipython notebook这个在数据科学项目上非常高效的工具。代码语法是python。&lt;/p&gt;
 &lt;p&gt;我们首先导入自然语言工具包。我们需要一个可靠的方法将句子切分成词并且将单词词干化处理。&lt;/p&gt; &lt;pre&gt;# use natural language toolkit
import nltk
from nltk.stem.lancaster import LancasterStemmer
import os
import json
import datetime
stemmer = LancasterStemmer()&lt;/pre&gt; &lt;p&gt;下面是我们的训练集，12个句子属于3个类别（“意图”）。&lt;/p&gt; &lt;pre&gt;# 3 classes of training data
training_data = []
training_data.append({&amp;quot;class&amp;quot;:&amp;quot;greeting&amp;quot;, &amp;quot;sentence&amp;quot;:&amp;quot;how are you?&amp;quot;})
training_data.append({&amp;quot;class&amp;quot;:&amp;quot;greeting&amp;quot;, &amp;quot;sentence&amp;quot;:&amp;quot;how is your day?&amp;quot;})
training_data.append({&amp;quot;class&amp;quot;:&amp;quot;greeting&amp;quot;, &amp;quot;sentence&amp;quot;:&amp;quot;good day&amp;quot;})
training_data.append({&amp;quot;class&amp;quot;:&amp;quot;greeting&amp;quot;, &amp;quot;sentence&amp;quot;:&amp;quot;how is it going today?&amp;quot;})

training_data.append({&amp;quot;class&amp;quot;:&amp;quot;goodbye&amp;quot;, &amp;quot;sentence&amp;quot;:&amp;quot;have a nice day&amp;quot;})
training_data.append({&amp;quot;class&amp;quot;:&amp;quot;goodbye&amp;quot;, &amp;quot;sentence&amp;quot;:&amp;quot;see you later&amp;quot;})
training_data.append({&amp;quot;class&amp;quot;:&amp;quot;goodbye&amp;quot;, &amp;quot;sentence&amp;quot;:&amp;quot;have a nice day&amp;quot;})
training_data.append({&amp;quot;class&amp;quot;:&amp;quot;goodbye&amp;quot;, &amp;quot;sentence&amp;quot;:&amp;quot;talk to you soon&amp;quot;})

training_data.append({&amp;quot;class&amp;quot;:&amp;quot;sandwich&amp;quot;, &amp;quot;sentence&amp;quot;:&amp;quot;make me a sandwich&amp;quot;})
training_data.append({&amp;quot;class&amp;quot;:&amp;quot;sandwich&amp;quot;, &amp;quot;sentence&amp;quot;:&amp;quot;can you make a sandwich?&amp;quot;})
training_data.append({&amp;quot;class&amp;quot;:&amp;quot;sandwich&amp;quot;, &amp;quot;sentence&amp;quot;:&amp;quot;having a sandwich today?&amp;quot;})
training_data.append({&amp;quot;class&amp;quot;:&amp;quot;sandwich&amp;quot;, &amp;quot;sentence&amp;quot;:&amp;quot;what&amp;apos;s for lunch?&amp;quot;})
print (&amp;quot;%s sentences in training data&amp;quot; % len(training_data))&lt;/pre&gt; &lt;p&gt;&lt;/p&gt; &lt;pre&gt;12 sentences in training data&lt;/pre&gt; &lt;p&gt;现在我们可以将数据结构组织为：  &lt;em&gt;documents&lt;/em&gt;,   &lt;em&gt;classes&lt;/em&gt; 和  &lt;em&gt;words&lt;/em&gt;.&lt;/p&gt; &lt;pre&gt;words = []
classes = []
documents = []
ignore_words = [&amp;apos;?&amp;apos;]
# loop through each sentence in our training data
for pattern in training_data:
    # tokenize each word in the sentence
    w = nltk.word_tokenize(pattern[&amp;apos;sentence&amp;apos;])
    # add to our words list
    words.extend(w)
    # add to documents in our corpus
    documents.append((w, pattern[&amp;apos;class&amp;apos;]))
    # add to our classes list
    if pattern[&amp;apos;class&amp;apos;] not in classes:
        classes.append(pattern[&amp;apos;class&amp;apos;])

# stem and lower each word and remove duplicates
words = [stemmer.stem(w.lower()) for w in words if w not in ignore_words]
words = list(set(words))

# remove duplicates
classes = list(set(classes))

print (len(documents), &amp;quot;documents&amp;quot;)
print (len(classes), &amp;quot;classes&amp;quot;, classes)
print (len(words), &amp;quot;unique stemmed words&amp;quot;, words)&lt;/pre&gt; &lt;p&gt;&lt;/p&gt; &lt;pre&gt;12 documents
3 classes [&amp;apos;greeting&amp;apos;, &amp;apos;goodbye&amp;apos;, &amp;apos;sandwich&amp;apos;]
26 unique stemmed words [&amp;apos;sandwich&amp;apos;, &amp;apos;hav&amp;apos;, &amp;apos;a&amp;apos;, &amp;apos;how&amp;apos;, &amp;apos;for&amp;apos;, &amp;apos;ar&amp;apos;, &amp;apos;good&amp;apos;, &amp;apos;mak&amp;apos;, &amp;apos;me&amp;apos;, &amp;apos;it&amp;apos;, &amp;apos;day&amp;apos;, &amp;apos;soon&amp;apos;, &amp;apos;nic&amp;apos;, &amp;apos;lat&amp;apos;, &amp;apos;going&amp;apos;, &amp;apos;you&amp;apos;, &amp;apos;today&amp;apos;, &amp;apos;can&amp;apos;, &amp;apos;lunch&amp;apos;, &amp;apos;is&amp;apos;, &amp;quot;&amp;apos;s&amp;quot;, &amp;apos;see&amp;apos;, &amp;apos;to&amp;apos;, &amp;apos;talk&amp;apos;, &amp;apos;yo&amp;apos;, &amp;apos;what&amp;apos;]&lt;/pre&gt; &lt;p&gt;注意每个单词都是词根并且小写。词根有助于机器将“have”和“having”等同起来。同时我们也不关心大小写。  &lt;a href="http://jbcdn2.b0.upaiyun.com/2017/08/339dcac37e068d2783eff6ec4e21eb24.png"&gt;   &lt;img alt="1-eUedufAl7_sI_QWSEIstZg" src="http://jbcdn2.b0.upaiyun.com/2017/08/339dcac37e068d2783eff6ec4e21eb24.png"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;我们将训练集中的每个句子转换为词包。&lt;/p&gt; &lt;pre&gt;# create our training data
training = []
output = []
# create an empty array for our output
output_empty = [0] * len(classes)

# training set, bag of words for each sentence
for doc in documents:
    # initialize our bag of words
    bag = []
    # list of tokenized words for the pattern
    pattern_words = doc[0]
    # stem each word
    pattern_words = [stemmer.stem(word.lower()) for word in pattern_words]
    # create our bag of words array
    for w in words:
        bag.append(1) if w in pattern_words else bag.append(0)

    training.append(bag)
    # output is a &amp;apos;0&amp;apos; for each tag and &amp;apos;1&amp;apos; for current tag
    output_row = list(output_empty)
    output_row[classes.index(doc[1])] = 1
    output.append(output_row)

# sample training/output
i = 0
w = documents[i][0]
print ([stemmer.stem(word.lower()) for word in w])
print (training[i])
print (output[i])&lt;/pre&gt; &lt;p&gt;&lt;/p&gt;

 &lt;div&gt;&lt;/div&gt;

 &lt;p&gt;&lt;/p&gt; &lt;pre&gt;[&amp;apos;how&amp;apos;, &amp;apos;ar&amp;apos;, &amp;apos;you&amp;apos;, &amp;apos;?&amp;apos;]
[0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 0]&lt;/pre&gt; &lt;p&gt;上面的步骤是文本分类中的一个经典步骤：每个训练句子被转化为一个包含0和1的数组，而不是语料库中包含独特单词的数组。&lt;/p&gt; &lt;pre&gt;[&amp;apos;how&amp;apos;, &amp;apos;are&amp;apos;, &amp;apos;you&amp;apos;, &amp;apos;?&amp;apos;]&lt;/pre&gt; &lt;p&gt;被词干化为：&lt;/p&gt; &lt;pre&gt;[&amp;apos;how&amp;apos;, &amp;apos;ar&amp;apos;, &amp;apos;you&amp;apos;, &amp;apos;?&amp;apos;]&lt;/pre&gt; &lt;p&gt;然后转换为输入词包的形式：  &lt;em&gt;1&lt;/em&gt;代表单词存在于词包中（忽略问号？）&lt;/p&gt; &lt;pre&gt;[0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]&lt;/pre&gt; &lt;p&gt;输出：第一类&lt;/p&gt; &lt;pre&gt;[1, 0, 0]&lt;/pre&gt; &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;注意：一个句子可以有多个分类，也可以没有。确保理解上面的内容，仔细阅读代码直到你理解它。&lt;/p&gt;
 &lt;h4&gt;机器学习的第一步是要有干净的数据&lt;/h4&gt;
 &lt;p&gt;  &lt;a href="http://jbcdn2.b0.upaiyun.com/2017/08/0f3abe8c32b873ad8ffa8c728a59102d.png"&gt;   &lt;img alt="1-CcQPggEbLgej32mVF2lalg" src="http://jbcdn2.b0.upaiyun.com/2017/08/0f3abe8c32b873ad8ffa8c728a59102d.png"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;

 &lt;div&gt;&lt;/div&gt;

 &lt;p&gt;接下来我们的学习2层神经网络的核心功能。&lt;/p&gt;
 &lt;p&gt;如果你是人工神经网络新手，这里是它的工作原理&lt;/p&gt;
 &lt;p&gt;我们使用  &lt;a href="http://www.numpy.org/"&gt;numpy&lt;/a&gt;，原因是它可以提供快速的矩阵乘法运算。&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://jbcdn2.b0.upaiyun.com/2017/08/f397d71e69f5ebeaf6a3bf0522861d69.png"&gt;   &lt;img alt="1-8SJcWjxz8j7YtY6K-DWxKw" src="http://jbcdn2.b0.upaiyun.com/2017/08/f397d71e69f5ebeaf6a3bf0522861d69.png"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;

 &lt;div&gt;&lt;/div&gt;

 &lt;p&gt;我们使用sigmoid函数对值进行归一化，用其导数来衡量错误率。通过不断迭代和调整，直到错误率低到一个可以接受的值。&lt;/p&gt;
 &lt;p&gt;下面我们也实现了bag-of-words函数，将输入的一个句子转化为一个包含0和1的数组。这就是转换训练数据，得到正确的转换数据至关重要。&lt;/p&gt; &lt;pre&gt;import numpy as np
import time

# compute sigmoid nonlinearity
def sigmoid(x):
    output = 1/(1+np.exp(-x))
    return output

# convert output of sigmoid function to its derivative
def sigmoid_output_to_derivative(output):
    return output*(1-output)

def clean_up_sentence(sentence):
    # tokenize the pattern
    sentence_words = nltk.word_tokenize(sentence)
    # stem each word
    sentence_words = [stemmer.stem(word.lower()) for word in sentence_words]
    return sentence_words

# return bag of words array: 0 or 1 for each word in the bag that exists in the sentence
def bow(sentence, words, show_details=False):
    # tokenize the pattern
    sentence_words = clean_up_sentence(sentence)
    # bag of words
    bag = [0]*len(words)  
    for s in sentence_words:
        for i,w in enumerate(words):
            if w == s: 
                bag[i] = 1
                if show_details:
                    print (&amp;quot;found in bag: %s&amp;quot; % w)

    return(np.array(bag))

def think(sentence, show_details=False):
    x = bow(sentence.lower(), words, show_details)
    if show_details:
        print (&amp;quot;sentence:&amp;quot;, sentence, &amp;quot;n bow:&amp;quot;, x)
    # input layer is our bag of words
    l0 = x
    # matrix multiplication of input and hidden layer
    l1 = sigmoid(np.dot(l0, synapse_0))
    # output layer
    l2 = sigmoid(np.dot(l1, synapse_1))
    return l2&lt;/pre&gt; &lt;p&gt;现在我们对神经网络训练函数进行编码，创造连接权重。别太激动，这主要是矩阵乘法——来自中学数学课堂。&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://jbcdn2.b0.upaiyun.com/2017/02/e0e648def4862d31563d98fa1d9e60a4.jpg"&gt;   &lt;img alt="2017-06-03_151025" src="http://jbcdn2.b0.upaiyun.com/2017/02/e0e648def4862d31563d98fa1d9e60a4.jpg"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;我们现在准备去构建我们的神经网络  &lt;em&gt;模型&lt;/em&gt;，我们将连接权重保存为json文件。&lt;/p&gt;
 &lt;p&gt;你应该尝试不同的“α”（梯度下降参数），看看它是如何影响错误率。此参数有助于错误调整，并找到最低错误率：&lt;/p&gt;
 &lt;p&gt;synapse_0 +=   &lt;strong&gt;alpha&lt;/strong&gt; * synapse_0_weight_update&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://jbcdn2.b0.upaiyun.com/2017/08/41e84709aaf122cd41527923403b8e99.png"&gt;   &lt;img alt="1-HZ-YQpdBM4hDbh4Q5FcsMA" src="http://jbcdn2.b0.upaiyun.com/2017/08/41e84709aaf122cd41527923403b8e99.png"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;我们在隐藏层使用了20个神经元，你可以很容易地调整。这些参数将随着于您的训练数据规模的不同而不同，将错误率调整到低于10 ^ – 3是比较合理的。&lt;/p&gt; &lt;pre&gt;X = np.array(training)
y = np.array(output)

start_time = time.time()

train(X, y, hidden_neurons=20, alpha=0.1, epochs=100000, dropout=False, dropout_percent=0.2)

elapsed_time = time.time() - start_time
print (&amp;quot;processing time:&amp;quot;, elapsed_time, &amp;quot;seconds&amp;quot;)&lt;/pre&gt; &lt;p&gt;&lt;/p&gt; &lt;pre&gt;Training with 20 neurons, alpha:0.1, dropout:False 
Input matrix: 12x26    Output matrix: 1x3
delta after 10000 iterations:0.0062613597435
delta after 20000 iterations:0.00428296074919
delta after 30000 iterations:0.00343930779307
delta after 40000 iterations:0.00294648034566
delta after 50000 iterations:0.00261467859609
delta after 60000 iterations:0.00237219554105
delta after 70000 iterations:0.00218521899378
delta after 80000 iterations:0.00203547284581
delta after 90000 iterations:0.00191211022401
delta after 100000 iterations:0.00180823798397
saved synapses to: synapses.json
processing time: 6.501226902008057 seconds&lt;/pre&gt; &lt;p&gt;synapse.json文件中包含了全部的连接权重，  &lt;strong&gt;这就是我们的模型&lt;/strong&gt;。&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://jbcdn2.b0.upaiyun.com/2017/08/8b07f3b20cfc25210fcad980aebab930.jpeg"&gt;   &lt;img alt="1-qYkCgPE3DD26VD-qDwsicA" src="http://jbcdn2.b0.upaiyun.com/2017/08/8b07f3b20cfc25210fcad980aebab930.jpeg"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;一旦连接权重已经计算完成，对于分类来说只需要classify()函数了：大约15行代码&lt;/p&gt;
 &lt;p&gt;备注：如果训练集有变化，我们的模型需要重新计算。对于非常大的数据集，这需要较长的时间。&lt;/p&gt;
 &lt;p&gt;现在我们可以生成一个句子属于一个或者多个分类的概率了。它的速度非常快，这是因为我们之前定义的think()函数中的点积运算。&lt;/p&gt; &lt;pre&gt;# probability threshold
ERROR_THRESHOLD = 0.2
# load our calculated synapse values
synapse_file = &amp;apos;synapses.json&amp;apos; 
with open(synapse_file) as data_file: 
    synapse = json.load(data_file) 
    synapse_0 = np.asarray(synapse[&amp;apos;synapse0&amp;apos;]) 
    synapse_1 = np.asarray(synapse[&amp;apos;synapse1&amp;apos;])

def classify(sentence, show_details=False):
    results = think(sentence, show_details)

    results = [[i,r] for i,r in enumerate(results) if r&amp;gt;ERROR_THRESHOLD ] 
    results.sort(key=lambda x: x[1], reverse=True) 
    return_results =[[classes[r[0]],r[1]] for r in results]
    print (&amp;quot;%s n classification: %s&amp;quot; % (sentence, return_results))
    return return_results

classify(&amp;quot;sudo make me a sandwich&amp;quot;)
classify(&amp;quot;how are you today?&amp;quot;)
classify(&amp;quot;talk to you tomorrow&amp;quot;)
classify(&amp;quot;who are you?&amp;quot;)
classify(&amp;quot;make me some lunch&amp;quot;)
classify(&amp;quot;how was your lunch today?&amp;quot;)
print()
classify(&amp;quot;good day&amp;quot;, show_details=True)&lt;/pre&gt; &lt;p&gt;&lt;/p&gt; &lt;pre&gt;  &lt;strong&gt;sudo make me a sandwich &lt;/strong&gt;
 [[&amp;apos;sandwich&amp;apos;, 0.99917711814437993]]
  &lt;strong&gt;how are you today? &lt;/strong&gt;
 [[&amp;apos;greeting&amp;apos;, 0.99864563257858363]]
  &lt;strong&gt;talk to you tomorrow &lt;/strong&gt;
 [[&amp;apos;goodbye&amp;apos;, 0.95647479275905511]]
  &lt;strong&gt;who are you? &lt;/strong&gt;
 [[&amp;apos;greeting&amp;apos;, 0.8964283843977312]]
  &lt;strong&gt;make me some lunch&lt;/strong&gt; 
 [[&amp;apos;sandwich&amp;apos;, 0.95371924052636048]]
  &lt;strong&gt;how was your lunch today? &lt;/strong&gt;
 [[&amp;apos;greeting&amp;apos;, 0.99120883810944971], [&amp;apos;sandwich&amp;apos;, 0.31626066870883057]]&lt;/pre&gt; &lt;p&gt;你可以用其它语句、不同概率来试验几次，也可以添加训练数据来改进／扩展当前的模型。尤其注意用很少的训练数据就得到稳定的预测结果。&lt;/p&gt;
 &lt;p&gt;有一些句子将会产生多个预测结果（高于阈值）。你需要给你的程序设定一个合适的阈值。并非所有的文本分类方案都是相同的：  &lt;em&gt;一些预测情况比其他预测需要更高的置信水平&lt;/em&gt;。&lt;/p&gt;
 &lt;p&gt;最后这个分类结果展示了一些内部的细节：&lt;/p&gt; &lt;pre&gt;found in bag: good
found in bag: day
sentence: **good day** 
 bow: [0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
good day 
 [[&amp;apos;greeting&amp;apos;, 0.99664077655648697]]&lt;/pre&gt; &lt;p&gt;从这个句子的词包中可以看到，有两个单词和我们的词库是匹配的。同时我们的神经网络从这些 0 代表的非匹配词语中学习了。&lt;/p&gt;
 &lt;p&gt;如果提供一个仅仅有一个常用单词 ‘a’ 被匹配的句子，那我们会得到一个低概率的分类结果A：&lt;/p&gt; &lt;pre&gt;found in bag: a
sentence: **a burrito! **
 bow: [0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
a burrito! 
 [[&amp;apos;sandwich&amp;apos;, 0.61776860634647834]]&lt;/pre&gt; &lt;p&gt;现在你已经掌握了构建聊天机器人的一些基础知识结构，它能处理大量不同的意图，并且对于有限或者海量的训练数据都能很好的适配。想要为某个意图添加一个或者多个响应实在轻而易举，就不必多讲了。&lt;/p&gt;
 &lt;h4&gt;Enjoy!&lt;/h4&gt;
 &lt;p&gt;  &lt;a href="http://blog.jobbole.com/112049/"&gt;用神经网络训练一个文本分类器&lt;/a&gt;，首发于  &lt;a href="http://blog.jobbole.com"&gt;文章 - 伯乐在线&lt;/a&gt;。&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>IT技术 机器学习</category>
      <guid isPermaLink="true">https://itindex.net/detail/57344-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C-%E8%AE%AD%E7%BB%83-%E6%96%87%E6%9C%AC</guid>
      <pubDate>Thu, 10 Aug 2017 21:05:38 CST</pubDate>
    </item>
    <item>
      <title>人工智能复杂的商业需求，正促使博弈 AI 的崛起 | AI大师圆桌会</title>
      <link>https://itindex.net/detail/57289-%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD-%E5%A4%8D%E6%9D%82-%E5%95%86%E4%B8%9A</link>
      <description>&lt;p&gt;  &lt;img alt="&amp;#21345;&amp;#32784;&amp;#22522;&amp;#26757;&amp;#38534;&amp;#22823;&amp;#23398;&amp;#35745;&amp;#31639;&amp;#26426;&amp;#31995;&amp;#25945;&amp;#25480;&amp;#12289;&amp;#24503;&amp;#25169;AI&amp;#20043;&amp;#29238; Tuomas Sandholm" src="http://images.tmtpost.com/uploads/images/2017/07/7a16a984704b013e5b1f1ac007fad0b1_15012946222.jpeg"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;卡耐基梅隆大学计算机系教授、德扑AI之父 Tuomas Sandholm&lt;/p&gt; &lt;p&gt;“人工智能时代，针对机器的算法是机器学习，针对人类的算法是博弈论。”这是清华大学交叉信息研究院青年千人助理教授、博士生导师、计算经济学研究室主任唐平中，在2017年7月20日钛媒体与杉数科技联合举办的  &lt;a href="http://www.tmtpost.com/tag/2649618"&gt;“AI大师圆桌会之AI时代的博弈与行为分析”&lt;/a&gt;活动上提出的观点。&lt;/p&gt; &lt;p&gt;唐平中在加入清华之前，于美国卡耐基梅隆大学计算机系从事博士后研究工作，师从德扑AI之父Tuomas Sandholm，从事人工智能、电子商务和机制设计的研究工作。&lt;/p&gt; &lt;p&gt;Tuomas Sandholm是卡耐基梅隆大学计算机系教授，研究兴趣包括人工智能、机制设计、优化理论、博弈论、电子商务、多代理系统、自动谈判及合同等广泛领域，迄今已经发表450多篇论文，还是Optimized Markets和Strategic Machine两家公司的创始人。&lt;/p&gt; &lt;p&gt;Tuomas Sandholm 在“AI时代的博弈与行为分析”圆桌会上，分享了在他带领下完成的人工智能系统Libratus（中文名：冷扑大师）及其在今年以来击败全球德州扑克世界高手的经历。&lt;/p&gt; &lt;p&gt;Tuomas表示，Libratus系统结合了优化的博弈论和机器学习等算法，是迄今为止唯一击败人类德州扑克世界最顶级高手的人工智能系统，在战略推理和战略思维方面达到了超越人类的水平。&lt;/p&gt; &lt;p&gt;与AlphaGo围棋的完美信息场景不同，德州扑克是典型的非完美信息场景，已经挑战人工智能+博弈论数十年之久。Tuomas表示，Libratus算法无应用领域限制，可广泛应用到多种商业与经济场景中。&lt;/p&gt; &lt;p&gt;在此次圆桌会上，除了唐平中和Tuomas Sandholm，上海财经大学教授、奥数金牌、杉数科技科学家何斯迈与上海财经大学交叉科学研究院院长、杉数科技首席科学家葛冬冬以及不列颠哥伦比亚大学（UBC）Sauder商学院助理教授丁弋川等也进行了分享。&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;h2&gt;博弈AI大时代的崛起  &lt;br /&gt;  &lt;img alt="&amp;#28165;&amp;#21326;&amp;#22823;&amp;#23398;&amp;#20132;&amp;#21449;&amp;#20449;&amp;#24687;&amp;#30740;&amp;#31350;&amp;#38498;&amp;#38738;&amp;#24180;&amp;#21315;&amp;#20154;&amp;#21161;&amp;#29702;&amp;#25945;&amp;#25480;&amp;#12289;&amp;#21338;&amp;#22763;&amp;#29983;&amp;#23548;&amp;#24072;&amp;#12289;&amp;#35745;&amp;#31639;&amp;#32463;&amp;#27982;&amp;#23398;&amp;#30740;&amp;#31350;&amp;#23460;&amp;#20027;&amp;#20219;&amp;#21776;&amp;#24179;&amp;#20013;" src="http://images.tmtpost.com/uploads/images/2017/07/b1ee744681a603070eaa7edd9517137a_15012946221.jpeg"&gt;&lt;/img&gt;&lt;/h2&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;清华大学交叉信息研究院青年千人助理教授、博士生导师、计算经济学研究室主任唐平中&lt;/p&gt; &lt;p&gt;有人的地方就有博弈。从囚徒困境到智猪博弈再到美女的硬币，博弈论一再说明一个道理，就是人类的思维不同于机器，特别由于人类的社会化属性，但凡有两个人以上的地方就充满着博弈的思想。因为有人的地方就有竞争，有竞争就有博弈。&lt;/p&gt; &lt;p&gt;作为数据、计算机与经济学的交叉学科，博弈论已经成为经济学的标准分析工具之一，在金融学、证券学、生物学、经济学、国际关系、计算机科学、政治学、军事战略和其它很多领域都有广泛的应用。博弈分为合作博弈与非合作博弈、完全信息博弈与非完全信息博弈、静态博弈与动态博弈等多种类型。&lt;/p&gt; &lt;p&gt;唐平中表示，博弈论在西方已经有近90年的历史，而在中国则是于近5年得到了广泛的关注和应用。从2009年开始的互联网广告拍卖设计算法，使博弈论在国内经济界得到了重视，如今国内工业界对博弈AI的算法也有大量的需求。&lt;/p&gt; &lt;p&gt;究其原因是在研究人工智能的时候，发现仅处理针对机器的算法远不能满足实际商业的需求，  &lt;strong&gt;在现代商业中往往是“人+机器”的复杂场景，而博弈论恰好是针对人类的智能算法。&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;丁弋川在解读Tuomas Sandholm对AI的观点时，强调博弈论与最优化决策的结合。正如Tuomas Sandholm所解释，博弈论本身只是一个描述性工具，并不做出具体的决策，而最优化决策则指出每一步的最优化结果，但同时并没有考虑对方的情况，AI就把二者结合起来做出二人博弈时的最优化决策平衡点。&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;这就是德州扑克在人工智能学术上不亚于甚至是超过AlphaGo的意义所在，它提供了一种新模型去分析在没有或缺乏信息的情况下，如何计算平衡点。&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Tuomas Sandholm强调，扑克游戏本身就是非完美信息的场景，德州扑克更是非完美信息的典型场景。在德州扑克进行的过程中，缺乏甚至没有任何数据，在这种情况如何计算出每一步的最优平衡点，同时还要考虑进对手的每一步情况。德州扑克有10的161次方个决策点，是非完美信息博弈算法的标准测试场景，国际上一直有学术团队在挑战这一场景。&lt;/p&gt; &lt;p&gt;葛冬冬介绍说，在杉数科技从事项目算法开发与实施的过程，发现很多现实中的商业问题不仅仅需要考虑最优化，很多时候还需要考虑到人类的行为，这些人类行为将给问题的解决带来额外的难度。&lt;/p&gt; &lt;p&gt;比如在考虑电商定价的时候，不仅要根据以前的价格历史来计算未来的最优价格，还要考虑到与顾客和竞争对手的反应。当价格低的时候，电商顾客会根据情况囤积自己的小库存，便宜就多买、不便宜就不买，同时竞争对手也会实时比价跟随定价。因此，在新零售等场景中，不再仅仅是数据驱动，还要考虑复杂情况下的顾客和竞争对手博弈等，这就应用了很多AI技巧。&lt;/p&gt; &lt;p&gt;博弈AI的崛起，正是复杂商业需求驱动的结果。&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;h2&gt;冷扑大师：超人类的机器战略思维&lt;/h2&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;  &lt;img height="480" src="http://images.tmtpost.com/uploads/images/2017/07/381382cd848f564191b19d1e8f2e18c0_1501294623.jpeg" width="720"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;上海财经大学教授、奥数金牌、杉数科技科学家何斯迈&lt;/p&gt; &lt;p&gt;2017年1月，在宾夕法尼亚州匹兹堡的赌场里，Libratus冷扑大师挑战了世界排名前15中的4位“一对一无限注”德州扑克人类高手，在设计了非常保守的对人类极为友好的对战环境后，Libratus在20天赛程内一共进行了12万手牌比赛，最后Libratus以绝对优势战胜4位人类选手。&lt;/p&gt; &lt;p&gt;2017年4月，Libratus应邀以“冷扑大师”之名到中国与来自中国的一组扑克、AI和计算机高手组成的“龙之队”对决，冷扑大师最终以792,327总记分牌的战绩获胜，获得了此次表演赛的200万奖励。Libratus冷扑大师完胜中外的德州扑克高手，说明其代表的博弈AI算法已经到了相当成熟的高度。&lt;/p&gt; &lt;p&gt;Tuomas Sandholm在“AI时代的博弈与行为分析”分享会上介绍了Libratus的历史，这是一项长达12年的研究。&lt;/p&gt; &lt;p&gt;从最早的2005年Rhode Island Hold’em扑克游戏起步，该游戏是一种小型的扑克游戏，但也有高达31亿个决策节点，具备大型德州扑克的许多特征。Tuomas Sandholm与Andrew Gilpin于2005年在AAAI会议上发表了论文，介绍了该项研究的初步成果。&lt;/p&gt; &lt;p&gt;从2005年起步，Tuomas Sandholm和团队在2014年5月推出了Tartanian7人工智能系统并赢得了2014年ACAP计算机扑克大赛，2015年4月推出了下一代Claudico人工智能系统，但Claudico在2015年败给了人类选手。&lt;/p&gt; &lt;p&gt;2015年12月推出的下一代Baby Tartanian8赢得了2016年ACAP大赛，接着又推出了Tartanian8。最后，就是2017年1月的Libratus赢得了人机对战的胜利。&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="http://images.tmtpost.com/uploads/images/2016/12/t5y4hyhgf3.jpg"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;Libratus包括了三个模块，分别是赛前的游戏规则抽取和纳什均衡近似，赛中用到的残局解算（Endgame Solving）以及持续自我强化。&lt;/p&gt; &lt;p&gt;博弈论中的纳什均衡主要指博弈双方的理性参与者都不会有单独改变策略的冲动，而该系统中采用纳什均衡，使得不论对手用什么样的策略，都不能取胜。&lt;/p&gt; &lt;p&gt;在前期寻求纳什均衡的过程中，采用了CFR算法（Counterfactual Regret Minimization，反事实遗憾最小化）的强化学习，这是一个源自心理学的算法，即“如果当初做了另外一种选择就好了”的反向思考方式，这种强调试错的方法可以让机器自己和自己玩大量游戏。&lt;/p&gt; &lt;p&gt;通过CFR算法，Tuomas Sandholm向Libratus描述了游戏规则后，经过几个月的随机训练100万亿手牌后，达到了足以挑战最优秀人类选手的水平，还开发出了一些人类没有的玩法。&lt;/p&gt; &lt;p&gt;而在残局解算中，Libratus的改进算法包括考虑进了对方所犯下的错误、尽早开始残局解算、对手每出一招即实时展开残局解算（Nested Endgame Solving）等等。&lt;/p&gt; &lt;p&gt;而在持续自我强化模块，Libratus在比赛的每晚不是分析对手的问题，而是总结自己当天的主要败笔，这就好像人类每天总结自己的弱点再有针对性的弥补，这样自己的弱点越来越少，给对手的可趁之机也越来越少，这种主动防守型的打法，最坏的结果就是双方打成平手，所谓纳什均衡追求的就是一种平衡。&lt;/p&gt; &lt;p&gt;当然，12年的研究成果并不是聊聊几句就可以完全解释清楚。除了主要算法的改进外，Libratus在计算方面也有所改进以适应庞大的分布式计算需求。根据匹兹堡超算中心的数字，Libratus约使用了150台服务器。随着硬件能力的提升，Libratus有望使用越来越少的计算资源，甚至将来有可能运行在手机里。&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;h2&gt;打开非完美信息世界之门&lt;/h2&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;  &lt;img height="480" src="http://images.tmtpost.com/uploads/images/2017/07/c650e406176f0a48ef9084eb91f066ab_1501294623.jpeg" width="720"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;上海财经大学交叉科学研究院院长、杉数科技首席科学家葛冬冬（左），不列颠哥伦比亚大学（UBC）Sauder商学院助理教授丁弋川（右）&lt;/p&gt; &lt;p&gt;Libratus是博弈AI的最新成果和高度，当然这并不意味着Libratus就已经完美。Libratus针对的是一对一比赛的场景，主要是纳什均衡即针对一对一对场景，在多人博弈场景下纳什均衡就不太适用。此外，Libratus针对的是每场重置筹码的比赛，而人类不重置筹码的比赛也未必适用。&lt;/p&gt; &lt;p&gt;尽管Libratus还有局限性，但Libratus至少打开非完美信息世界的大门。&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;目前Libratus已经取得的成就，可以通过机器自动化的方式在很多商业场景中替人类做出战略决策和最优化策略选择，这就是人工智能梦寐以求的决策自动化。&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Tuomas Sandholm表示，如果机器能够替代人类做出战略决策，那么结合深度学习的机器学习，就可以形成自动化的学习-决策-再学习-再决策的闭环，这才是完整的人工智能算法。&lt;/p&gt; &lt;p&gt;何斯迈介绍了杉数科技在新零售行业，特别是收益和库存管理中应用博弈论和人工智能的例子。&lt;/p&gt; &lt;p&gt;很多国内企业开始进入数字化和数据化时代，基于数据积累要开始量化管理，需要用到博弈AI算法。比如根据友商价格、顾客价格敏感度、顾客心理价位、季节性、产品替代及互补性、促销及拉升效应等多种量价关系要素来进行动态博弈，解决方案包括使用网络爬虫及机器学习来捕捉友商实际价格及分析友商价格策略，分析产品市场定位（主导者/跟随者/竞争者），再寻找远期利益与近期利益的动态平衡点。&lt;/p&gt; &lt;p&gt;何斯迈表示，现在普遍商业企业的规模很大，在一个调度管理问题的实例中，可能涉及决策变量就有上百万个，而且对实时性要求非常高，必须在秒级别完成上百万个决策变量的问题求解。何斯迈一直在杉数科技进行这方面的工作。&lt;/p&gt; &lt;p&gt;葛冬冬也强调，在杉数科技等的推动下，特定类型问题中百万级变量的整数规划问题求解，可以在1秒钟完成。实际上，对整数规划来说，哪怕只有几百个变量都求解都非常困难，极端情况下最好的商业软件也需要很长时间才能计算出来。&lt;/p&gt; &lt;p&gt;杉数这方面的进展，是与斯坦福大学的叶荫宇教授、乔治亚理工的蓝光辉教授、上海财经大学的“Leaves”优化实验室长期艰苦合作，才能实现特定场景下秒级百万规模变量的最优化求解。&lt;/p&gt; &lt;p&gt;有了Libratus这样成熟的博弈AI算法，其算法并没有行业领域的应用限制，再加上超大规模最优化问题秒级求解的工程化能力，就能在战略定价、产品组合优化、金融、商业谈判、业务战略、下一代安全、拍卖、医疗健康等广泛的非完美信息场景中实现机器决策+机器学习的完整人工智能体系，这将对整个社会、经济和商业带来深远的影响。（本文首发钛媒体）&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;更多精彩内容，关注钛媒体微信号（ID：taimeiti），或者下载钛媒体App&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>钛媒体·国际AI大师圆桌会 人工智能 头条 机器学习</category>
      <guid isPermaLink="true">https://itindex.net/detail/57289-%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD-%E5%A4%8D%E6%9D%82-%E5%95%86%E4%B8%9A</guid>
      <pubDate>Sat, 29 Jul 2017 10:20:00 CST</pubDate>
    </item>
  </channel>
</rss>

