第一篇:初探推荐引擎
初探:探索推荐引擎内部的秘密(1)推荐者:怕冷的南瓜(积分 36596)| 原作者:赵晨婷
“探索推荐引擎内部的秘密”系列将带领读者从浅入深的学习探索推荐引擎的机制,实现方法,其中还涉及一些基本的优化方法,例如聚类和分类的应用。同时在理 论讲解的基础上,还会结合 Apache Mahout 介绍如何在大规模数据上实现各种推荐策略,进行策略优化,构建高效的推荐引擎的方法。本文作为这个系列的第一篇文章,将深入介绍推荐引擎的工作原理,和其 中涉及的各种推荐机制,以及它们各自的优缺点和适用场景,帮助用户清楚的了解和快速构建适合自己的推荐引擎。
信息发现
如今已经进入了一个数据爆炸的时代,随着 Web 2.0 的发展,Web 已经变成数据分享的平台,那么,如何让人们在海量的数据中想要找到他们需要的信息将变得越来越难。
在这样的情形下,搜索引擎(Google,Bing,百度等等)成为大家快速找到目标信息的最好途径。在用户对自己需求相对明确的时候,用搜索引擎 很方便的通过关键字搜索很快的找到自己需要的信息。但搜索引擎并不能完全满足用户对信息发现的需求,那是因为在很多情况下,用户其实并不明确自己的需要,或者他们的需求很难用简单的关键字来表述。又或者他们需要更加符合他们个人口味和喜好的结果,因此出现了推荐系统,与搜索引擎对应,大家也习惯称它为推荐 引擎。
随着推荐引擎的出现,用户获取信息的方式从简单的目标明确的数据的搜索转换到更高级更符合人们使用习惯的信息发现。
如今,随着推荐技术的不断发展,推荐引擎已经在电子商务(E-commerce,例如 Amazon,当当网)和一些基于 social 的社会化站点(包括音乐,电影和图书分享,例如豆瓣,Mtime 等)都取得很大的成功。这也进一步的说明了,Web2.0 环境下,在面对海量的数据,用户需要这种更加智能的,更加了解他们需求,口味和喜好的信息发现机制。
推荐引擎
前面介绍了推荐引擎对于现在的 Web2.0 站点的重要意义,这一章我们将讲讲推荐引擎到底是怎么工作的。推荐引擎利用特殊的信息过滤技术,将不同的物品或内容推荐给可能对它们感兴趣的用户。
图 1.推荐引擎工作原理图
图 1 给出了推荐引擎的工作原理图,这里先将推荐引擎看作黑盒,它接受的输入是推荐的数据源,一般情况下,推荐引擎所需要的数据源包括:
要推荐物品或内容的元数据,例如关键字,基因描述等; 系统用户的基本信息,例如性别,年龄等 用户对物品或者信息的偏好,根据应用本身的不同,可能包括用户对物品的评分,用户查看物品的记录,用户的购买记录等。其实这些用户的偏好信息可以分为两类:
显式的用户反馈:这类是用户在网站上自然浏览或者使用网站以外,显式的提供反馈信息,例如用户对物品的评分,或者对物品的评论。 隐式的用户反馈:这类是用户在使用网站是产生的数据,隐式的反应了用户对物品的喜好,例如用户购买了某物品,用户查看了某物品的信息等等。
显式的用户反馈能准确的反应用户对物品的真实喜好,但需要用户付出额外的代价,而隐式的用户行为,通过一些分析和处理,也能反映用户的喜好,只是数 据不是很精确,有些行为的分析存在较大的噪音。但只要选择正确的行为特征,隐式的用户反馈也能得到很好的效果,只是行为特征的选择可能在不同的应用中有很 大的不同,例如在电子商务的网站上,购买行为其实就是一个能很好表现用户喜好的隐式反馈。
推荐引擎根据不同的推荐机制可能用到数据源中的一部分,然后根据这些数据,分析出一定的规则或者直接对用户对其他物品的喜好进行预测计算。这样推荐引擎可以在用户进入的时候给他推荐他可能感兴趣的物品。
推荐引擎的分类
推荐引擎的分类可以根据很多指标,下面我们一一介绍一下: 1.推荐引擎是不是为不同的用户推荐不同的数据
根据这个指标,推荐引擎可以分为基于大众行为的推荐引擎和个性化推荐引擎
根据大众行为的推荐引擎,对每个用户都给出同样的推荐,这些推荐可以是静态的由系统管理员人工设定的,或者基于系统所有用户的反馈统计计算出的当下比较流行的物品。o 个性化推荐引擎,对不同的用户,根据他们的口味和喜好给出更加精确的推荐,这时,系统需要了解需推荐内容和用户的特质,或者基于社会化网络,通过找到与当前用户相同喜好的用户,实现推荐。o
这是一个最基本的推荐引擎分类,其实大部分人们讨论的推荐引擎都是将个性化的推荐引擎,因为从根本上说,只有个性化的推荐引擎才是更加智能的信息发现过程。2.根据推荐引擎的数据源
其实这里讲的是如何发现数据的相关性,因为大部分推荐引擎的工作原理还是基于物品或者用户的相似集进行推荐。那么参考图 1 给出的推荐系统原理图,根据不同的数据源发现数据相关性的方法可以分为以下几种: 根据系统用户的基本信息发现用户的相关程度,这种被称为基于人口统计学的推荐(Demographic-based Recommendation)o 根据推荐物品或内容的元数据,发现物品或者内容的相关性,这种被称为基于内容的推荐(Content-based Recommendation)o 根据用户对物品或者信息的偏好,发现物品或者内容本身的相关性,或者是发现用户的相关性,这种被称为基于协同过滤的推荐(Collaborative Filtering-based Recommendation)。
3.根据推荐模型的建立方式 o
可以想象在海量物品和用户的系统中,推荐引擎的计算量是相当大的,要实现实时的推荐务必需要建立一个推荐模型,关于推荐模型的建立方式可以分为以下几种:
o 基于物品和用户本身的,这种推荐引擎将每个用户和每个物品都当作独立的实体,预测每个用户对于每个物品的喜好程度,这些信息往往 是用一个二维矩阵描述的。由于用户感兴趣的物品远远小于总物品的数目,这样的模型导致大量的数据空置,即我们得到的二维矩阵往往是一个很大的稀疏矩阵。同 时为了减小计算量,我们可以对物品和用户进行聚类,然后记录和计算一类用户对一类物品的喜好程度,但这样的模型又会在推荐的准确性上有损失。o 基于关联规则的推荐(Rule-based Recommendation):关联规则的挖掘已经是数据挖掘中的一个经典的问题,主要是挖掘一些数据的依赖关系,典型的场景就是“购物篮问题”,通过 关联规则的挖掘,我们可以找到哪些物品经常被同时购买,或者用户购买了一些物品后通常会购买哪些其他的物品,当我们挖掘出这些关联规则之后,我们可以基于 这些规则给用户进行推荐。o 基于模型的推荐(Model-based Recommendation):这是一个典型的机器学习的问题,可以将已有的用户喜好信息作为训练样本,训练出一个预测用户喜好的模型,这样以后用户在 进入系统,可以基于此模型计算推荐。这种方法的问题在于如何将用户实时或者近期的喜好信息反馈给训练好的模型,从而提高推荐的准确度。
其实在现在的推荐系统中,很少有只使用了一个推荐策略的推荐引擎,一般都是在不同的场景下使用不同的推荐策略从而达到最好的推荐效果,例如 Amazon 的推荐,它将基于用户本身历史购买数据的推荐,和基于用户当前浏览的物品的推荐,以及基于大众喜好的当下比较流行的物品都在不同的区域推荐给用户,让用户 可以从全方位的推荐中找到自己真正感兴趣的物品。
深入推荐机制
这一章的篇幅,将详细介绍各个推荐机制的工作原理,它们的优缺点以及应用场景。
基于人口统计学的推荐
基于人口统计学的推荐机制(Demographic-based Recommendation)是一种最易于实现的推荐方法,它只是简单的根据系统用户的基本信息发现用户的相关程度,然后将相似用户喜爱的其他物品推荐给当前用户,图 2 给出了这种推荐的工作原理。图 2.基于人口统计学的推荐机制的工作原理
从图中可以很清楚的看到,首先,系统对每个用户都有一个用户 Profile 的建模,其中包括用户的基本信息,例如用户的年龄,性别等等;然后,系统会根据用户的 Profile 计算用户的相似度,可以看到用户 A 的 Profile 和用户 C 一样,那么系统会认为用户 A 和 C 是相似用户,在推荐引擎中,可以称他们是“邻居”;最后,基于“邻居”用户群的喜好推荐给当前用户一些物品,图中将用户 A 喜欢的物品 A 推荐给用户 C。
这种基于人口统计学的推荐机制的好处在于:
1.因为不使用当前用户对物品的喜好历史数据,所以对于新用户来讲没有“冷启动(Cold Start)”的问题。2.这个方法不依赖于物品本身的数据,所以这个方法在不同物品的领域都可以使用,它是领域独立的(domain-independent)。那么这个方法的缺点和问题是什么呢?这种基于用户的基本信息对用户进行分类的方法过于粗糙,尤其是对品味要求较高的领域,比如图书,电影和音乐等领 域,无法得到很好的推荐效果。可能在一些电子商务的网站中,这个方法可以给出一些简单的推荐。另外一个局限是,这个方法可能涉及到一些与信息发现问题本身 无关却比较敏感的信息,比如用户的年龄等,这些用户信息不是很好获取。基于内容的推荐
基于内容的推荐是在推荐引擎出现之初应用最为广泛的推荐机制,它的核心思想是根据推荐物品或内容的元数据,发现物品或者内容的相关性,然后基于用户以往的喜好记录,推荐给用户相似的物品。图 3 给出了基于内容推荐的基本原理。
图 3.基于内容推荐机制的基本原理
图 3 中给出了基于内容推荐的一个典型的例子,电影推荐系统,首先我们需要对电影的元数据有一个建模,这里只简单的描述了一下电影的类型;然后通过电影的元数据 发现电影间的相似度,因为类型都是“爱情,浪漫”电影 A 和 C 被认为是相似的电影(当然,只根据类型是不够的,要得到更好的推荐,我们还可以考虑电影的导演,演员等等);最后实现推荐,对于用户 A,他喜欢看电影 A,那么系统就可以给他推荐类似的电影 C。
这种基于内容的推荐机制的好处在于它能很好的建模用户的口味,能提供更加精确的推荐。但它也存在以下几个问题:
1.需要对物品进行分析和建模,推荐的质量依赖于对物品模型的完整和全面程度。在现在的应用中我们可以观察到关键词和标签(Tag)被认为是描述物品元数据的一种简单有效的方法。
2.物品相似度的分析仅仅依赖于物品本身的特征,这里没有考虑人对物品的态度。
3.因为需要基于用户以往的喜好历史做出推荐,所以对于新用户有“冷启动”的问题。虽然这个方法有很多不足和问题,但他还是成功的应用在一些电影,音乐,图书的社交站点,有些站点还请专业的人员对物品进行基因编码,比如潘多拉,在一份报告中说道,在潘多拉的推荐引擎中,每首歌有超过 100 个元数据特征,包括歌曲的风格,年份,演唱者等等。基于协同过滤的推荐
随着 Web2.0 的发展,Web 站点更加提倡用户参与和用户贡献,因此基于协同过滤的推荐机制因运而生。它的原理很简单,就是根据用户对物品或者信息的偏好,发现物品或者内容本身的相关 性,或者是发现用户的相关性,然后再基于这些关联性进行推荐。基于协同过滤的推荐可以分为三个子类:基于用户的推荐(User-based Recommendation),基于项目的推荐(Item-based Recommendation)和基于模型的推荐(Model-based Recommendation)。下面我们一个一个详细的介绍着三种协同过滤的推荐机制。基于用户的协同过滤推荐
基于用户的协同过滤推荐的基本原理是,根据所有用户对物品或者信息的偏好,发现与当前用户口味和偏好相似的“邻居”用户群,在一般的应用中是采用计算“K-邻居”的算法;然后,基于这 K 个邻居的历史偏好信息,为当前用户进行推荐。下图 4 给出了原理图。
图 4.基于用户的协同过滤推荐机制的基本原理
上图示意出基于用户的协同过滤推荐机制的基本原理,假设用户 A 喜欢物品 A,物品 C,用户 B 喜欢物品 B,用户 C 喜欢物品 A,物品 C 和物品 D;从这些用户的历史喜好信息中,我们可以发现用户 A 和用户 C 的口味和偏好是比较类似的,同时用户 C 还喜欢物品 D,那么我们可以推断用户 A 可能也喜欢物品 D,因此可以将物品 D 推荐给用户 A。
基于用户的协同过滤推荐机制和基于人口统计学的推荐机制都是计算用户的相似度,并基于“邻居”用户群计算推荐,但它们所不同的是如何计算用户的相似 度,基于人口统计学的机制只考虑用户本身的特征,而基于用户的协同过滤机制可是在用户的历史偏好的数据上计算用户的相似度,它的基本假设是,喜欢类似物品 的用户可能有相同或者相似的口味和偏好。基于项目的协同过滤推荐
基于项目的协同过滤推荐的基本原理也是类似的,只是说它使用所有用户对物品或者信息的偏好,发现物品和物品之间的相似度,然后根据用户的历史偏好信息,将类似的物品推荐给用户,图 5 很好的诠释了它的基本原理。
假设用户 A 喜欢物品 A 和物品 C,用户 B 喜欢物品 A,物品 B 和物品 C,用户 C 喜欢物品 A,从这些用户的历史喜好可以分析出物品 A 和物品 C 时比较类似的,喜欢物品 A 的人都喜欢物品 C,基于这个数据可以推断用户 C 很有可能也喜欢物品 C,所以系统会将物品 C 推荐给用户 C。
与上面讲的类似,基于项目的协同过滤推荐和基于内容的推荐其实都是基于物品相似度预测推荐,只是相似度计算的方法不一样,前者是从用户历史的偏好推断,而后者是基于物品本身的属性特征信息。
图 5.基于项目的协同过滤推荐机制的基本原理
同时协同过滤,在基于用户和基于项目两个策略中应该如何选择呢?其实基于项目的协同过滤推荐机制是 Amazon 在基于用户的机制上改良的一种策略,因为在大部分的 Web 站点中,物品的个数是远远小于用户的数量的,而且物品的个数和相似度相对比较稳定,同时基于项目的机制比基于用户的实时性更好一些。但也不是所有的场景都 是这样的情况,可以设想一下在一些新闻推荐系统中,也许物品,也就是新闻的个数可能大于用户的个数,而且新闻的更新程度也有很快,所以它的形似度依然不稳 定。所以,其实可以看出,推荐策略的选择其实和具体的应用场景有很大的关系。基于模型的协同过滤推荐
基于模型的协同过滤推荐就是基于样本的用户喜好信息,训练一个推荐模型,然后根据实时的用户喜好的信息进行预测,计算推荐。
基于协同过滤的推荐机制是现今应用最为广泛的推荐机制,它有以下几个显著的优点:
1.它不需要对物品或者用户进行严格的建模,而且不要求物品的描述是机器可理解的,所以这种方法也是领域无关的。
2.这种方法计算出来的推荐是开放的,可以共用他人的经验,很好的支持用户发现潜在的兴趣偏好 而它也存在以下几个问题: 1.方法的核心是基于历史数据,所以对新物品和新用户都有“冷启动”的问题。
2.推荐的效果依赖于用户历史偏好数据的多少和准确性。3.在大部分的实现中,用户历史偏好是用稀疏矩阵进行存储的,而稀疏矩阵上的计算有些明显的问题,包括可能少部分人的错误偏好会对推荐的准确度有很大的影响等等。
4.对于一些特殊品味的用户不能给予很好的推荐。5.由于以历史数据为基础,抓取和建模用户的偏好后,很难修改或者根据用户的使用演变,从而导致这个方法不够灵活。混合的推荐机制
在现行的 Web 站点上的推荐往往都不是单纯只采用了某一种推荐的机制和策略,他们往往是将多个方法混合在一起,从而达到更好的推荐效果。关于如何组合各个推荐机制,这里讲几种比较流行的组合方法。
1.加权的混合(Weighted Hybridization): 用线性公式(linear formula)将几种不同的推荐按照一定权重组合起来,具体权重的值需要在测试数据集上反复实验,从而达到最好的推荐效果。
2.切换的混合(Switching Hybridization):前面也讲到,其实对于不同的情况(数据量,系统运行状况,用户和物品的数目等),推荐策略可能有很大的不同,那么切换的混合方式,就是允许在不同的情况下,选择最为合适的推荐机制计算推荐。
3.分区的混合(Mixed Hybridization):采用多种推荐机制,并将不同的推荐结果分不同的区显示给用户。其实,Amazon,当当网等很多电子商务网站都是采用这样的方式,用户可以得到很全面的推荐,也更容易找到他们想要的东西。
4.分层的混合(Meta-Level Hybridization): 采用多种推荐机制,并将一个推荐机制的结果作为另一个的输入,从而综合各个推荐机制的优缺点,得到更加准确的推荐。
推荐引擎的应用
介绍完推荐引擎的基本原理,基本推荐机制,下面简要分析几个有代表性的推荐引擎的应用,这里选择两个领域:Amazon 作为电子商务的代表,豆瓣作为社交网络的代表。
推荐在电子商务中的应用 – Amazon
Amazon 作为推荐引擎的鼻祖,它已经将推荐的思想渗透在应用的各个角落。Amazon 推荐的核心是通过数据挖掘算法和比较用户的消费偏好于其他用户进行对比,借以预测用户可能感兴趣的商品。对应于上面介绍的各种推荐机制,Amazon 采用的是分区的混合的机制,并将不同的推荐结果分不同的区显示给用户,图 6 和图 7 展示了用户在 Amazon 上能得到的推荐。
图 6.Amazon 的推荐机制浏览物品
Amazon 利用可以记录的所有用户在站点上的行为,根据不同数据的特点对它们进行处理,并分成不同区为用户推送推荐: 今日推荐(Today's Recommendation For You): 通常是根据用户的近期的历史购买或者查看记录,并结合时下流行的物品给出一个折中的推荐。 新产品的推荐(New For You): 采用了基于内容的推荐机制(Content-based Recommendation),将一些新到物品推荐给用户。在方法选择上由于新物品没有大量的用户喜好信息,所以基于内容的推荐能很好的解决这个“冷启动”的问题。
捆绑销售(Frequently Bought Together): 采用数据挖掘技术对用户的购买行为进行分析,找到经常被一起或同一个人购买的物品集,进行捆绑销售,这是一种典型的基于项目的协同过滤推荐机制。
别人购买 / 浏览的商品(Customers Who Bought/See This Item Also Bought/See): 这也是一个典型的基于项目的协同过滤推荐的应用,通过社会化机制用户能更快更方便的找到自己感兴趣的物品。
值得一提的是,Amazon 在做推荐时,设计和用户体验也做得特别独到: Amazon 利用有它大量历史数据的优势,量化推荐原因。
基于社会化的推荐,Amazon 会给你事实的数据,让用户信服,例如:购买此物品的用户百分之多少也购买了那个物品;
基于物品本身的推荐,Amazon 也会列出推荐的理由,例如:因为你的购物框中有 ***,或者因为你购买过 ***,所以给你推荐类似的 ***。
另外,Amazon 很多推荐是基于用户的 profile 计算出来的,用户的 profile 中记录了用户在 Amazon 上的行为,包括看了那些物品,买了那些物品,收藏夹和 wish list 里的物品等等,当然 Amazon 里还集成了评分等其他的用户反馈的方式,它们都是 profile 的一部分,同时,Amazon 提供了让用户自主管理自己 profile 的功能,通过这种方式用户可以更明确的告诉推荐引擎他的品味和意图是什么。
推荐在社交网站中的应用 – 豆瓣
豆瓣是国内做的比较成功的社交网站,它以图书,电影,音乐和同城活动为中心,形成一个多元化的社交网络平台,自然推荐的功能是必不可少的,下面我们看看豆瓣是如何推荐的。
图 8.豆瓣的推荐机制基于用户品味的推荐
豆瓣的推荐是通过“豆瓣猜”,为了让用户清楚这些推荐是如何来的,豆瓣还给出了“豆瓣猜”的一个简要的介绍。
“你的个人推荐是根据你的收藏和评价自动得出的,每个人的推荐清单都不同。你的收藏和评价越多,豆瓣给你的推荐会越准确和丰富。每天推荐的内容可能会有变化。随着豆瓣的长大,给你推荐的内容也会越来越准。”
这一点让我们可以清晰明了的知道,豆瓣必然是基于社会化的协同过滤的推荐,这样用户越多,用户的反馈越多,那么推荐的效果会越来越准确。
相对于 Amazon 的用户行为模型,豆瓣电影的模型更加简单,就是“看过”和“想看”,这也让他们的推荐更加专注于用户的品味,毕竟买东西和看电影的动机还是有很大不同的。
另外,豆瓣也有基于物品本身的推荐,当你查看一些电影的详细信息的时候,他会给你推荐出“喜欢这个电影的人也喜欢的电影”,如图 10,这是一个基于协同过滤的应用。
图 10.豆瓣的推荐机制物品偏好的二维矩阵中,我们可以将一个用户对所有物品的偏好作为一个向量来计算用户之间的相似度,或者将所有用户对某个物品的偏好作为一个向量来计算物品 之间的相似度。下面我们详细介绍几种常用的相似度计算方法:
欧几里德距离(Euclidean Distance)
最初用于计算欧几里德空间中两个点的距离,假设 x,y 是 n 维空间的两个点,它们之间的欧几里德距离是:
可以看出,当 n=2 时,欧几里德距离就是平面上两个点的距离。
当用欧几里德距离表示相似度,一般采用以下公式进行转换:距离越小,相似度越大
皮尔逊相关系数(Pearson Correlation Coefficient)
皮尔逊相关系数一般用于计算两个定距变量间联系的紧密程度,它的取值在 [-1,+1] 之间。
sx, sy是 x 和 y 的样品标准偏差。
Cosine 相似度(Cosine Similarity)
Cosine 相似度被广泛应用于计算文档数据的相似度:
Tanimoto 系数(Tanimoto Coefficient)
Tanimoto 系数也称为 Jaccard 系数,是 Cosine 相似度的扩展,也多用于计算文档数据的相似度:
相似邻居的计算
介绍完相似度的计算方法,下面我们看看如何根据相似度找到用户用户 C,然后将用户 C 喜欢的物品 D 推荐给用户 A。
图 2.基于用户的 CF 的基本原理
基于物品的 CF(Item CF)
基于物品的 CF 的原理和基于用户的 CF 类似,只是在计算邻居时采用物品本身,而不是从用户的角度,即基于用户对物品的偏好找到相似的物品,然后根据用户的历史偏好,推荐相似的物品给他。从计算 的角度看,就是将所有用户对某个物品的偏好作为一个向量来计算物品之间的相似度,得到物品的相似物品后,根据用户历史的偏好预测当前用户还没有表示偏好的 物品,计算得到一个排序的物品列表作为推荐。图 3 给出了一个例子,对于物品 A,根据所有用户的历史偏好,喜欢物品 A 的用户都喜欢物品 C,得出物品 A 和物品 C 比较相似,而用户 C 喜欢物品 A,那么可以推断出用户 C 可能也喜欢物品 C。
图 3.基于物品的 CF 的基本原理
User CF vs.Item CF
前面介绍了 User CF 和 Item CF 的基本原理,下面我们分几个不同的角度深入看看它们各自的优缺点和适用场景:
计算复杂度
Item CF 和 User CF 是基于协同过滤推荐的两个最基本的算法,User CF 是很早以前就提出来了,Item CF 是从 Amazon 的论文和专利发表之后(2001 年左右)开始流行,大家都觉得 Item CF 从性能和复杂度上比 User CF 更优,其中的一个主要原因就是对于一个在线网站,用户的数量往往大大超过物品的数量,同时物品的数据相对稳定,因此计算物品的相似度不但计算量较小,同时 也不必频繁更新。但我们往往忽略了这种情况只适应于提供商品的电子商务网站,对于新闻,博客或者微内容的推荐系统,情况往往是相反的,物品的数量是海量 的,同时也是更新频繁的,所以单从复杂度的角度,这两个算法在不同的系统中各有优势,推荐引擎的设计者需要根据自己应用的特点选择更加合适的算法。
适用场景
在非社交网络的网站中,内容内在的联系是很重要的推荐原则,它比基于相似用户的推荐原则更加有效。比如在购书网站上,当你看一本书的时候,推荐引擎 会给你推荐相关的书籍,这个推荐的重要性远远超过了网站首页对该用户的综合推荐。可以看到,在这种情况下,Item CF 的推荐成为了引导用户浏览的重要手段。同时 Item CF 便于为推荐做出解释,在一个非社交网络的网站中,给某个用户推荐一本书,同时给出的解释是某某和你有相似兴趣的人也看了这本书,这很难让用户信服,因为用 户可能根本不认识那个人;但如果解释说是因为这本书和你以前看的某本书相似,用户可能就觉得合理而采纳了此推荐。
相反的,在现今很流行的社交网络站点中,User CF 是一个更不错的选择,User CF 加上社会网络信息,可以增加用户对推荐解释的信服程度。
推荐多样性和精度
研究推荐引擎的学者们在相同的数据集合上分别用 User CF 和 Item CF 计算推荐结果,发现推荐列表中,只有 50% 是一样的,还有 50% 完全不同。但是这两个算法确有相似的精度,所以可以说,这两个算法是很互补的。关于推荐的多样性,有两种度量方法:
第一种度量方法是从单个用户的角度度量,就是说给定一个用户,查看系统给出的推荐列表是否多样,也就是要比较推荐列表中的物品之间两两的相似度,不 难想到,对这种度量方法,Item CF 的多样性显然不如 User CF 的好,因为 Item CF 的推荐就是和以前看的东西最相似的。
第二种度量方法是考虑系统的多样性,也被称为覆盖率(Coverage),它是指一个推荐系统是否能够提供给所有用户丰富的选择。在这种指标下,Item CF 的多样性要远远好于 User CF, 因为 User CF 总是倾向于推荐热门的,从另一个侧面看,也就是说,Item CF 的推荐有很好的新颖性,很擅长推荐长尾里的物品。所以,尽管大多数情况,Item CF 的精度略小于 User CF,但如果考虑多样性,Item CF 却比 User CF 好很多。
如果你对推荐的多样性还心存疑惑,那么下面我们再举个实例看看 User CF 和 Item CF 的多样性到底有什么差别。首先,假设每个用户兴趣爱好都是广泛的,喜欢好几个领域的东西,不过每个用户肯定也有一个主要的领域,对这个领域会比其他领域更 加关心。给定一个用户,假设他喜欢 3 个领域 A,B,C,A 是他喜欢的主要领域,这个时候我们来看 User CF 和 Item CF 倾向于做出什么推荐:如果用 User CF, 它会将 A,B,C 三个领域中比较热门的东西推荐给用户;而如果用 ItemCF,它会基本上只推荐 A 领域的东西给用户。所以我们看到因为 User CF 只推荐热门的,所以它在推荐长尾里项目方面的能力不足;而 Item CF 只推荐 A 领域给用户,这样他有限的推荐列表中就可能包含了一定数量的不热门的长尾物品,同时 Item CF 的推荐对这个用户而言,显然多样性不足。但是对整个系统而言,因为不同的用户的主要兴趣点不同,所以系统的覆盖率会比较好。从上面的分析,可以很清晰的看到,这两种推荐都有其合理性,但都不是最好的选择,因此他们的精度也会有损失。其实对这类系统的最好选择是,如果系统 给这个用户推荐 30 个物品,既不是每个领域挑选 10 个最热门的给他,也不是推荐 30 个 A 领域的给他,而是比如推荐 15 个 A 领域的给他,剩下的 15 个从 B,C 中选择。所以结合 User CF 和 Item CF 是最优的选择,结合的基本原则就是当采用 Item CF 导致系统对个人推荐的多样性不足时,我们通过加入 User CF 增加个人推荐的多样性,从而提高精度,而当因为采用 User CF 而使系统的整体多样性不足时,我们可以通过加入 Item CF 增加整体的多样性,同样同样可以提高推荐的精度。
用户对推荐算法的适应度
前面我们大部分都是从推荐引擎的角度考虑哪个算法更优,但其实我们更多的应该考虑作为推荐引擎的最终使用者--应用用户对推荐算法的适应度。对于 User CF,推荐的原则是假设用户会喜欢那些和他有相同喜好的用户喜欢的东西,但如果一个用户没有相同喜好的朋友,那 User CF 的算法的效果就会很差,所以一个用户对的 CF 算法的适应度是和他有多少共同喜好用户成正比的。Item CF 算法也有一个基本假设,就是用户会喜欢和他以前喜欢的东西相似的东西,那么我们可以计算一个用户喜欢的物品的自相似度。一个用户喜欢物品的自相似度大,就 说明他喜欢的东西都是比较相似的,也就是说他比较符合 Item CF 方法的基本假设,那么他对 Item CF 的适应度自然比较好;反之,如果自相似度小,就说明这个用户的喜好习惯并不满足 Item CF 方法的基本假设,那么对于这种用户,用 Item CF 方法做出好的推荐的可能性非常低。
通过以上的介绍,相信大家已经对协同过滤推荐的各种方法,原则,特点和适用场景有深入的了解,下面我们就进入实战阶段,重点介绍如何基于 Apache Mahout 实现协同过滤推荐算法。
基于 Apache Mahout 实现高效的协同过滤推荐
Apache Mahout 是 Apache Software Foundation(ASF)旗下的一个开源项目,提供一些可扩展的机器学习领域经典算法的实现,旨在帮助开发人员更加方便快捷地创建智能应用程序,并且,在 Mahout 的最近版本中还加入了对 Apache Hadoop 的支持,使这些算法可以更高效的运行在云计算环境中。
关于 Apache Mahout 的安装和配置请参考《基于 Apache Mahout 构建社会化推荐引擎》,它是笔者 09 年发表的一篇关于基于 Mahout 实现推荐引擎的 developerWorks 文章,其中详细介绍了 Mahout 的安装步骤,并给出一个简单的电影推荐引擎的例子。
Apache Mahout 中提供的一个协同过滤算法的高效实现,它是一个基于 Java 实现的可扩展的,高效的推荐引擎。图 4 给出了 Apache Mahout 中协同过滤推荐实现的组件图,下面我们逐步深入介绍各个部分。
图 4.组件图
数据表示:Data Model Preference
基于协同过滤的推荐引擎的输入是用户的历史偏好信息,在 Mahout 里它被建模为 Preference(接口),一个 Preference 就是一个简单的三元组 < 用户 ID, 物品 ID, 用户偏好 >,它的实现类是 GenericPreference,可以通过以下语句创建一个 GenericPreference。
GenericPreference preference = new GenericPreference(123, 456, 3.0f);这其中,123 是用户 ID,long 型;456 是物品 ID,long 型;3.0f 是用户偏好,float 型。从这个例子我们可以看出,单单一个 GenericPreference 的数据就占用 20 bytes,所以你会发现如果只简单实用数组 Array 加载用户偏好数据,必然占用大量的内存,Mahout 在这方面做了一些优化,它创建了
PreferenceArray(接口)保存一组用户偏好数据,为了优化性能,Mahout 给出了两个实现类,GenericUserPreferenceArray 和
GenericItemPreferenceArray,分别按照用户和物品本身对用户偏好进行组装,这样就可以压缩用户 ID 或者物品 ID 的空间。下面清单 1 的代码以 GenericUserPreferenceArray 为例,展示了如何创建和使用一个 PreferenceArray。
清单 1.创建和使用 PreferenceArray
PreferenceArray userPref = new GenericUserPreferenceArray(2);//size = 2
userPref.setUserID(0, 1L);
userPref.setItemID(0, 101L);//<1L, 101L, 2.0f> userPref.setValue(0, 2.0f);userPref.setItemID(1, 102L);//<1L, 102L, 4.0f> userPref.setValue(1, 4.0f);
Preference pref = userPref.get(1);//<1L, 102L, 4.0f>
为了提高性能 Mahout 还构建了自己的 HashMap 和 Set:FastByIDMap 和 FastIDSet,有兴趣的朋友可以参考 Mahout 官方说明。DataModel
Mahout 的推荐引擎实际接受的输入是 DataModel,它是对用户偏好数据的压缩表示,通过创建内存版 DataModel 的语句我们可以看出:
DataModel model = new GenericDataModel(FastByIDMap
map);他保存在一个按照用户 ID 或者物品 ID 进行散列的 PreferenceArray,而 PreferenceArray 中对应保存着这个用户 ID 或者物品 ID 的所有用户偏好信息。
DataModel 是用户喜好信息的抽象接口,它的具体实现支持从任意类型的数据源抽取用户喜好信息,具体实现包括内存版的 GenericDataModel,支持文件读取的 FileDataModel 和支持数据库读取的 JDBCDataModel,下面我们看看如何创建各种 DataModel。
清单 2.创建各种 DataModel
//In-memory DataModelFileDataModel DataModel dataModel = new FileDataModel(new File(“preferences.csv”);
//Database-based DataModelPart 2: 深入推荐引擎相关算法--协同过滤》。
Mahout 中的向量 Vector 是一个每个域是浮点数(double)的复合对象,最容易联想到的实现就是一个浮点数的数组。但在具体应用由于向量本身数据内容的不同,比如有些向量的值很密集,每个域都有值;有些呢则是很稀 疏,可能只有少量域有值,所以 Mahout 提供了多个实现:
1.DenseVector,它的实现就是一个浮点数数组,对向量里所有域都进行存储,适合用于存储密集向量。
2.RandomAccessSparseVector 基于浮点数的 HashMap 实现的,key 是整形(int)类型,value 是浮点数(double)类型,它只存储向量中不为空的值,并提供随机访问。
3.SequentialAccessVector 实现为整形(int)类型和浮点数(double)类型的并行数组,它也只存储向量中不为空的值,但只提供顺序访问。用户可以根据自己算法的需求选择合适的向量实现类,如果算法需要很多随机访问,应该选择 DenseVector 或者 RandomAccessSparseVector,如果大部分都是顺序访问,SequentialAccessVector 的效果应该更好。
介绍了向量的实现,下面我们看看如何将现有的数据建模成向量,术语就是“如何对数据进行向量化”,以便采用 Mahout 的各种高效的聚类算法。1.简单的整形或浮点型的数据
这种数据最简单,只要将不同的域存在向量中即可,比如 n 维空间的点,其实本身可以被描述为一个向量。2.枚举类型数据
这类数据是对物体的描述,只是取值范围有限。举个例子,假设你有一个苹果信息的数据集,每个苹果的数据包括:大小,重量,颜色等,我们以颜 色为例,设苹果的颜色数据包括:红色,黄色和绿色。在对数据进行建模时,我们可以用数字来表示颜色,红色 =1,黄色 =2,绿色 =3,那么大小直径 8cm,重量 0.15kg,颜色是红色的苹果,建模的向量就是 <8, 0.15, 1>。
下面的清单 1 给出了对以上两种数据进行向量化的例子。
清单 1.创建简单的向量
// 创建一个二维点集的向量组
public static final double[][] points = { { 1, 1 }, { 2, 1 }, { 1, 2 }, { 2, 2 }, { 3, 3 }, { 8, 8 }, { 9, 8 }, { 8, 9 }, { 9, 9 }, { 5, 5 }, { 5, 6 }, { 6, 6 }};public static List
double[] fr = raw[i];// 这里选择创建 RandomAccessSparseVector
Vector vec = new RandomAccessSparseVector(fr.length);
// 将数据存放在创建的 Vector 中 vec.assign(fr);
points.add(vec);} return points;}
// 创建苹果信息数据的向量组
public static List
NamedVector apple = new NamedVector(new DenseVector(new double[] {0.11, 510, 1}),“Small round green apple”);apples.add(apple);apple = new NamedVector(new DenseVector(new double[] {0.2, 650, 3}),“Large oval red apple”);apples.add(apple);apple = new NamedVector(new DenseVector(new double[] {0.09, 630, 1}),“Small elongated red apple”);apples.add(apple);apple = new NamedVector(new DenseVector(new double[] {0.25, 590, 3}),“Large round yellow apple”);apples.add(apple);apple = new NamedVector(new DenseVector(new double[] {0.18, 520, 2}),“Medium oval green apple”);apples.add(apple);return apples;}
3.文本信息
作为聚类算法的主要应用场景逆向文本频率(Term Frequency – Inverse Document Frequency, TF-IDF):它是对 TF 方法的一种加强,字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在所有文本中出现的频率成反比下降。举个例子,对于“高频无意义词 汇”,因为它们大部分会出现在所有的文本中,所以它们的权重会大打折扣,这样就使得文本模型在描述文本特征上更加精确。在信息检索领域,TF-IDF 是对文本信息建模的最常用的方法。o
对于文本信息的向量化,Mahout 已经提供了工具类,它基于 Lucene 给出了对文本信息进行分析,然后创建文本向量。下面的清单 2 给出了一个例子,分析的文本数据是路透提供的新闻数据,参考资源里给出了下载地址。将数据集下载后,放在“clustering/reuters”目录 下。
清单 2.创建文本信息的向量
public static void documentVectorize(String[] args)throws Exception{ //1.将路透的数据解压缩 , Mahout 提供了专门的方法
DocumentClustering.extractReuters();//2.将数据存储成 SequenceFile,因为这些工具类就是在 Hadoop 的基础上做的,所以首先我们需要将数据写
// 成 SequenceFile,以便读取和计算
DocumentClustering.transformToSequenceFile();//3.将 SequenceFile 文件中的数据,基于 Lucene 的工具进行向量化
DocumentClustering.transformToVector();
}
public static void extractReuters(){ //ExtractReuters 是基于 Hadoop 的实现,所以需要将输入输出的文件目录传给它,这里我们可以直接把它映
// 射到我们本地的一个文件夹,解压后的数据将写入输出目录下
File inputFolder = new File(“clustering/reuters”);File outputFolder = new File(“clustering/reuters-extracted”);ExtractReuters extractor = new ExtractReuters(inputFolder, outputFolder);extractor.extract();}
public static void transformToSequenceFile(){ //SequenceFilesFromDirectory 实现将某个文件目录下的所有文件写入一个 SequenceFiles 的功能
// 它其实本身是一个工具类,可以直接用命令行调用,这里直接调用了它的 main 方法
String[] args = {“-c”, “UTF-8”, “-i”, “clustering/ reuters-extracted/”, “-o”, “clustering/reuters-seqfiles”};// 解释一下参数的意义:
//-c: 指定文件的编码形式,这里用的是“UTF-8” //-i: 指定输入的文件目录,这里指到我们刚刚导出文件的目录
//-o: 指定输出的文件目录
try {
SequenceFilesFromDirectory.main(args);} catch(Exception e){
e.printStackTrace();} }
public static void transformToVector(){ //SparseVectorsFromSequenceFiles 实现将 SequenceFiles 中的数据进行向量化。// 它其实本身是一个工具类,可以直接用命令行调用,这里直接调用了它的 main 方法 String[] args = {“-i”, “clustering/reuters-seqfiles/”, “-o”, “clustering/reuters-vectors-bigram”, “-a”, “org.apache.lucene.analysis.WhitespaceAnalyzer” , “-chunk”, “200”, “-wt”, “tfidf”, “-s”, “5”, “-md”, “3”, “-x”, “90”, “-ng”, “2”, “-ml”, “50”, “-seq”};// 解释一下参数的意义:
//-i: 指定输入的文件目录,这里指到我们刚刚生成 SequenceFiles 的目录
//-o: 指定输出的文件目录
//-a: 指定使用的 Analyzer,这里用的是 lucene 的空格分词的 Analyzer //-chunk: 指定 Chunk 的大小,单位是 M。对于大的文件集合,我们不能一次 load 所有文件,所以需要 // 对数据进行切块
//-wt: 指定分析时采用的计算权重的模式,这里选了 tfidf //-s: 指定词语在整个文本集合出现的最低频度,低于这个频度的词汇将被丢掉
//-md: 指定词语在多少不同的文本中出现的最低值,低于这个值的词汇将被丢掉
//-x: 指定高频词汇和无意义词汇(例如 is,a,the 等)的出现频率上限,高于上限的将被丢掉
//-ng: 指定分词后考虑词汇的最大长度,例如 1-gram 就是,coca,cola,这是两个词,// 2-gram 时,coca cola 是一个词汇,2-gram 比 1-gram 在一定情况 下分析的更准确。
//-ml: 指定判断相邻词语是不是属于一个词汇的相似度阈值,当选择 >1-gram 时才有用,其实计算的是
// Minimum Log Likelihood Ratio 的阈值 //-seq: 指定生成的向量是
SequentialAccessSparseVectors,没设置时默 认生成还是
// RandomAccessSparseVectors
try {
SparseVectorsFromSequenceFiles.main(args);} catch(Exception e){
e.printStackTrace();} }
这里补充一点,生成的向量化文件的目录结构是这样的:
图 2 文本信息向量化
o o o o o o o df-count 目录:保存着文本的频率信息
tf-vectors 目录:保存着以 TF 作为权值的文本向量
tfidf-vectors 目录:保存着以 TFIDF 作为权值的文本向量 tokenized-documents 目录:保存着分词过后的文本信息 wordcount 目录:保存着全局的词汇出现的次数 dictionary.file-0 目录:保存着这些文本的词汇表
frequcency-file-0 目录 : 保存着词汇表对应的频率信息。
介绍完向量化问题,下面我们深入分析各个聚类算法,首先介绍的是最经典的 K 均值算法。K 均值聚类算法 K 均值是典型的基于距离的排他的划分方法:给定一个 n 个对象的数据集,它可以构建数据的 k 个划分,每个划分就是一个聚类,并且 k<=n,同时还需要满足两个要求:
每个组至少包含一个对象
每个对象必须属于且仅属于一个组。
K 均值的基本原理是这样的,给定 k,即要构建的划分的数目,1.首先创建一个初始划分,随机地选择 k 个对象,每个对象初始地代表了一个簇中心。对于其他的对象,根据其与各个簇中心的距离,将它们赋给最近的簇。
2.然后采用一种迭代的重定位技术,尝试通过对象在划分间移动来改进划分。所谓重定位技术,就是当有新的对象加入簇或者已有对象离开簇的时候,重新计算簇的平均值,然后对对象进行重新分配。这个过程不断重复,直到没有簇中对象的变化。当结果簇是密集的,而且簇和簇之间的区别比较明显时,K 均值的效果比较好。对于处理大数据集,这个算法是相对可伸缩的和高效的,它的复杂度是 O(nkt),n 是对象的个数,k 是簇的数目,t 是迭代的次数,通常 k< K 均值的最大问题是要求用户必须事先给出 k 的个数,k 的选择一般都基于一些经验值和多次实验结果,对于不同的数据集,k 的取值没有可借鉴性。另外,K 均值对“噪音”和孤立点数据是敏感的,少量这类的数据就能对平均值造成极大的影响。 说了这么多理论的原理,下面我们基于 Mahout 实现一个简单的 K 均值算法的例子。如前面介绍的,Mahout 提供了基本的基于内存的实现和基于 Hadoop 的 Map/Reduce 的实现,分别是 KMeansClusterer 和 KMeansDriver,下面给出一个简单的例子,就基于我们在清单 1 里定义的二维点集数据。 清单 3.K 均值聚类算法示例 // 基于内存的 K 均值聚类算法实现 public static void kMeansClusterInMemoryKMeans(){ // 指定需要聚类的个数,这里选择 2 类 int k = 2;// 指定 K 均值聚类算法的最大迭代次数 int maxIter = 3;// 指定 K 均值聚类算法的最大距离阈值 double distanceThreshold = 0.01;// 声明一个计算距离的方法,这里选择了欧几里德距离 DistanceMeasure measure = new EuclideanDistanceMeasure();// 这里构建向量集,使用的是清单 1 里的二维点集 List List List // 打印最终的聚类结果 for(Cluster cluster : finalClusters.get(finalClusters.size()-1)){ System.out.println(“Cluster id: ” + cluster.getId()+ “ center: ” + cluster.getCenter().asFormatString());System.out.println(“ Points: ” + cluster.getNumPoints());} } // 基于 Hadoop 的 K 均值聚类算法实现 public static void kMeansClusterUsingMapReduce()throws Exception{ // 声明一个计算距离的方法,这里选择了欧几里德距离 DistanceMeasure measure = new EuclideanDistanceMeasure();// 指定输入路径,如前面介绍的一样,基于 Hadoop 的实现就是通过指定输入 输出的文件路径来指定数据源的。 Path testpoints = new Path(“testpoints”);Path output = new Path(“output”);// 清空输入输出路径下的数据 HadoopUtil.overwriteOutput(testpoints);HadoopUtil.overwriteOutput(output);RandomUtils.useTestSeed();// 在输入路径下生成点集,与内存的方法不同,这里需要把所有的向量写进文件,下面给出具体 的例子 SimpleDataSet.writePointsToFile(testpoints);// 指定需要聚类的个数,这里选择 2 类 int k = 2;// 指定 K 均值聚类算法的最大迭代次数 int maxIter = 3;// 指定 K 均值聚类算法的最大距离阈值 double distanceThreshold = 0.01;// 随机的选择 k 个作为簇的中心 Path clusters = RandomSeedGenerator.buildRandom(testpoints, new Path(output, “clusters-0”), k, measure);// 调用 KMeansDriver.runJob 方法执行 K 均值聚类算法 KMeansDriver.runJob(testpoints, clusters, output, measure, distanceThreshold, maxIter, 1, true, true);// 调用 ClusterDumper 的 printClusters 方法将聚类结果打印出来。ClusterDumper clusterDumper = new ClusterDumper(new Path(output, “clusters-” + maxIter-1), new Path(output, “clusteredPoints”));clusterDumper.printClusters(null);} //SimpleDataSet 的 writePointsToFile 方法,将测试点集写入文件里 // 首先我们将测试点集包装成 VectorWritable 形式,从而将它们写入文件 public static List double[] fr = raw[i]; Vector vec = new RandomAccessSparseVector(fr.length); vec.assign(fr);// 只是在加入点集前,在 RandomAccessSparseVector 外加了一层 VectorWritable 的包装 points.add(new VectorWritable(vec));} return points;} // 将 VectorWritable 的点集写入文件,这里涉及一些基本的 Hadoop 编程元素,详细的请 参阅参考资源里相关的内容 public static void writePointsToFile(Path output)throws IOException { // 调用前面的方法生成点集 List Configuration conf = new Configuration();// 生成 Hadoop 文件系统对象 FileSystem FileSystem fs = FileSystem.get(output.toUri(), conf);// 生成一个 SequenceFile.Writer,它负责将 Vector 写入文件中 SequenceFile.Writer writer = new SequenceFile.Writer(fs, conf, output, Text.class, VectorWritable.class);// 这里将向量按照文本形式写入文件 try { for(VectorWritable vw : pointVectors){ writer.append(new Text(), vw); } } finally { writer.close();} } 执行结果 KMeans Clustering In Memory Result Cluster id: 0 center:{“class”:“org.apache.mahout.math.RandomAccessSparseVector”, “vector”:“{”values“:{”table“:[0,1,0],”values“:[1.8,1.8,0.0], ”state“:[1,1,0], ”freeEntries“:1,”distinct“:2,”lowWaterMark“:0,”highWaterMark“:1, ”minLoadFactor“:0.2,”maxLoadFactor“:0.5},”size“:2,”lengthSquared “:-1.0}”} Points: 5 Cluster id: 1 center:{“class”:“org.apache.mahout.math.RandomAccessSparseVector”, “vector”:“{”values“:{”table“:[0,1,0],”values“:[7.***,7.***,0.0],”state“:[1,1,0],”freeEntries“:1,”distinct“:2,”lowWaterMark“:0,”highWaterMark“:1,”minLoadFactor“:0.2,”maxLoadFactor“:0.5},”size“:2,”lengthSquared“ :-1.0}”} Points: 7 KMeans Clustering Using Map/Reduce Result Weight: Point: 1.0: [1.000, 1.000] 1.0: [2.000, 1.000] 1.0: [1.000, 2.000] 1.0: [2.000, 2.000] 1.0: [3.000, 3.000] Weight: Point: 1.0: [8.000, 8.000] 1.0: [9.000, 8.000] 1.0: [8.000, 9.000] 1.0: [9.000, 9.000] 1.0: [5.000, 5.000] 1.0: [5.000, 6.000] 1.0: [6.000, 6.000] 介绍完 K 均值聚类算法,我们可以看出它最大的优点是:原理简单,实现起来也相对简单,同时执行效率和对于大数据量的可伸缩性还是较强的。然而缺点也是很明确的,首 先它需要用户在执行聚类之前就有明确的聚类个数的设置,这一点是用户在处理大部分问题时都不太可能事先知道的,一般需要通过多次试验找出一个最优的 K 值;其次就是,由于算法在最开始采用随机选择初始聚类中心的方法,所以算法对噪音和孤立点的容忍能力较差。所谓噪音就是待聚类对象中错误的数据,而孤立点 是指与其他数据距离较远,相似性较低的数据。对于 K 均值算法,一旦孤立点和噪音在最开始被选作簇中心,对后面整个聚类过程将带来很大的问题,那么我们有什么方法可以先快速找出应该选择多少个簇,同时找到簇 的中心,这样可以大大优化 K 均值聚类算法的效率,下面我们就介绍另一个聚类方法:Canopy 聚类算法。Canopy 聚类算法 Canopy 聚类算法的基本原则是:首先应用成本低的近似的距离计算方法高效的将数据分为多个组,这里称为一个 Canopy,我们姑且将它翻译为“华盖”,Canopy 之间可以有重叠的部分;然后采用严格的距离计算方式准确的计算在同一 Canopy 中的点,将他们分配与最合适的簇中。Canopy 聚类算法经常用于 K 均值聚类算法的预处理,用来找合适的 k 值和簇中心。 下面详细介绍一下创建 Canopy 的过程:初始,假设我们有一组点集 S,并且预设了两个距离阈值,T1,T2(T1>T2);然后选择一个点,计算它与 S 中其他点的距离(这里采用成本很低的计算方法),将距离在 T1 以内的放入一个 Canopy 中,同时从 S 中去掉那些与此点距离在 T2 以内的点(这里是为了保证和中心距离在 T2 以内的点不能再作为其他 Canopy 的中心),重复整个过程直到 S 为空为止。 对 K 均值的实现一样,Mahout 也提供了两个 Canopy 聚类的实现,下面我们就看看具体的代码例子。 清单 4.Canopy 聚类算法示例 //Canopy 聚类算法的内存实现 public static void canopyClusterInMemory(){ // 设置距离阈值 T1,T2 double T1 = 4.0;double T2 = 3.0;// 调用 CanopyClusterer.createCanopies 方法创建 Canopy,参数分别是: // 1.需要聚类的点集 // 2.距离计算方法 // 3.距离阈值 T1 和 T2 List // 有必须的时候,可以拿到 Canopy 聚类的结果作为 K 均值聚类的输入,能更精确更高效 的解决聚类问题 for(Canopy canopy : canopies){ System.out.println(“Cluster id: ” + canopy.getId()+ “ center: ” + canopy.getCenter().asFormatString()); System.out.println(“ Points: ” + canopy.getNumPoints()); } } //Canopy 聚类算法的 Hadoop 实现 public static void canopyClusterUsingMapReduce()throws Exception{ // 设置距离阈值 T1,T2 double T1 = 4.0;double T2 = 3.0;// 声明距离计算的方法 DistanceMeasure measure = new EuclideanDistanceMeasure();// 设置输入输出的文件路径 Path testpoints = new Path(“testpoints”);Path output = new Path(“output”);// 清空输入输出路径下的数据 HadoopUtil.overwriteOutput(testpoints);HadoopUtil.overwriteOutput(output);// 将测试点集写入输入目录下 SimpleDataSet.writePointsToFile(testpoints); // 调用 CanopyDriver.buildClusters 的方法执行 Canopy 聚类,参数是: // 1.输入路径,输出路径 // 2.计算距离的方法 // 3.距离阈值 T1 和 T2 new CanopyDriver().buildClusters(testpoints, output, measure, T1, T2, true);// 打印 Canopy 聚类的结果 List List } } } 执行结果 Canopy Clustering In Memory Result Cluster id: 0 center:{“class”:“org.apache.mahout.math.RandomAccessSparseVector”, “vector”:“{”values“:{”table“:[0,1,0],”values“:[1.8,1.8,0.0],”state“:[1,1,0],”freeEntries“:1,”distinct“:2,”lowWaterMark“:0, ”highWaterMark“:1,”minLoadFactor“:0.2,”maxLoadFactor“:0.5}, ”size“:2,”lengthSquared“:-1.0}”} Points: 5 Cluster id: 1 center:{“class”:“org.apache.mahout.math.RandomAccessSparseVector”,“vector”:“{”values“:{”table“:[0,1,0],”values“:[7.5,7.***,0.0],”state“:[1,1,0],”freeEntries“:1,”distinct“:2,”lowWaterMark“:0,”highWaterMark“:1,”minLoadFactor“:0.2,”maxLoadFactor“:0.5},”size“:2, ”lengthSquared“:-1.0}”} Points: 6 Cluster id: 2 center:{“class”:“org.apache.mahout.math.RandomAccessSparseVector”, “vector”:“{”values“:{”table“:[0,1,0],”values“:[5.0,5.5,0.0],”state“:[1,1,0],”freeEntries“:1,”distinct“:2,”lowWaterMark“:0,”highWaterMark“:1,”minLoadFactor“:0.2,”maxLoadFactor“:0.5},”size“:2, ”lengthSquared“:-1.0}”} Points: 2 Canopy Clustering Using Map/Reduce Result Cluster id: 0 center:{“class”:“org.apache.mahout.math.RandomAccessSparseVector”, “vector”:“{”values“:{”table“:[0,1,0],”values“:[1.8,1.8,0.0],”state“:[1,1,0],”freeEntries“:1,”distinct“:2,”lowWaterMark“:0, ”highWaterMark“:1,”minLoadFactor“:0.2,”maxLoadFactor“:0.5}, ”size“:2,”lengthSquared“:-1.0}”} Points: 5 Cluster id: 1 center:{“class”:“org.apache.mahout.math.RandomAccessSparseVector”,“vector”:“{”values“:{”table“:[0,1,0],”values“:[7.5,7.***,0.0],”state“:[1,1,0],”freeEntries“:1,”distinct“:2,”lowWaterMark“:0,”highWaterMark“:1,”minLoadFactor“:0.2,”maxLoadFactor“:0.5},”size“:2, ”lengthSquared“:-1.0}”} Points: 6 Cluster id: 2 center:{“class”:“org.apache.mahout.math.RandomAccessSparseVector”, “vector”:“{”values“:{”table“:[0,1,0],”values“:[5.***,5.***,0.0],”state“:[1,1,0],”freeEntries“:1,”distinct“:2,”lowWaterMark“:0,”highWaterMark“:1,”minLoadFactor“:0.2,”maxLoadFactor“:0.5},”size“:2,”lengthSquared“:-1.0}”} Points: 3 模糊 K 均值聚类算法 模糊 K 均值聚类算法是 K 均值聚类的扩展,它的基本原理和 K 均值一样,只是它的聚类结果允许存在对象属于多个簇,也就是说:它属于我们前面介绍过的可重叠聚类算法。为了深入理解模糊 K 均值和 K 均值的区别,这里我们得花些时间了解一个概念:模糊参数(Fuzziness Factor)。 与 K 均值聚类原理类似,模糊 K 均值也是在待聚类对象向量集合上循环,但是它并不是将向量分配给距离最近的簇,而是计算向量与各个簇的相关性(Association)。假设有一个向量 v,有 k 个簇,v 到 k 个簇中心的距离分别是 d1,d2„ dk,那么 V 到第一个簇的相关性 u1可以通过下面的算式计算: 计算 v 到其他簇的相关性只需将 d1替换为对应的距离。 从上面的算式,我们看出,当 m近似 2 时,相关性近似 1;当 m近似 1 时,相关性近似于到该簇的距离,所以 m 的取值在(1,2)区间内,当 m 越大,模糊程度越大,m 就是我们刚刚提到的模糊参数。 讲了这么多理论的原理,下面我们看看如何使用 Mahout 实现模糊 K 均值聚类,同前面的方法一样,Mahout 一样提供了基于内存和基于 Hadoop Map/Reduce 的两种实现 FuzzyKMeansClusterer 和 FuzzyMeansDriver,分别是清单 5 给出了一个例子。 清单 5.模糊 K 均值聚类算法示例 public static void fuzzyKMeansClusterInMemory(){ // 指定聚类的个数 int k = 2;// 指定 K 均值聚类算法的最大迭代次数 int maxIter = 3;// 指定 K 均值聚类算法的最大距离阈值 double distanceThreshold = 0.01;// 指定模糊 K 均值聚类算法的模糊参数 float fuzzificationFactor = 10;// 声明一个计算距离的方法,这里选择了欧几里德距离 DistanceMeasure measure = new EuclideanDistanceMeasure();// 构建向量集,使用的是清单 1 里的二维点集 List List clusters.add(new SoftCluster(v, clusterId++, measure));} // 调用 FuzzyKMeansClusterer 的 clusterPoints 方法进行模糊 K 均值聚类 List for(SoftCluster cluster : finalClusters.get(finalClusters.size()-1)){ System.out.println(“Fuzzy Cluster id: ” + cluster.getId()+ “ center: ” + cluster.getCenter().asFormatString());} } public class fuzzyKMeansClusterUsingMapReduce { // 指定模糊 K 均值聚类算法的模糊参数 float fuzzificationFactor = 2.0f;// 指定需要聚类的个数,这里选择 2 类 int k = 2;// 指定最大迭代次数 int maxIter = 3;// 指定最大距离阈值 double distanceThreshold = 0.01;// 声明一个计算距离的方法,这里选择了欧几里德距离 DistanceMeasure measure = new EuclideanDistanceMeasure();// 设置输入输出的文件路径 Path testpoints = new Path(“testpoints”);Path output = new Path(“output”);// 清空输入输出路径下的数据 HadoopUtil.overwriteOutput(testpoints);HadoopUtil.overwriteOutput(output);// 将测试点集写入输入目录下 SimpleDataSet.writePointsToFile(testpoints);// 随机的选择 k 个作为簇的中心 Path clusters = RandomSeedGenerator.buildRandom(testpoints, new Path(output, “clusters-0”), k, measure);FuzzyKMeansDriver.runJob(testpoints, clusters, output, measure, 0.5, maxIter, 1, fuzzificationFactor, true, true, distanceThreshold, true);// 打印模糊 K 均值聚类的结果 ClusterDumper clusterDumper = new ClusterDumper(new Path(output, “clusters-” + maxIter),new Path(output, “clusteredPoints”));clusterDumper.printClusters(null);} 执行结果 Fuzzy KMeans Clustering In Memory Result Fuzzy Cluster id: 0 center:{“class”:“org.apache.mahout.math.RandomAccessSparseVector”, “vector”:“{”values“:{”table“:[0,1,0],”values“:[1.***3,1.***,0.0],”state“:[1,1,0],”freeEntries“:1,”distinct“:2,”lowWaterMark“:0,”highWaterMark“:1,”minLoadFactor“:0.2,”maxLoadFactor“:0.5},”size“:2,”lengthSquared“:-1.0}”} Fuzzy Cluster id: 1 center:{“class”:“org.apache.mahout.math.RandomAccessSparseVector”, “vector”:“{”values“:{”table“:[0,1,0],”values“:[7.***,7.***,0.0],”state“:[1,1,0], ”freeEntries":1, 一些内容来自于我能找到的资料和我自己的理解,不保证正确,但是大致应该是没错的。 一、排版引擎 首先厘清一下浏览器内核是什么东西。 英文叫做:Rendering Engine,中文翻译很多,排版引擎、解释引擎、渲染引擎,现在流行称为浏览器内核,至于为什么流行这么称呼,请自行领悟。 Rendering Engine,顾名思义,就是用来渲染网页内容的,将网页的内容和排版代码转换为可视的页面。因为是排版,所以肯定会排版错位等问题。为什么会排版错位呢?有的是由于网站本身编写不规范,有的是由于浏览器本身的渲染不标准。 现在有几个主流的排版引擎,因为这些排版引擎都有其代表的浏览器,所以常常会把排版引擎的名称和浏览器的名称混用,比如常的说IE内核、Chrome内核。其实这样子是不太合理的,因为一个完整的浏览器不会只有一的排版引擎,还有自己的界面框架和其它的功能支撑,而排版引擎本身也不可能实现浏览器的所有功能。下面罗列一下几款主流的排版引擎和浏览器。 1、Trident(Windows) IE浏览器所使用的内核,也是很多浏览器所使用的内核,通常被称为IE内核。基于Trident内核的浏览器非常多,这是因为Trident内核提供了丰富的调用接口。老的Trident内核(比如常说的IE6内核)一直是不遵循W3C标准的,但是由于它的市场份额最大,所以后果就是大量的网站只支持老的Trident内核,依据W3C标准写的网页在老的Trident内核下面又出现偏差。目前可供调用的最新版的Trident内核是IE9所用的内核,相较之前的版本对W3C标准的支持增强了很多。 Trident内核的浏览器: IE6、IE7、IE8(Trident 4.0)、IE9(Trident 5.0)、IE10(Trident 6.0); 世界之窗 1、世界之窗 2、世界之窗3; 360安全浏览器1、360安全浏览器2、360安全浏览器3、360安全浏览器4、360安全浏览器5; 傲游 1、傲游2;搜狗浏览器1;腾讯TT;阿云浏览器(早期版本)、百度浏览器(早期版本)、瑞星安全浏览器、Slim Browser; GreenBrowser、爱帆浏览器(12 之前版本)、115浏览器、155浏览器; 闪游浏览器、N氧化碳浏览器、糖果浏览器、彩虹浏览器、瑞影浏览器、勇者无疆浏览器、114浏览器、蚂蚁浏览器、飞腾浏览器、速达浏览器、佐罗浏览器; 2、Gecko(跨平台) Netscape6启用的内核,现在主要由Mozilla基金会进行维护,是开源的浏览器内核,目前最主流的Gecko内核浏览器是Mozilla Firefox,所以也常常称之为火狐内核。因为Firefox的出现,IE的霸主地位逐步被削弱,Chrome的出现则是加速了这个进程。非Trident内核的兴起正在改变着整个互联网,最直接的就是推动了编码的标准化,也使得微软在竞争压力下不得不改进IE。不过比较可惜的是,虽然是开源的,也开发了这么多年,基于Gecko的浏览器并不多见,除了一些简单的改动(坑爹的X浏览器)或者是重新编译(绫川ayakawa、tete009),深度定制或者增强型外壳的还比较少见。另外就是有一些其它软件借用了Gecko内核,比如音乐管理软件SongBird。 常见的Gecko内核的浏览器 Mozilla Firefox、Mozilla SeaMonkey Epiphany(早期版本)、Flock(早期版本)、K-Meleon3、KHTML(Linux) KDE开发的内核,速度快捷,容错度低。这个内核可能不见得很多人知道,但是后面再看下去你就明白了。 常见的KHTML内核的浏览器:Konqueror4、WebKit(跨平台) 由KHTML发展而来,也是苹果给开源世界的一大贡献。是目前最火热的浏览器内核,火热倒不是说市场份额,而是应用的面积和势头。因为是脱胎于KHTML,所以也是具有高速的特点,同样遵循W3C标准。 常见的WebKit内核的浏览器:Apple Safari、Symbian系统浏览器 5、Chromium(跨平台) 维基百科里面并没有将Chromium从WebKit分出来,这个区分完全是基于我个人的恶趣味。记得以前看过一个大牛的博文说过,Chromium把WebKit的代码梳理得可读性提高很多,所以以前可能需要一天进行编译的代码,现在只要两个小时就能搞定。这个我自己也没有考究过,但是估计可信。这个也能解释为什么Gecko和WebKit出来了这么久,第三方编译、定制的版本并不多,但是由Chromium衍生出来的浏览器早就满坑满谷了。 常见的Chromium内核的浏览器:Chromium、Google Chrome、SRWare Iron、Comodo Dragon6、Presto(跨平台) Opera的内核,准确地说,是Opera 7.0及以后版本的内核,Opera 3.5-6.1版本使用的内核叫做Elektra。不用说,Presto对W3C标准的支持也是很良好的。虽然我很喜欢Opera,但是我对Presto的渲染速度一直有保留态度。之前在OperaChina论坛看见有人说过,Presto优先解析文字,保证可阅读性,媒体资源的渲染放后。 常见的Presto内核的浏览器:Opera7、其它 http://zh.wikipedia.org/wiki/排版引擎 二、JavaScript引擎 说完了排版引擎,接下来说说JavaScript引擎。顾名思义,JavaScript引擎就是用来渲染JavaScript的。为什么要单独拿出来说呢?因为它涉及到跑分。经常看见很多文章在报道说哪个浏览器更快,其实大部分说的就是JavaScript的渲染速度,而不是页面的载入速度。在网速许可的情况下,其实各个浏览器的页面载入速度差别不大(Opera逊色一些)。那是不是说对比JavaScript的渲染速度其实没有意义?也不是这么说,因为现在JavaScript在页面中的比重会越来越大,越来越多的动态页面开始大量借助JavaScript,比如现在主流的SNS、邮箱、网页游戏,所以JavaScript的渲染速度也是一个很重要的指标。JavaScript的渲染速度越快,动态页面的展示也越快。Opera在JavaScript引擎的跑分上面一直都是很牛逼的,一般来说最新测试版之间PK,Opera基本都会夺冠。 1、Chakra 查克拉,IE9启用的新的JavaScript引擎。 2、SpiderMonkey/TraceMonkey/JaegerMonkey SpiderMonkey应用在Mozilla Firefox 1.0-3.0,TraceMonkey应用在Mozilla Firefox 3.5-3.6版本,JaegerMonkey应用在Mozilla Firefox 4.0及后续的版本。 3、V8 应用于Chrome、傲游3。 4、Nitro 应用于Safari 4及后续的版本。 5、Linear A/Linear B/Futhark/Carakan Linear A应用于Opera 4.0-6.1版本,Linear B应用于Opera 7.0~9.2版本,Futhark应用于Opera 9.5-10.2版本,Carakan应用于Opera 10.5及后续的版本。 6、KJS KHTML对应的JavaScript引擎。 三、几个测试 1、V8引擎 http://v8.googlecode.com/svn/data/benchmarks/v6/run.html 现在很多“双核”浏览器都用它来跑分测试JavaScript引擎,分数越高越好。 2、Acid3 http://acid3.acidtests.org/ 标准支持测试,分数越高越好,满分是100分。 3、HTML5 http://?hl=zh-cn Beta在线安装包: http://?hl=zh-CN&extra=betachannel Dev在线安装包: http://?hl=zh-CN&extra=devchannel Canary在线安装包: http://?hl=zh-CN&extra=canarychannel Stable离线安装包: http://?hl=zh-CN&standalone=1 Beta离线安装包: http://?hl=zh-CN&standalone=1&extra=betachannel Dev离线安装包: http://?hl=zh-CN&standalone=1&extra=devchannel Canary离线安装包: http://?hl=zh-CN&standalone=1&extra=canarychannel(6)Opera 官网: http://www.xiexiebang.com/ 正式版: http://www.xiexiebang.com/download/ 测试版: http://snapshot.opera.com/windows/latest 各类搜索引擎的优缺点 搜索引擎是指根据一定的策略、运用特定的计算机程序从互联网上搜集信息,在对信息进行组织和处理后,为用户提供检索服务,将用户检索相关的信息展示给用户的系统。搜索引擎包括全文索引、目录索引、元搜索引擎、垂直搜索引擎、集合式搜索引擎、门户搜索引擎与免费链接列表等。百度和谷歌等是搜索引擎的代表。 百度搜索引擎 优点: 1、知识交流功能强,可以实现互动、知识的共享。 2、强大的地图导航功能,方便路线的查询。 3、对于中国人的阅读和浏览更为熟悉,服务更加本土化。 4、提供RSS(简易信息聚合)新闻订阅服务。 5、提供历史和各省市新闻查阅。 6、图片格式多样化,基本上都有。 7、百度还提供搜索flash的功能。 8、如果无法打开某个搜索结果,或者打开速度特别慢,“百度快照”能帮您解决问题。每个被收录的网页,在百度上都存有一个纯文本的备份,称为“百度快照”。不过,百度只保留文本内容。 9、提供高级搜索语法搜索功能。 10、提供错别字提醒、英汉互译词典、计算器和度量衡转换、拼音提示、股票与列车时刻表和飞机航班查询等功能。 缺点: 1、由于知识来源广,重复的内容多而繁杂。 2、页面布局不合理,页面没有充分利用。 3、更新时间迅速的优势没有充分发挥。 4、商业味太重,你搜索的关键字的首页基本都价排名出价高的企业占据了,很难找到你需要的真正自然搜索的结果,百度的搜索排名技术不够权威; 5、搜索结果中广告、垃圾网站和死链比较多。 Google搜索引擎 优点: 1、容量大和范围广:其数据库如今是最大的,包括了PDF、DOC、PS及其他许多文件类型。 2、易用性较强。 3、根据站点的链接数和权威性进行相关性排序。 4、网页缓存归档,浏览过的网页被编入索引。 5、还有其他数据库:Google群组、新闻和目录等数据库。 缺点: 1、网页排版不新颖,美观度不高。 2、搜索特性有限,没有嵌套搜索,没有截词搜索,不支持全部的布尔逻辑检索。 3、链接搜索必须准确,而且不完整。 死链率比较高,中文网站检索的更新频率不够高,不能及时淘汰过时的链接。 5、只能把网页的前101KB和PDF的大约前120KB编入索引。 6、可能会在不告诉你的情况下,检索复数/单数、同义词和语法变体。 雅虎搜索引擎 优点: 1、搜索引擎数据库庞大而且新颖。 2、包括页面的缓存拷贝。 3、也包括指向雅虎目录的链接。 4、支持全部的布尔逻辑检索。 5、雅虎的分类目录提供交叉显示 6、雅虎中文提供了一份规范、科学、层次丰富的中文网站分类目录。 7、全球各地的中文网站,包括简体、繁体和图形中文网站。在同类搜索引擎中,它收录的网站数目属于较丰富者。 缺点: 1、缺少某些高级的搜索特性,譬如截词搜索。 2、搜索文本内能输入字数较少,不能详细录入。 3、连接搜索需要加入http://。包括有些付费才能加入的站点,比较麻烦。 5、搜索结果的描述和在用户搜索过程中的提示说明也不太清晰、明了,搜索的易用性稍差。 4、死链率较高 而且缺少一些应有的高级搜索功能。 5、许多网站没有简介,反馈信息中没有网站注册日期项。 6、检索反馈的结果多有重复,这是由于其目录交叉显示引起的。复杂条件查询功能较弱。无法控制每次反馈网站的个数和排列方式等。 7、中文版的原始版本似为繁体中文,而简体中文版是在此基础上使用软件自动转换生成的。故简体中文版个别地方出现以“□”代表的缺字。 8、英文版中许多非常出色的功能和服务,在中文版中未予提供。 搜狗搜索引擎 优点: 1、独立的门户网站,查询简洁方便。 2、特有分类主题一体化查询功能。 3、互动式“搜索提示”。 4、专家优选信息价值高。 5、与电子商务紧密结合。 6、查询语句简单化。 7、多元搜索“直通车”。 8、搜狗指数,瞬间播报。 9、免费网站登陆,参与竞价排名。 缺点: 1、搜狗域名问题。搜狗域名(www.sogou.com)沿用了搜狐(www.sohu.com)的一部分,使得搜狗的域名不太符合中国用户的使用习惯。对于搜狗,很多用户在域名输入的时候会按照惯性输入(www.sougou.com),但是搜索的结果却不是搜狗,这样用户会觉得很麻烦。 2、专业性较弱。搜狗在搜索方面信息的收录量虽然很大,但是在信息检索划分上还是存在一定的问题,页面混乱,展示结果繁杂。搜狗在搜索上还是未能做精做深。搜狗搜索是偏向于娱乐性,虽然音乐搜索做的很专业,但是还是未能脱离搜索引擎同质化的现状。 3、市场竞争力不强,搜索主页认知度低。 4、首选忠诚度低,首选用户流失率高。 MSN搜索引擎 优点: 1、庞大、独特的数据库。 2、具有创建查询的“搜索生成器”和布尔逻辑检索功能。 3、网页的缓存拷贝,包含缓存数据。 4、拥有自动本地搜索的选项。 5、MSN搜索引擎使用自己的Web数据库,还有独立的新闻、图片和本地等数据库。 6、有指向微软电子百科全书(Encarta Encyclopedia)内容的链接。 缺点: 1、高级特性有限。 2、截词操作符功能不稳定。 4、没有标题搜索、截词和词根操作。 职业化的内涵 所谓职业化,就是一种工作状态的标准化、规范化和制度化,即要求人们把社会或组织交代下来的岗位职责,专业地完成到最佳,准确扮演好自己的工作角色。 以国际通行的概念分析,职业化的内涵至少包括四个方面:一是以“人事相宜”为追求,优化人们的职业资质; 二是以“胜任愉快”为目标,保持人们的职业体能; 三是以“创造绩效”为主导,开发人们的职业意识; 四是以“适应市场”为基点,修养人们的职业道德。 职业化引擎核心内容 第一部分:职业化心态力——职业人的精神食粮 1.阳光心态——每个人都有选择快乐的权利 ◆ 烽火猎聘成功者将挫折、困难归因于个人能力、经验的不完善,强调内在,他们乐意不断地向好的方向改进和发展。 ◆ 失败者怪罪于机遇、环境的不公,强调外在的因素造就了他们的人生位置,他们总是抱怨、等待与放弃。 ◆ 阳光心态不是得意的心态,而是一种不骄不躁、处乱不惊的平常心态。 2.共赢心态——个人完不成的事业,团队可以完成◆ 团队协作的收获往往要超过团队各成员单独努力所获的简单累加,超出的部分就是协作的超值回报。 ◆ 共赢的本质就是共同创造、共同进步,共赢是团队的内在气质。◆ 共赢强调发挥优势,尊重差异,合作互补。 3.空杯心态——只有把水倒出来,才能装更多的水 ◆ 空杯心态就是一种挑战自我的永不满足。 ◆ 空杯心态就是对自我的不断扬弃和否定。 ◆ 空杯心态就是不断清洗自己的大脑和心灵。 ◆ 空杯心态就是不断学习、与时俱进。 4.老板心态——把老板的钱当成自己的钱,把老板的事当成自己的事◆ 如果你是老板,目前这个项目是不是需要再考虑一下,再做投资的决定? ◆ 如果你是老板,面对公司中无谓的浪费会不会采取必要的措施?◆ 如果你是老板,对自己的言行举止是不是应该更加注意,以免造成不良的后果? 5.感恩心态——对别人时怀感激,对周围的一切时怀感激 ◆ 感恩是一种追求幸福的过程和生活方式。 ◆ 感恩是一种利人利己的责任。 ◆ 感恩让我们坦然面对工作中的起伏、挫折和困难。 第二部分:职业化人际力——职业人的事业之基 1.人际关系的黄金法则和白金法则 ◆ 黄金法则:你希望别人怎么对待你,你就怎么对待别人。黄金法则已成明日黄花。 ◆ 白金法则:别人希望你怎么对待他们,你就怎么对待他们。 2.情感账户——每个人心灵中都有个情感银行账户 ◆ 首先了解对方,遵守承诺,有礼貌,主动承担责任,接收别人的回馈。这是存款。 ◆ 要求对方首先了解自己,违背承诺,不礼貌,傲慢地推卸责任,拒绝别人的回馈。这是取款。 3.炼就同理心——相互关怀、理解,才能融洽 ◆ 表层同理心:站在别人的角度去理解;深层同理心:理解别人的感情成分。 ◆ 同理心训练:专心听对方说话,让对方觉得被尊重;正确辩识对方情绪、解读对方说话的含义。 4.人际关系的六种模式 ◆ 人输我赢。巧取豪夺,坑蒙拐骗,损人利己。 ◆ 人赢我输。迫于压力,委曲求全,损己利人。 ◆ 人赢我赢。送人玫瑰,手留余香,利人利己。◆ 人输我输。杀敌一千,自伤八百,两败俱伤。◆ 不输不赢。生意不成情谊在,好聚好散,没有交易。◆ 孤芳自赏。自扫门前雪,独善其身。5.沟通不良的弊病◆ 价值判断;追根究底;好为人师;自以为是。第三部分:职业化执行力——职业人的立身之本1.承诺底线——说到就要做到◆ 在沃尔玛,“我们说的就是我们做的”是每一位员工的信条。◆ “这就是我的责任”:锁定责任,彻底执行。2.走出任务的陷阱——任务是工作的假象◆ 任务是完成差事、例行公事、应付了事。◆ 陷入任务的泥潭让你有苦劳、没功劳。◆ 员工做任务,公司就会在这名员工身上形成负债。3.坚守结果底线——没有了结果,执行将是空谈◆ 结果是执行的起点,也是执行的终点,有了结果,执行才有意义。◆ 结果三要素:客户要的;有价值的;可交换的。 ◆ 结果是对行动、任务的最终状态的描述,是可以用来与别人交换的成果。 4.结果价值判断——有价值,才是结果 ◆ 外包思想定义结果:一项工作如果外包给其他公司来做,只有满意的结果才会付报酬。 ◆ 底线结果思维训练:完成任何一项工作都有底线,破坏了这个底线,就无从谈结果。 5.客户价值——团队的执行信仰 ◆ 提供客户价值才能获得客户忠诚。 ◆ 客户价值对企业的回报是保证企业不死。 ◆ 细节决定生死。用细节感动客户,是企业强大的基础。第四部分:职业化时控力——职业人的第二生命 1,时间对每个人都一样的公平 ◆ 时间的特质:绝对稀缺,不能交换,无法节省,无法储存的,无法增加,一去不返。 ◆ 运用时间管理规划,进行有效的个人时间管理。如果你愿意,成功就离你很近。 2.,有效个人管理的四个步骤 ◆ 确定角色:确定你认为重要的角色。 ◆ 选择目标:分别为每个角色确定未来一周要达成的目标。 ◆ 安排进度:为这些目标确定完成时间。◆ 逐日调整:每日清晨依据行事历,安排一天做事的顺序。3,时间管理方法演练◆ 生命倒计时法;时间价值分析法;生活节奏法;日程表管理法。◆ 德鲁克时间管理法;柯维时间管理法;艾维·利时间管理法。4,时间管理的四个象限◆ 重要并且紧急:及时而全力以赴。◆ 重要但不紧急:计划并有条不紊地进行,做到未雨绸缪。◆ 紧急但不重要:花一点时间做,请人代办,集中处理。◆ 不重要且不紧急:有空再做或者不做。 5,快乐而又高效地度过每一天 ◆ 学会做计划:回顾目标——制定计划——衡量时间——预测意外——排列顺序。 ◆ 积极开始、积极结束:好好利用早上的时间,带着好心情投入一天的工作。 ◆ 在工作之前做好自我调整:根据工作的重要性和紧迫性把头一天制定的计划及目标再梳理一遍。 ◆ 在离开工作岗位之前做好清算:今日事,今日毕;做好第二天的工作计划。 第五部分:职业化团结力——职业人的发展之道 1,人生“三成” ◆ “不成”:依赖别人、受别人控制和影响的人终将一事无成。◆ “小成”:孤军奋战、不善合作的人,只能取得有限的成功。◆ “大成”:善于合作、懂得分享的、利人利己的人能成就轰轰烈烈的大事业。 2,没有完美的个人,只有完美的团队 ◆ 优秀的团队并非全是由优秀的个人组成,但优秀的团队一定能塑造出优秀的个人。 ◆ 单靠个人无法完成任务,一个没有组织性的团队也不能圆满完成任务,默契配合、相互支持的团队才能成功。 ◆ 没有完美的个人,但通过团队协作,完全能够成就完美的事业。3,团队发展的四个阶段 ◆ 组合期:对团队规则不熟悉;彼此陌生,互相猜忌;对团队目标和个人目标不了解;花了力气,效果不好;合适的人选、确定目标;运用社交活动、小组讨论。 ◆ 摸索期:成员冲突、彼此敌对;信息不通、出现混乱;调整工作内容及角色;开放沟通渠道、共享信息;领导建立威信、沟通会议;确定问题解决办法。 ◆ 共识期:执行或者修正既定计划;及时修正建立工作模式;建立团队忠诚。 ◆ 发挥期:对队员适当鼓励;保持团队的效率;加强队员沟通;接受挑战性的任务。 4,高效团队的特征 ◆ 清晰的共同目标;恰当的领导;相关的技能;有效的组织机构;相互信任的精神;一致的承诺;分享成果;开放的沟通;外部的有效支持。第六部分:职业化创新力——职业人的进化动力 1,创新力缔造核心竞争力 ◆ 成功者将创新当作一种习惯;争做第一的企业才会成功。◆ 超越性思维和制导性思维是树立核心竞争力的关键。 2,影响创新力的几大障碍 ◆ 功能固着心理:一个人看到一种惯常的事物功用或联系后,就很难看出其他新的功用和联系。 ◆ 思维定势效应:固定的思维模式使人们习惯于从固定的角度来观察、思考事物,以固定的方式来接受事物。 ◆ 自我选择效应:一旦个人选择了某一人生道路,就存在向这条路走下去的惯性并且不断自我强化。 ◆ 从众效应:每个人都有不同程度的从众倾向,总是倾向跟随大多数人的想法或态度,以证明自己并不孤立。 3,学习力造就创新力 ◆ 学习力是最可贵的生命力。历史绵延很久的“一次性学习时代”已告终结,学历教育已被终身教育取代。 ◆ 学习力是最活跃的创造力。勇于创新,敢为人先,鼓励尝试,宽容失败,这是成功企业所共有的文化元素。 ◆ 学习力是最本质的竞争力。学习是一种投资,创新就要投资自己,提高自己的附加价值,培养自己不论处于什么情况下,都能存活的本领。 专题七近代以来科学技术的辉煌 三 人类文明的引擎 课标要求:以蒸汽机的发明和电气技术的应用为例,说明科学技术进步对社会发展的作用 考试说明:改良蒸汽机 电气技术的应用 重点难点:重点:蒸汽时代和电气时代的到来及其对人类的影响。 难点:理解科学与技术的结合是社会需求发展的结果,是人类文明不断积淀的结果。 第一学习时间 自主预习 课前知识体系图解 1、请阅读课本,提取关键词。 2、画出本课知识的思维导图。 材料阅读 1.瓦特改进蒸汽机 〔材料〕瓦特,一七三六年一月十九日出生在苏格兰西海岸的一个小港口。父亲虽是一个造船工人,有相当良好的教养。瓦特从小就很聪明,在留校读书时数学最好,尤其对于自然科学特别有兴趣„„十八岁时„„在一个理学机械师门下当学徒„„ 有一次瓦特从一个学生的口里,听到有关纽昆门抽水机的故事。结果立刻引起他对蒸汽机的兴趣„„然而瓦特最初想象的蒸汽机,也是利用大气的压力来推动活塞,这 和纽昆门的抽水机并没有什么不同。到后来瓦特在活塞的上面,用蒸汽代替了大气的压力,如此一改良才算发明了真正的蒸汽机„„后来瓦特又继续改良,用蒸汽的 压力使活塞在上面也能动„„瓦特为了把往复运动改成回转运动,就又研究出“曲柄”„„结果又被歹人窃取设计图„„瓦特又研究出‘日月齿车’„„瓦特完成回 转运动以后,他所发明的蒸汽机就可以用来发动各种机械„„蒸汽机一发明,就立刻被广泛使用,大规模的工厂一个个出现„„立刻使以前的手工业完全机械化,因 此那些在家庭式手工艺工厂做工的人,就都转移到资本家经营的大工厂去。 „„瓦特所发明的蒸汽机,就是欧洲产业革命的原动力,的确是一件具有划时代历史意义的大事。 ──冯作民《西洋全史》,第511~514页 【解读】这则材料对我们了解瓦特改进蒸汽机的情况有参考价值。最早关于蒸汽机的设想,可追溯到公元1世纪的古罗马,利用火力推开教堂大门的机器,是世界上最早的蒸汽机。17世纪末18世纪初,矿井排水问题十分突出,众多的发明家思考并设法去解决。最早发明利用蒸汽力排水机器的是英国人萨弗里,后经过许多人的研究创 造,不断推陈出新,英国铁匠出身的纽可门发明了一种新的可供实用的蒸汽机,慢慢地纽可门蒸汽机的缺点凸显出来,它加工粗糙,效率低。瓦特正是在纽可门蒸汽 机的基础上发明了新蒸汽机。 瓦特是英国伟大的发明家,格拉斯格大学的机械修理工,他勤奋好学,善于观察,掌握了法文、德文和意大利文,读过许多著名物理学家、化学家的著作。1769年,瓦特对纽可门蒸汽机进行了一系列的重大改进,制成了单向蒸汽机,不久取得专利权。1785年他研制成了联动式蒸汽机。从最初接触蒸汽机到瓦特蒸汽机研制成功,瓦特经过了二十多年的艰难历程,他虽然多次受挫,屡遭失败,但仍以对科学的执著进行革新,把那种最初只能用于矿井抽水的蒸汽机,变成了即将翻转世界的蒸汽机。 (1)瓦特勤奋好学,勇于创新。(2)瓦特的蒸汽机被广泛使用,成为欧洲产业革命的原动力。 2、史料: 材料一 627年秋,唐贞观元年,28岁的玄奘法师„„开始了孤身求法的西行历程。历经一年的跋涉到达印度境内。 材料二 1872年,斐利亚·福克先生在伦敦改良俱乐部和会友们打赌,要在八十天内环游地球一周。在当时的情况下,这确实是一件很难办到的事。但最后他提前一天完成了。(《八十天环游地球》简介) 材料三 1992年10月,一架“协和”号超音速客机,为了纪念哥伦布发现美洲新大陆500周年,只用了32小时49分绕地球一周,创造了环球飞行的新纪录。 解读: 三段材料反映了不同的时代里,交通发展的情况及其对人们生活和社会发展的影响。在古代中国:步行是一般人常用的出行方式。在19世纪中后期,经历了第一次工业革命之后,蒸汽机车的发明和应用使出行更加方便和快速。而从20世纪以来,飞机的应用使交通更加便捷,人们之间的沟通也就更加密切。材料充分体现了科学技术的发展对人类社会进步的重要作用。 3、工业革命对世界的影响 〔材料一〕1820~1890年,英国70年间生产总量增加了60倍,人口增加3倍,人均产值增加13倍。一个劳动力平均每年工作时间从3 000小时减到1 700小时,劳动生产率提高了20倍,人均寿命从35岁增至79岁。 英国农业在国民生产总值中的比重从1770年的45%降至1841年的22%,1901年再降至6%,工业相应地从4%升至34%和69%。 〔材料二〕18世纪下半叶至19世纪初,英国企图垄断先进的工业生产技术,曾颁布禁止熟练工人移民和机器出口的禁令。但是新技术是封锁不住的,许多技术人员和熟练工人不顾禁令移民国外。 法国在18世纪70年代引进英国的珍妮纺纱机以后,欧美大国相继引进。他们不仅大量地进口英国的机器,而且还引进英国企业家和技术人才。 19世纪40年代,法国一半的铁路公司有英国的股份,英国还在美国、俄罗斯等地建立一系列股份公司。 〔材料三〕最早开始工业革命的英国也是最大的殖民地国家。英国的机器制成的产品摧毁了印度手纺机和手织机,破坏了殖民地的城乡工场手工业,英国的蒸汽机和科学技术在印度全境把农业和手工业彻底摧毁了。 英国还打破了亚非国家的封闭和与世隔绝,破坏了传统社会经济结构。促进了这些地区的资本主义生产关系的缓慢发展。把亚非国家卷入工业化的洪流,变成世界资本主义大市场中的一部分。 ──[美]斯塔夫里阿诺斯《全球通史──1500年以后的世界》,上海社会科学出版社,200 1年版 【解读】透 过资料可看到:(1)对英国的影响:机器生产取代手工劳动,工厂制取代了手工工场,极大地提高了生产率,促进了资本主义的发展;工人的生活状况有所改善,平均工作时间缩 短,人均寿命提高,促使产业结构的变化,工业在国民生产中的总值大大超过农业。(2)对其他国家的影响,英国的技术和技术工人流向国外,工 业革命扩展到英国以外的其他国家;密切了英国同其他国家的经济联系。(3)对亚洲的影响:较早完成工业革命的英、法、美等先进资本主义国家都积极进行殖民 扩张,使亚洲一些国家沦为其殖民地或半殖民地,把它们卷入资本主义世界市场,加剧了当地的贫困和落后,造成东方从属于西方的局面,另一方面,它们也破坏了 亚洲国家的封闭与隔绝,破坏了当地传统的社会经济结构和落后的生产关系,在一定程度上改造了亚洲社会。(4)工业革命极大地促进了生产力的发展。引起了社 会结构的重大变革,密切了世界各地的联系,改变了世界面貌;科学技术是第一生产力,应充分利用工业革命的成果增强国力,促进社会的进步和发展。 预习检测1、18世纪,在英国揭开了工业革命的序幕的行业是() A、棉纺织业 B、毛纺织业 C、交通运输 D、机械加工制造业 2. 成为一切生产部门和交通运输部门“万能的原动机”发明是()A、电动机 B、火车机车 C、蒸汽机 D、汽船 3.第一次工业革命最关键的步骤和主要环节是() A.机器的使用 B.交通工具的创新 C.蒸汽机的改良和使用 D.电的应用 4.人类进入20世纪后,工业发展的主要动力是() A.水力 B.蒸汽动力 C.电力 D.风力 5.第二次工业革命对世界经济发展的影响是() A.亚非拉地区的工业飞速发展 B.促成欧洲经济一体化的形成 C.资本主义世界市场进一步扩大 D.世界贸易中心开始转移到大西洋沿岸 1、A 2、C 3、A 4、C 5、C 6、阅读下列材料 材料一: 材料一: 19世纪巴黎路灯 材料二:恩格斯在《英国工人阶级状况》中指出:“新生的工业能够成长起来,只是因为它用机器代替了手工工具, ……并把民间的一切差别化为工人和资本家之间的对立。” 材料三:资产阶级,由于一切工具的改进,由于交通运输的及其便利,把一切民族甚至最野蛮的民族都融进文明了。它迫使一切民族──如果他们不想灭亡的话──采用资产阶级的生活方式……它按照自己的面貌为自己创造出一个世界。 请回答: 阅读三段材料,说说蒸汽机的发明和电的广泛使用,在哪些方面推动了社会变革?有哪些负面影响?(1)社会生产力大幅度提高;社会结构和世界形势的改变,世界日益成为一个整体;城市化进程加快和人们生活更加丰富多彩 (2)负面影响:技术的运用,生产效率的提高,使资本家剥削工人的手段更加隐蔽;自然环境的破坏等 第二学习时间 课堂检测反馈 1、19世纪上半期,水上运输的交通工具发生了重大变革,1819年“萨凡纳”号轮船,从美国出发,横渡大西洋成功,这一成就的取得主要得益于() A.发电机的发明和使用 B.电动机的发明和使用 C.长距离输变电技术的日趋成熟 D.蒸汽机的发明与使用 答案:D。辨析:本题主要考查基础知识的灵活运用。“萨凡纳”号轮船是用蒸汽机驱动的轮船。故答案为D。 2、瓦特改良蒸汽机产生的重大变化和影响有() ①解决了工业发展的动力问题 ②出现了发明和使用机器的热潮 ③使世界的经济和文化联系日益密切 ④使交通运输业发生了彻底改变 A.①②③ B.①③④ C.①②③④ D.①②④ 答案:C。辨析:本题考查学生的分析能力,属于组合式选择题,可以用排除法,也可以用直选法。故答案为C。 3、一部网络小说中曾经有这样的情节:“主人公从21世纪回到了过去,正在室内与大物理学家牛顿闲谈,天色渐晚,牛顿起身拉亮了身边的台灯„„”由此可知() A.小说作者出现了低级错误,他并不了解历史 B.牛顿时代已经发明了电灯 C.主人公回到了第二次工业革命时期 D.电的发明是第二次工业革命的重要标志 答案:A辨析:本题以网络小说切题,旨在考查分析能力。牛顿生活在17世纪,电的使用和电灯的发明属于第二次工业革命的成就,时间属于19世纪的中后期。故答案为A。 4、蒸汽机的改良和发电机的产生都是()A.科学理论与生产技术密切结合的产物 B.社会生产发展需要的结果 C.第一次工业革命的重大成果 D.第二次工业革命的重大成果 答案:B 辨析:蒸汽机的改良和发电机的创制都是工业生产发展需要的结果。蒸汽机的改良是由瓦特在早期蒸汽机的基础上完成的,不是依靠某种先进科学理论,而是依靠经验改良而 成的,因此,蒸汽机不是科学理论与生产技术密切结合的产物,而发电机则是以电磁感应现象为理论创制成功的,在这里科学与技术达到完美的结合。故答案为B。 5、从蒸汽机的发明和电气技术的应用带来的后果来看,科学技术进步对社会发展的作用是() ①社会生产力大幅度提高 ②社会结构和世界形势因此改变 ③人类的生活更加丰富多彩 ④资本主义经济危机周期性出现 A.①②③④ B.①②③ C.②③④ D.①②④ 答案:B。辨析:本题考查科技革命对社会发展作用的理解分析能力,蒸汽机的发明使人类进入“蒸汽时代”,电气技术的应用使人类进入到“电气时代”。它们既促进了生产力的发展,又促进了生产关系、社会结构的变化,使人类的生活更加丰富多彩。而资本主义经济危机是资本主义基本矛盾发展的必然结果,与科技进步没有必然联系。故答案为B。 6、阅读下列材料 材料一这一变化就是工业革命。全部近代史上没有什么别的事件更惊人的影响普通人的生活或对人类进步开拓了更广阔的前景,或造成了更剧烈的苦难和不满。但19世纪开始,进入第一个四分之一之时,在英国和其他地方——工厂越来越多,直到有些部门,工厂制造几乎完全代替了家庭制造业。 材料二为了生存,青年人有的全家离开了村舍家园或者小农场,去追随工厂…… 材料三资本家购买原料,拥有机器和工厂,并出卖制成品。工业革命使资本家成为工业的主人。……最后,拐弯抹角地,工业革命导致了民主政治。……在这种情况下,工业革命发生的不幸,引起了民主政治和劳工运动。 —— 卡尔顿·海斯 请回答: (1)归纳材料一关于工业革命的观点(2)材料二说明什么? (3)材料三说明工业革命导致的什么结果? (1)机器代替手工业;(2)工业革命引起经济结构的变化。(3)资产阶级和无产阶级的对立;民主政治和劳工运动的兴起。课堂互动研讨 1、请在课前预习及检测的基础上,提出你有疑难的问题,与小组同学讨论,解决问题。 2、重点难点探究 当堂训练 1、(2008高考重庆卷)下列表述与第二次工业革命史实不符的是()A.汽船的发明改变了水上运输的状况 B.发电机的问世使电力成为一种新能源 C.无线电报的出现增强了各国的联系 D.内燃机的创制推动了石化工业的发展 答案:A.辨析:本体通过对重大发明的影响的比较,注重了对教材主干知识工业革命的考查,符合新课程改革的方向。解题的关键在于区分两次工业革命的重大成就,汽船属于第一次工业革命中的重大发明,排除。故答案为A。 2、(2008高考上海综合能力)18世纪下半叶开始的工业革命,以蒸汽机的改进和运用为标志,大大推动了人类交通工具的改进。1814年世界上第一辆蒸汽火车机车的发明者是() A.瓦特 B.史蒂芬孙 C.富尔顿 D.莱特兄弟 答案:B 辨析:本题考查学生的记忆能力,世界第一辆蒸汽火车机车的发明者应该是史蒂芬孙。故答案为B。 3、(2008年高考全国卷)诺贝尔获奖者道格拉斯·诺斯写到:“假设一位古希腊人被奇迹般的送到1750年的英国,他或她会发现许多熟悉的事物。不过,如果希腊人再晚两个世纪被送进来,就会发现,自己宛如置身于一个‘幻想的’世界,什么都不认识,甚至什么都不理解。”作者提出最后一种假设的主要历史依据是() A.工业社会形成与发展 B.人口爆炸性增长 C.经济全球化进程加速 D.各国间交往密切 答案:A辨析:审题时应该注意时间,1750年后的两个世纪后,即1950年。与古代相比,工业文明在方方面面都出现巨大的变化,不能为古希腊人所理解。B项人口问题无法体现材料“什么都不认识,什么都不理解”;C项错在时间,经济全球化加速在20世纪90年代而非50年代;D项过于单一,国际交往问题无法体现材料“什么都不认识,什么都不理解”。故答案为A。 4、(2008年广东文科基础)下列属于第二次工业革命时期的发明的是()A.蒸汽机 B.轮船 C.汽车 D.电视机 答案:C 辨析:本题考查学生的记忆能力,A、B都是第一次工业革命的发明。 5、(2008年四川高考)经济学家马歇尔指出,在美国西北部,农业机械如果受损,农民用电报告知机械商店受损部件的号码,下一班火车就能带来新部件,农民自己就能安装。马歇尔所描绘的情景最早可能出现于() A.18世纪中后期 B.19世纪初期 C.19世纪中后期 D.20世纪前期 答案:C 辨析:注意提干中的有效信息:“电报”、“火车”,火车出现于第一次工业革命,电报出现于第二次工业革命。而题干中的限定词语为“最早可能出现于”。 6、阅读下列材料: 材料一 材料二 材料三 瓦特(1736-1819)和蒸汽机 使用蒸汽机的工厂越来越多 法拉第和电磁感应实验装臵 材料四 天才是百分之一的灵感加上百分之九十九的汗水。 我平生从来没有做出过一次偶然的发明,我的一切发明都是深思熟虑和严格实验的结果。请回答: (1)材料一、三分别反映了什么历史信息?各产生了什么影响? (2)据材料二,分析说明工厂的特点,并归纳瓦特蒸汽机问世的影响是什么?(3)据材料四,指出一个科学家应该具备什么样的科学精神? 参考答案:(1)信息与影响:瓦特改良了蒸汽机,使人类进入“蒸汽时代”。法拉第发现了电磁感应现象,为发电机的研制奠定了理论基础,此后,发电机、电动机相继出现,人类进入“电气时代”。(2)特点:规模大,使用机器生产,同时带来了环境污染。影响:解决了工业发展中的动力问题,出现了发明和使用机器的热潮,人类进入“蒸汽时代”。(3)为科学献身,一丝不苟的精神。 课后练习 精析精练学力测评部分> finalClusters = KMeansClusterer.clusterPoints(pointVectors, clusters, measure, maxIter, distanceThreshold);
> clustersM = DisplayClustering.loadClusters(output);
> finalClusters = FuzzyKMeansClusterer.clusterPoints(points, clusters, measure, distanceThreshold, maxIter, fuzzificationFactor);// 打印聚类结果
第二篇:浏览器引擎介绍
第三篇:各类引擎的优缺点
第四篇:职业化引擎核心内容
第五篇:专题七 人类文明引擎学案