慎选与计算,推荐算法

直线即表示Clara和罗Bert的偏好完全一致。他们都觉着Phoenix是最棒的乐队,然后是Blues
Traveler、Norah
Jones。借使Clara和罗Bert的视角不雷同,那么落在直线上的点就越少。

余弦相似度(cos simliarity)


只要,有八个乐队,用户A=(x1,y1),用户B=(x2,y2),那么她们之间的余弦相识度为:

图片 1

表 6 Angelica 与 Bill 的余弦相似度

图片 2

放大到n维,用户A和B,对n个乐队的评分分别为(x1,x2,…,xn)和(y1,y2,…,yn),则他们之间的余弦相似度为

图片 3

《Dataminingguide》书阅读,第叁章 推荐系统入门

当大家计算Hailey和Veronica的相距时会发现贰个题材:他们共同评论的乐队唯有两支(Norah
Jones和The
Strokes),而Hailey和Jordyn共同评论了五支乐队,那犹如会潜移默化大家的测算结果,因为Hailey和Veronica之间是二维的,而Haily和Veronica之间是五维的。曼哈顿距离和欧几里得距离在数据全部的情形下效果最棒。怎么着处理缺点和失误数据,那在斟酌世界仍是二个活跃的话题。本书的接轨内容会进展一些座谈,这里先不开始展览。现在,让我们初阶营造叁个推荐系统啊。

本文内容

  • 用户评分表
  • 曼哈顿(Manhattan)距离
  • 欧式(Euclidean)距离
  • 余弦相似度(cos simliarity)

引进算法以及数据挖掘算法,总计“距离”是必须的~近年来想搭1个引进系统,看了有的材料和书《写给程序员的多寡挖掘指南》,此书不错,推荐大家看看,讲解得很透彻,有理论有代码,还有相关网站。看完后,你即刻就能把引进算法应用在您的品种中~

正文先首要表达什么计算物品或用户之间的“距离”,陆续会介绍引进算法本人~

叁 、闵克夫斯基距离

能够将曼哈顿距离和欧几里得距离归结成三个公式,这几个公式称为闵可夫斯基距离:

image.png

Qashqai值越大,单个维度的差值大小会对完全距离有更大的影响。

也正是x之差的断然值加上y之差的相对值,那样他们的离开正是4。

曼哈顿(Manhattan)距离


测算距离最简便易行的艺术是曼哈顿相距。如果,先考虑二维意况,唯有八个乐队 x

y,用户A的评价为(x1,y1),用户B的褒贬为(x2,y2),那么,它们中间的曼哈顿距离为

图片 4

故而,Angelica 与 比尔  之间的曼哈顿距离如下表所示。

表 2 Angelica 与 比尔 的曼哈顿距离

图片 5

那正是说,Angelica 与 Bill 之间的曼哈顿距离为
9,即第1列减第叁列的相对化值,最终累加。

只顾,必须是那七个用户都评分的乐队。

能够加大到n个乐队,即n维向量,用户 A(x1,x2,…,xn),用户B(y1,y2,…,yn)
,那么它们之间的曼哈顿距离为

图片 6 

则用户之间的曼哈顿距离如下表所示。

表 3 用户之间的曼哈顿距离

图片 7

曼哈顿相差的最大利益正是简单,只是加减法而已。假诺有几百万个用户,总结起来会相当慢。

不光可以扩大到 n 个乐队,当然也能够扩展到 m
个用户,它们得以形成三个矩阵。上边包车型地铁其余距离同理。

Netflix 当初出 100 万美元奖励给能提高推荐算法 十分一准确率的集体或人,而博得奖金的人就是运用了一种叫奇异矩阵分解的法子~

肆 、 Pearson相关周全

分数膨胀:例如用户对乐队的评分,能够窥见每一种用户的打分专业10分分裂,A的四分相当于B的6分仍旧四分?

杀鸡取卵办法之一正是利用Pearson相关全面。

皮尔森相关周详用于度量四个变量之间的相关性,它的值在-1到1以内,1意味完全符合,-1表示完全相左。

皮尔逊相关全面的总计公式是:

image.png

地方的公式除了看起来相比复杂,另2个题材是要博得计算结果必须对数据做往往有益于,辛亏有别的1个公式,能够总计Pearson相关周详的近似值:

image.png

其一公式看起来更为错综复杂,而且计算结果不太稳定,有自然误差存在,可是最大的多少是用代码完成的时候可以只遍历三回数据。

“`python
>>> r = recommender(users)
>>> r.recommend(‘Jordyn’)
[(‘Blues Traveler’, 5.0)]
>>> r.recommend(‘Hailey’)
[(‘Phoenix’, 5.0), (‘Slightly Stoopid’, 4.5)]
“`

用户评分表


差不多上,推荐算法能够有二种简单的思绪:一是形似的用户,二是一般的物料。

前者,把与您相似的用户喜爱(或购销或臧否高)的货品推荐给您,约等于说,假设你跟有些用户的喜好相比像样,那么就足以把那么些用户喜好的,而你不精通(或没浏览过,或没置办过等等)的物料推荐给您。什么叫“喜好接近”,正是对少数物品的评论能够,购买也罢,都相比接近,就以为,你和她喜好同一~

前者的后天不足在于,用户的评价毕竟是个别,想想,你评价过(显式评价)的物品有个别许!当先1/3依旧隐式评价,所谓隐式评价,假使您购买3个物料,这显著你会喜欢她,否则也不会买~由此,利用一般的用户是有局限性的。不及使用一般的物品来推荐。

上面“距离”算法主要针对总括用户之间的离开(相似性)。

即使,几个用户对八个乐队进行评分,如下表所示。横向是用户,纵向是乐队。

表 1 用户评分表

图片 8

⑤ 、 余弦相似度

余弦相似度的测算中会略过那么些非零值。它的总括公式是:

image.png

举个例证:

image.png

Pearson相关周详的计算公式是:

源代码 dis.py


#

#  dis.py

#

 

from math import *

 

teams = [

    "Blues Traveler", 

    "Broken Bells", 

    "Deadmau5", 

    "Norah Jones", 

    "Phoenix", 

    "Slightly Stoopid", 

    "The Strokes", 

    "Vampire Weekend"

]

 

 

users = {

    "Angelica": {

        "Blues Traveler": 3.5,

        "Broken Bells": 2,

        "Norah Jones": 4.5,

        "Phoenix": 5,

        "Slightly Stoopid": 1.5,

        "The Strokes": 2.5,

        "Vampire Weekend": 2

    },

    "Bill": {

        "Blues Traveler": 2,

        "Broken Bells": 3.5,

        "Deadmau5": 4,

        "Phoenix": 2,

        "Slightly Stoopid": 3.5,

        "Vampire Weekend": 3

    },

    "Chan": {

        "Blues Traveler": 5,

        "Broken Bells": 1,

        "Deadmau5": 1,

        "Norah Jones": 3,

        "Phoenix": 5,

        "Slightly Stoopid": 1

    },

    "Dan": {

        "Blues Traveler": 3,

        "Broken Bells": 4,

        "Deadmau5": 4.5,

        "Phoenix": 3,

        "Slightly Stoopid": 4.5,

        "The Strokes": 4,

        "Vampire Weekend": 2

    },

    "Hailey": {

        "Broken Bells": 4,

        "Deadmau5": 1,

        "Norah Jones": 4,

        "The Strokes": 4,

        "Vampire Weekend": 1

    },

    "Jordyn": {

        "Broken Bells": 4.5,

        "Deadmau5": 4,

        "Norah Jones": 5,

        "Phoenix": 5,

        "Slightly Stoopid": 4.5,

        "The Strokes": 4,

        "Vampire Weekend": 4

    },

    "Sam": {

        "Blues Traveler": 5,

        "Broken Bells": 2,

        "Norah Jones": 3,

        "Phoenix": 5,

        "Slightly Stoopid": 4,

        "The Strokes": 5

    },

    "Veronica": {

        "Blues Traveler": 3,

        "Norah Jones": 5,

        "Phoenix": 4,

        "Slightly Stoopid": 2.5,

        "The Strokes": 3

    }

}

 

def manhattan(rating1, rating2):

    """Computes the Manhattan distance. Both rating1 and rating2 are dictionaries

       of the form {'The Strokes': 3.0, 'Slightly Stoopid': 2.5}"""

    distance = 0

    commonRatings = False 

    for key in rating1:

        if key in rating2:

            distance += abs(rating1[key] - rating2[key])

            commonRatings = True

    if commonRatings:

        return distance

    else:

        return -1 #Indicates no ratings in common

 

 

def euclidean(rating1, rating2):

    """Computes the euclidean distance. Both rating1 and rating2 are dictionaries

       of the form {'The Strokes': 3.0, 'Slightly Stoopid': 2.5}"""

    distance = 0

    commonRatings = False 

    for key in rating1:

        if key in rating2:

            distance += pow(rating1[key] - rating2[key],2)

            commonRatings = True

    if commonRatings:

        return sqrt(distance)

    else:

        return -1 #Indicates no ratings in common

 

 

def minkowski(rating1, rating2, r):

    """Computes the minkowski distance. Both rating1 and rating2 are dictionaries

       of the form {'The Strokes': 3.0, 'Slightly Stoopid': 2.5}"""

    distance = 0

    commonRatings = False 

    for key in rating1:

        if key in rating2:

            distance += pow(abs(rating1[key] - rating2[key]),r)

            commonRatings = True

    if commonRatings:

        return pow(distance, 1.0/r)

    else:

        return -1 #Indicates no ratings in common

 

 

def cosineSimilarity (rating1, rating2):

    """Computes the Cosine Similarity distance. Both rating1 and rating2 are dictionaries

       of the form {'The Strokes': 3.0, 'Slightly Stoopid': 2.5}"""

    sum_xy = 0

    sum_sqr_x = 0

    sum_sqr_y = 0

    for key in teams:

        if key in rating1 and key in rating2:

            sum_xy += rating1[key]* rating2[key]

            sum_sqr_x += pow(rating1[key], 2)

            sum_sqr_y += pow(rating2[key], 2)

        elif key not in rating1 and key in rating2:

            sum_xy += 0

            sum_sqr_x += 0

            sum_sqr_y += pow(rating2[key], 2)

        elif key in rating1 and key not in rating2:

            sum_xy += 0

            sum_sqr_x += pow(rating1[key], 2)

            sum_sqr_y += 0

        else:

            sum_xy += 0

            sum_sqr_x += 0

            sum_sqr_y += 0

 

    if sum_sqr_x ==0 or sum_sqr_y==0:

        return -1 #Indicates no ratings in common

    else:

        return sum_xy / (sqrt(sum_sqr_x) * sqrt(sum_sqr_y))

 

 

def pearson(rating1, rating2):

    """Computes the pearson distance. Both rating1 and rating2 are dictionaries

       of the form {'The Strokes': 3.0, 'Slightly Stoopid': 2.5}"""

    sum_xy = 0

    sum_x = 0

    sum_y = 0

    sum_x2 = 0

    sum_y2 = 0

    n = 0

    for key in rating1:

        if key in rating2:

            n += 1

            x = rating1[key]

            y = rating2[key]

            sum_xy += x * y

            sum_x += x

            sum_y += y

            sum_x2 += pow(x, 2)

            sum_y2 += pow(y, 2)

    # now compute denominator

    denominator = sqrt(sum_x2 - pow(sum_x, 2) / n) * sqrt(sum_y2 - pow(sum_y, 2) / n)

    if denominator == 0:

        return 0

    else:

        return (sum_xy - (sum_x * sum_y) / n) / denominator

二 、 欧几里得距离

也许用(X,Y)来代表壹人,那么三人里面包车型地铁离开就是:

欧几里得距离总结

在盘算五个用户之间的相似度距离时,只取双方都评价过的货品。

曼哈顿相差和欧几里得距离在 数码完全 的景况下效果最棒。

SMO算法,主要选择在SVM中。

欧式(Euclidean)距离


除了那一个之外曼哈顿距离外,还足以测算多个用户之间的欧式距离。

仍旧先考虑多少个乐队 x 和 y
的动静,假使,用户A=(x1,y1),用户B=(x2,y2),那么它们之间的欧式距离:

图片 9 

Angelica 与 Bill 之间的曼哈顿距离如下表所示。

表 4 Angelica 与 Bill 的欧式距离

图片 10

推广到 n 个乐队,用户 A(x1,x2,…,xn),用户B(y1,y2,…,yn)

图片 11

表 5 用户之间的欧式距离

图片 12

但曼哈顿距离和欧式距离,有个毛病。相比较一下 Hailey 与 Veronica 和
Jordyn,Hailey
与前者只有三个乐队评过分,而与膝下是四个。换句话说,Hailey 与 Veronica
的相距是基于二维的,而 Hailey 与 Jordyn 是依据五维。想想都觉着不经常。

故此,曼哈顿相距和欧式距离符合数据对比密实、缺点和失误值比较少的情状。假使缺点和失误值很多,余弦相似度就比较适当。

曼哈顿相差和欧式距离,有通用公式,称为闵可夫斯基距离(Minkowski
Distance)。

① 、 曼哈顿相差

最简便的距离总括情势。在二维总计模型中,种种人都能够用(X,Y)的点来代表。例如(X1,Y1)来表示Aimee,(X2,Y2)来表示另1个人人,那么他们之间的曼哈顿距离就是:
|X1-X2|+|Y1-Y2|

也正是x之差的相对值加上y之差的相对值。

曼哈顿距离的长处之一就是总结速度快,对于Twitter那样须要计算百万用户之间的相似度时就相当有利于。

比方大家要为Amy推荐乐队,她爱好Phoenix、Passion Pit、以及Vampire
Weekend。和她最相似的用户是鲍勃,他也喜爱那三支乐队。他的阿爸为WalterOstanek乐队演奏手风琴,所以受此影响,他给了那支乐队5星评价。依据大家现在的推荐逻辑,大家会将那支乐队推荐给Amy,但有可能他并不爱好。

总结

该采取哪个种类相似度计算: 如若数据存在“分数膨胀”,正是用Pearson相关周到。

假如数量里面相比较“密集”,变量之间基本存在公有值,且那一个离开数据是老大重要的,那就采取欧几里得或曼哈顿距离。

若果数量是稀疏的,则动用余弦相似度。

那么,怎样相比那么些用户呢?比如Hailey的陆分相当于Jordan的5分依然四分呢?我觉得更就好像五分。那样一来就会潜移默化到推荐介绍系统的准头了。

![](img/chapter-2/chapter-2-50.png)

其间Difference一栏表示双方评分之差的断然值,加起来也就是9,也即是她们之间的距离。

### 皮尔逊相关周详

“`python
users = {“Angelica”: {“Blues Traveler”: 3.5, “Broken Bells”: 2.0, “Norah
Jones”: 4.5, “Phoenix”: 5.0, “Slightly Stoopid”: 1.5, “The Strokes”:
2.5, “Vampire Weekend”: 2.0},
         “Bill”:{“Blues Traveler”: 2.0, “Broken Bells”: 3.5, “Deadmau5”:
4.0, “Phoenix”: 2.0, “Slightly Stoopid”: 3.5, “Vampire Weekend”: 3.0},
         “Chan”: {“Blues Traveler”: 5.0, “Broken Bells”: 1.0,
“Deadmau5”: 1.0, “Norah Jones”: 3.0, “Phoenix”: 5, “Slightly Stoopid”:
1.0},
         “Dan”: {“Blues Traveler”: 3.0, “Broken Bells”: 4.0, “Deadmau5”:
4.5, “Phoenix”: 3.0, “Slightly Stoopid”: 4.5, “The Strokes”: 4.0,
“Vampire Weekend”: 2.0},
         “Hailey”: {“Broken Bells”: 4.0, “Deadmau5”: 1.0, “Norah Jones”:
4.0, “The Strokes”: 4.0, “Vampire Weekend”: 1.0},
         “Jordyn”:  {“Broken Bells”: 4.5, “Deadmau5”: 4.0, “Norah
Jones”: 5.0, “Phoenix”: 5.0, “Slightly Stoopid”: 4.5, “The Strokes”:
4.0, “Vampire Weekend”: 4.0},
         “Sam”: {“Blues Traveler”: 5.0, “Broken Bells”: 2.0, “Norah
Jones”: 3.0, “Phoenix”: 5.0, “Slightly Stoopid”: 4.0, “The Strokes”:
5.0},
         “Veronica”: {“Blues Traveler”: 3.0, “Norah Jones”: 5.0,
“Phoenix”: 4.0, “Slightly Stoopid”: 2.5, “The Strokes”: 3.0}
        }
“`

题材就在于大家只依靠最相似的 **一个**
用户来做推荐,如果那么些用户有些与众差别的偏好,就会间接呈今后举荐内容里。解决办法之一是找寻多少个一般的用户,那里即将用到K最靠近算法了。

* `r = 1` 该公式即曼哈顿相距
* `r = 2` 该公式即欧几里得距离
* `r = ∞` 非常的大距离

### 项目实行

那多个人都会对推荐结果具有进献,难点在于我们什么样分明他们的比重呢?咱们间接用相关周密的比例来讲述,Sally的比例是0.8/2=五分二,埃里克是0.7/2=35%,阿曼达则是25%:

“`python
>>> recommend(‘Chan’, users)
[(‘The Strokes’, 4.0), (‘Vampire Weekend’, 1.0)]
>>> recommend(‘Sam’, users)
[(‘Deadmau5’, 1.0)]
“`

很简单把?上面大家计算那些公式:

当r = 2时,大家获取欧几里得距离的计算公式:

![](img/chapter-2/chapter-2-32.png)

## 使用Python代码来代表数据(终于要从头编制程序了)

测试一下:

![](img/chapter-2/chapter-2-23.png)

能够看来,Moonlight
Sonata这首歌小编播放了2八回,但很有或许你2遍都并未听过。事实上,上边列出的那个歌曲或许您一首都没听过。别的,iTunes上有1500万首音乐,而自我只听过伍仟首。所以说单个用户的数额是
*稀疏*
的,因为非零值较完整要少得多。当大家用1500万首歌曲来相比较三个用户时,很有恐怕他们中间从未此外交集,这样一来就不许计算他们之间的离开了。

对于Angelica,大家获取了空的重返值,也等于说大家鞭长莫及对其展开推荐。让大家看看是何地不常常:

从肆 、伍 、6那三首歌来看,两个人离Jake的离开是同等的,但总结出的曼哈顿距离却不这么展现:

“`python
>>> recommend(‘Angelica’, users)
[]
“`

![](img/chapter-2/chapter-2-39.png)

安琪ica最相似的用户是Veronica,让大家回头看看数据:

Linda和Jake只共同评分了一首歌曲:Chris Cagle的 *What a Beautiful Day*
。琳达打了3分,Jake打了六分,所以他们中间的曼哈顿距离为2,欧几里得距离为:

users = {“Angelica”: {“Blues Traveler”: 3.5, “Broken Bells”: 2.0,
                      “Norah Jones”: 4.5, “Phoenix”: 5.0,
                      “Slightly Stoopid”: 1.5,
                      “The Strokes”: 2.5, “Vampire Weekend”: 2.0},
         
         “Bill”:{“Blues Traveler”: 2.0, “Broken Bells”: 3.5,
                 “Deadmau5”: 4.0, “Phoenix”: 2.0,
                 “Slightly Stoopid”: 3.5, “Vampire Weekend”: 3.0},
         
         “Chan”: {“Blues Traveler”: 5.0, “Broken Bells”: 1.0,
                  “Deadmau5”: 1.0, “Norah Jones”: 3.0, “Phoenix”: 5,
                  “Slightly Stoopid”: 1.0},
         
         “Dan”: {“Blues Traveler”: 3.0, “Broken Bells”: 4.0,
                 “Deadmau5”: 4.5, “Phoenix”: 3.0,
                 “Slightly Stoopid”: 4.5, “The Strokes”: 4.0,
                 “Vampire Weekend”: 2.0},
         
         “Hailey”: {“Broken Bells”: 4.0, “Deadmau5”: 1.0,
                    “Norah Jones”: 4.0, “The Strokes”: 4.0,
                    “Vampire Weekend”: 1.0},
         
         “Jordyn”:  {“Broken Bells”: 4.5, “Deadmau5”: 4.0,
                     “Norah Jones”: 5.0, “Phoenix”: 5.0,
                     “Slightly Stoopid”: 4.5, “The Strokes”: 4.0,
                     “Vampire Weekend”: 4.0},
         
         “Sam”: {“Blues Traveler”: 5.0, “Broken Bells”: 2.0,
                 “Norah Jones”: 3.0, “Phoenix”: 5.0,
                 “Slightly Stoopid”: 4.0, “The Strokes”: 5.0},
         
         “Veronica”: {“Blues Traveler”: 3.0, “Norah Jones”: 5.0,
                      “Phoenix”: 4.0, “Slightly Stoopid”: 2.5,
                      “The Strokes”: 3.0}
        }

### 欧几里得距离

第2,下表展现有三个人用户对那两本书做了评价:

![](img/chapter-2/chapter-2-49.png)

小编们能够将曼哈顿距离和欧几里得距离归结成多个公式,这一个公式称为闵可夫斯基距离:

## 你欣赏的东西我也爱不释手

Linda和埃里克喜欢相同的音乐,他们的评分列表中有20首相同的的歌曲,且评分均值相差不到0.5!所以他们中间的曼哈顿距离为20
x 0.5 = 10,欧几里得距离则为:

表中的短横表示这位用户并未给那支乐队打分。大家在盘算三个用户的离开时,只行使他们都评价过的乐队,比如要总括Angelica和比尔的相距,大家只会用到5支乐队。那多少个用户的曼哈顿距离为:

简单测试一下:

![](img/chapter-2/chapter-2-43.png)

注:本书的兼具代码能够在[这里](code/)找到。

![](img/chapter-2/chapter-2-60.png)

![](img/chapter-2/chapter-2-35.png)

![](img/chapter-2/chapter-2-53.png)

![](img/chapter-2/chapter-2-19.png)

“`python
def computeNearestNeighbor(username, users):
    “””总括有所用户至username用户的离开,倒序排列并重临结果列表”””
    distances = []
    for user in users:
        if user != username:
            distance = manhattan(users[user], users[username])
            distances.append((distance, user))
    # 按距离排序——距离近的排在后面
    distances.sort()
    return distances
“`

“`python
from math import sqrt

最简易的相距总计方法是曼哈顿距离。在二维模型中,每一种人都足以用(x,
y)的点来表示,那里作者用下标来代表不相同的人,(x<sub>1</sub>,
y<sub>1</sub>)表示Aimee,(x<sub>2</sub>,
y<sub>2</sub>)表示那位神秘的X先生,那么他们中间的曼哈顿距离正是:

运用勾股定理,大家得以如下总计距离:

![](img/chapter-2/chapter-2-47.png)

### 如何找到相似的用户?

### 古怪的气象

### 总结曼哈顿距离

![](img/chapter-2/chapter-2-26.png)

“`python
def manhattan(rating1, rating2):
    “””总计曼哈顿距离。rating1和rating2参数中蕴藏的数码格式均为
    {‘The Strokes’: 3.0, ‘Slightly Stoopid’: 2.5}”””
    distance = 0
    for key in rating1:
        if key in rating2:
            distance += abs(rating1[key] – rating2[key])
    return distance
“`

所以余弦相似度是:

![](img/chapter-2/chapter-2-21.png)

归纳扫一眼前面包车型大巴数目(只怕用事先讲过的离开总计公式),我们能够发现Ann的深爱和Sally更为相似。

![](img/chapter-2/chapter-2-48.png)

Cooper评价了26首歌曲,个中25首和Jake是相同的。他们对每首歌曲的评论差值唯有0.25!

![](img/chapter-2/chapter-2-25.png)

CSV文件包蕴了三张表:

![](img/chapter-2/chapter-2-34.png)

### N维模型

**运作示例**

终极,大家构成以上内容来展开推介。倘诺笔者想为Hailey做推荐,那里本身找到了离他离开近来的用户Veronica。然后,小编会找到出Veronica评价过但Hailey没有评论的乐队,并如若Hailey对那么些面生乐队的褒贬会和Veronica相近。比如,Hailey没有评论过Phoenix乐队,而Veronica对那几个乐队打出了五分,所以我们认为Hailey也会喜欢这支乐队。上面包车型客车函数就落到实处了这一逻辑:

Aimee的距离方今,在她的浏览历史中能够见见他曾给巴奇加比索的《发条女孩》打过五星,于是大家就能够把那本书推荐给X先生。

“`python
def recommend(username, users):
    “””重回推荐结果列表”””
    # 找到距离近年来的用户
    nearest = computeNearestNeighbor(username, users)[0][1]
    recommendations = []
    # 找出那位用户评价过、但自个儿从不评价的乐队
    neighborRatings = users[nearest]
    userRatings = users[username]
    for artist in neighborRatings:
        if not artist in userRatings:
            recommendations.append((artist,
neighborRatings[artist]))
    # 依照评分举行排序
    return sorted(recommendations, key=lambda artistTuple:
artistTuple[1], reverse = True)
“`

![](img/chapter-2/chapter-2-62.png)

![](img/chapter-2/chapter-2-58.png)

刚刚我们一味对两本书进行评论(二维模型),下边让我们扩雷文杰下,尝试更复杂的模型。假诺大家以往要为二个在线音乐网站的用户推荐乐队。用户能够用1至5星来评论一个乐队,当中包含半星(如2.5星)。下表体现了5人用户对8支乐队的评头品足:

![](img/chapter-2/chapter-2-27.png)

大家用上文中“偏好完全一致”的示范:

下边大家就足以用它来为Hailey做推荐了:

末段获得:

  1. 兑现三个乘除曼哈顿距离和欧几里得距离的不二法门;
    2.
    本书的网站上有几个涵盖25部电影片评论论的[数据集](http://guidetodatamining.com/guide/ch2/Movie\_Ratings.csv),实现一个推荐算法。

# 第②章:推荐系统入门

它们的模是:

今昔让我们采纳2个一发真实的数码集。Cai-NicolasZeigler从书本漂流站收集了跨越100万条评论数据——278,8伍13个人用户为271,379本书打了分。那份数据(匿名)可以从[这几个地点](http://www.informatik.uni-freiburg.de/~cziegler/BX/)获得,有SQL和CSV两种格式。由于特殊符号的关系,这些数据无法直接加载到Python里。我做了一些清洗,可以从\[这里下载\](http://guidetodatamining.com/guide/ch2/BX-Dump.zip)。

![](img/chapter-2/chapter-2-29.png)

那种光景在数量挖掘领域称为“分数膨胀”。Clara最低给了伍分——她怀有的打分都在4至伍分之间。大家将它绘制成图表:

![](img/chapter-2/chapter-2-2.png)

从而,那1个巨型公式的积极分子正是70 – 67.5 = 2.5。

Newton法,首要接纳 在线型回归中;

![](img/chapter-2/chapter-2-16.png)

### 应该利用哪个种类相似度?

    def userRatings(self, id, n):
        “””再次来到该用户评分最高的物料”””
        print (“Ratings for ” + self.userid2name[id])
        ratings = self.data[id]
        print(len(ratings))
        ratings = list(ratings.items())
        ratings = [(self.convertProductID2name(k), v)
                   for (k, v) in ratings]
        # 排序并回到结果
        ratings.sort(key=lambda artistTuple: artistTuple[1],
                     reverse = True)
        ratings = ratings[:n]
        for rating in ratings:
            print(“%s\t%i” % (rating[0], rating[1]))

## Python推荐模块

![](img/chapter-2/chapter-2-51.png)

![](img/chapter-2/chapter-2-36.png)

![](img/chapter-2/chapter-2-56.png)

**为什么吧?**

其中:

故而率先要做的做事是找到相似的用户。那里用最简易的二维模型来叙述。假如用户会在网站用五颗星来评论一本书——没有星表示书写得很糟,五颗星表示很好。因为大家用的是二维模型,所以仅对两本书举办评价:Stephenson的《雪崩》(纵轴)和Larsson的《龙纹身的女孩》(横轴)。

**掌握了吧?** 那就试试总计其余多少个用户之间的距离呢。

以此公式就算看起来更为复杂,而且其总计结果会不太稳定,有一定误差存在,但它最大的帮助和益处是,用代码达成的时候能够只遍历一回数据,大家会在下文看到。首先,我们将以此公式做一个分解,计算下边那个表明式的值:

难点就在于数量中的0值对结果的影响相当大,所以用0代替空值的主意并不及原来的方程好。还有一种转移的法门是一个钱打二十五个结“平均值”——将三人一齐评论过的歌曲分数除以歌曲数量。

![](img/chapter-2/chapter-2-18.png)

    def pearson(self, rating1, rating2):
        sum_xy = 0
        sum_x = 0
        sum_y = 0
        sum_x2 = 0
        sum_y2 = 0
        n = 0
        for key in rating1:
            if key in rating2:
                n += 1
                x = rating1[key]
                y = rating2[key]
                sum_xy += x * y
                sum_x += x
                sum_y += y
                sum_x2 += pow(x, 2)
                sum_y2 += pow(y, 2)
        if n == 0:
            return 0
        # 计算分母
        denominator = (sqrt(sum_x2 – pow(sum_x, 2) / n)
                       * sqrt(sum_y2 – pow(sum_y, 2) / n))
        if denominator == 0:
            return 0
        else:
            return (sum_xy – (sum_x * sum_y) / n) / denominator

![](img/chapter-2/chapter-2-30.png)

![](img/chapter-2/chapter-2-40.png)

![](img/chapter-2/chapter-2-13.png)

上边的公式除了看起来相比复杂,另一个题材是要取得总计结果必须对数码做往往遍历。幸而我们有其它一个公式,能够总结Pearson相关全面的近似值:

因此,假诺数据是凝聚的,曼哈顿相距和欧几里得距离都以适用的。那么稀疏的数码足以行使啊?我们来看三个也和音乐有关的言传身教:假若有四人,每人都给100首音音乐评论过分。

全部的盘算结果如下:

比方作者要为Ann做推荐,并令K=3。使用Pearson相关周全获得的结果是:

class recommender:

“`python
>>> computeNearestNeighbor(“Hailey”, users)
[(2.0, ‘Veronica’), (4.0, ‘Chan’), (4.0, ‘Sam’), (4.5, ‘Dan’), (5.0,
‘Angelica’), (5.5, ‘Bill’), (7.5, ‘Jordyn’)]
“`

上文Python代码中的loadBookDB方法能够加载这几个数据,用法如下:

就此我们从直觉上看Cooper和Keylsey离Jake的相距应该相似。但是,当大家总计他们之间的曼哈顿距离和欧几里得距离时(代入0值),会发现Cooper要比Keylsey离Jake近得多。

 

![](img/chapter-2/chapter-2-11.png)

    def __init__(self, data, k=1, metric=’pearson’, n=5):
        “”” 开首化推荐模块
        data   陶冶多少
        k      K邻近算法中的值
        metric 使用何种距离总括方法
        n      推荐结果的数码
        “””
        self.k = k
        self.n = n
        self.username2id = {}
        self.userid2name = {}
        self.productid2name = {}
        # 将离开总括情势保存下去
        self.metric = metric
        if self.metric == ‘pearson’:
            self.fn = self.pearson
        #
        # 假诺data是三个字典类型,则保留下来,不然忽略
        #
        if type(data).__name__ == ‘dict’:
            self.data = data

def pearson(rating1, rating2):
    sum_xy = 0
    sum_x = 0
    sum_y = 0
    sum_x2 = 0
    sum_y2 = 0
    n = 0
    for key in rating1:
        if key in rating2:
            n += 1
            x = rating1[key]
            y = rating2[key]
            sum_xy += x * y
            sum_x += x
            sum_y += y
            sum_x2 += pow(x, 2)
            sum_y2 += pow(y, 2)
    # 计算分母
    denominator = sqrt(sum_x2 – pow(sum_x, 2) / n) * sqrt(sum_y2 –
pow(sum_y, 2) / n)
    if denominator == 0:
        return 0
    else:
        return (sum_xy – (sum_x * sum_y) / n) / denominator
“`

![](img/chapter-2/chapter-2-24.png)

仍用上文的音乐站点为例,x和y分别代表七个用户,d(x,
y)表示他们中间的相距,n表示他们联合评论过的乐队数量,大家此前曾经做过测算:

设若他们三人对Grey 沃德ens的评分以及加权后的结果如下:

我们整本书都会追究那些题材,以下是一对升迁:

### 新的多寡集

![](img/chapter-2/chapter-2-31.png)

**先休息一下吧**

![](img/chapter-2/chapter-2-3.png)

Aimee给《龙纹身》和《雪崩》都打了五颗星,神秘的X先生分别打了两星和四星,那样他们之间的欧几里得距离便是:

**题目在哪里?**

下边我们编辑2个函数来找出距离近来的用户(其实该函数会回去2个用户列表,按距离排序):

“`python
>>> manhattan(users[‘Hailey’], users[‘Veronica’])
2.0
>>> manhattan(users[‘Hailey’], users[‘Jordyn’])
7.5
>>>
“`

### 用户的题材

* Jake(左):爵士乐的忠实观众。
* Linda和埃里克(右):我们爱六十年代的灵魂乐!

![](img/chapter-2/chapter-2-14.png)

我们来看上边的数目:

总的说来,曼哈顿相距和欧几里得距离在多少完整的景观下会运维得万分好,假若数据相比较稀疏,则要考虑动用余弦距离。

**注意** 由于数据集相比大,大致须要几十秒的年月加载和查询。

在Python中,我们能够用多样主意来叙述上表中的数据,那里作者选取Python的字典类型(或许叫做关联数组、哈希表)。

    def recommend(self, user):
       “””重临推荐列表”””
       recommendations = {}
       # 首先,获取将近用户
       nearest = self.computeNearestNeighbor(user)
       #
       # 获取用户评价过的货物
       #
       userRatings = self.data[user]
       #
       # 总结总距离
       totalDistance = 0.0
       for i in range(self.k):
          totalDistance += nearest[i][1]
       # 汇总K邻近用户的评分
       for i in range(self.k):
          # 计算饼图的各种分片
          weight = nearest[i][1] / totalDistance
          # 获取用户名称
          name = nearest[i][0]
          # 获取用户评分
          neighborRatings = self.data[name]
          # 获得没有评论过的货品
          for artist in neighborRatings:
             if not artist in userRatings:
                if artist not in recommendations:
                   recommendations[artist] =
(neighborRatings[artist]
                                              * weight)
                else:
                   recommendations[artist] =
(recommendations[artist]
                                              +
neighborRatings[artist]
                                              * weight)
       # 先河引进
       recommendations = list(recommendations.items())
       recommendations = [(self.convertProductID2name(k), v)
                          for (k, v) in recommendations]
       # 排序并回到
       recommendations.sort(key=lambda artistTuple: artistTuple[1],
                            reverse = True)
       # 返回前n个结果
       return recommendations[:self.n]
“`

大家将从引进系统早先,开启数据挖掘之旅。推荐系统无处不在,如亚马逊(亚马逊)网站的“看过那件商品的消费者还购置过”板块:

运作结果和大家的料想相符。我们看能够观看,和Hailey距离近期的用户是Veronica,Veronica对Phoenix乐队打了五分。大家再试试别的人:

![](img/chapter-2/chapter-2-1.png)

让我们密切看看用户对乐队的评分,可以窥见各类用户的打分标准12分不一样:

回首一下,那里的x<sub>1</sub>表示用户1喜欢《龙纹身》的品位,x<sub>2</sub>是用户2喜欢那本书的水准;y<sub>1</sub>则是用户1喜欢《雪崩》的程度,y<sub>2</sub>是用户2喜欢那本书的档次。

小编在iTunes上有大致6000首歌曲,上面是自身最常听的音乐:

梯度降低法,首要使用在线型回归,逻辑回归,神经互连网,推荐算法中;

后来,我们会研究哪些消除这一标题。

相当算法:推荐算法

“`python
>>> pearson(users[‘Angelica’], users[‘Bill’])
-0.9040534990682699
>>> pearson(users[‘Angelica’], users[‘Hailey’])
0.42008402520840293
>>> pearson(users[‘Angelica’], users[‘Jordyn’])
0.7639748605475432
“`

* 假诺数量存在“分数膨胀”难点,就选取Pearson相关周密。
*
假设数量相比较“密集”,变量之间基本都留存公有值,且这几个离开数据是万分重庆大学的,那就接纳欧几里得或曼哈顿距离。
* 假若数量是稀疏的,则使用余弦相似度。

“`python
>>> user[“Veronica”]
{“Blues Traveler”: 3.0, “Norah Jones”: 5.0, “Phoenix”: 4.0, “Slightly
Stoopid”: 2.5, “The Strokes”: 3.0}
>>>
“`

大家得以推断Chan会喜欢The Strokes乐队,而Sam不会太欣赏Deadmau5。

* 用户表,包含用户ID、位置、年龄等音讯。其中用户的全名早已隐去;
* 书籍表,包含ISBN号、标题、小编、出版日期、出版社等;
* 评分表,包蕴用户ID、书籍ISBN号、以及评分(0-13分)。

### 推广:闵可夫斯基距离

![](img/chapter-2/chapter-2-28.png)

![](img/chapter-2/chapter-2-12.png)

里面,“·”号表示数量积。“||x||”表示向量x的模,计算公式是:

原文:http://guidetodatamining.com/chapter-2/

当您在书中看看这几个数学公式,你能够挑选火速略过它,继续读下边包车型客车文字,过去自家正是那样;你也足以停下来,好好分析一下这么些公式,会发觉其实它们并不难精晓。比如上边的公式,当r
= 1时,能够简化成如下格局:

![](img/chapter-2/chapter-2-20.png)

终极总计获得的分数为:

那条斜线就是欧几里得距离,公式是:

![](img/chapter-2/chapter-2-61.png)

![](img/chapter-2/chapter-2-15.png)

“`python
>>> r.loadBookDB(‘/Users/raz/Downloads/BX-Dump/’)
1700018
>>> r.recommend(‘171118’)
“`

![](img/chapter-2/chapter-2-54.png)

先是创设一个推荐类,然后拿走引进结果:

凯尔西在大家网站上评价了150首歌曲,个中25首和Jake相同。和Cooper一样,她和Jake之间的评论差值也只有0.25!

last.fm上对音乐和演奏会的推荐(相似歌唱家):

**眼光基本一致的情状**

**一条直线——完全符合!!!**

**勾股定理**

首先:

说到底离开正是上方数据的加和:(1.5 + 1.5 + 3 + 2 + 1)。

![](img/chapter-2/chapter-2-37.png)

**算算Pearson相关全面的代码**

![](img/chapter-2/chapter-2-5.png)

故此四个向量为:

多少积的持筹握算:

内容:
* 推荐系统办事原理
* 社会化协同过滤工作原理
* 怎么着找到相似物品
* 曼哈顿相差
* 欧几里得距离
* 闵可夫斯基距离
* Pearson相关系数
* 余弦相似度
* 使用Python完毕K最靠近算法
* 图书漂流站(Book克罗丝ing)数据集

**作业:达成三个划算闵可夫斯基距离的函数,并在测算用户距离时选用它。**

那边笔者说说本人的经验。笔者大学读的是现代音乐艺术,课程包涵芭蕾、现代派舞蹈、衣服设计等,没有其余数学课程。笔者高级中学读的是男儿高校,学习了管道工程和小车维修,只晓得很基础的数学知识。不知是因为自个儿的学科背景,照旧习惯于用直觉来研究,当自家赶上这么的数学公式时会习惯性地跳过,继续读上边包车型客车文字。假设您和我同一,作者强烈指出你与那种惰性抗争,试着去明白那个公式。它们固然看起来很复杂,但要么可以被常人所知晓的。

在亚马逊(亚马逊(Amazon))的例子里,它用了多个要一向进展推介:一是自小编浏览了里维斯翻译的《法华经》一书;二是其他浏览过该书的消费者还浏览过的译作。

![](img/chapter-2/chapter-2-41.png)

* Bill没有打出极端的分数,都在2至陆分之间;
* Jordyn如同爱不释手具有的乐队,打分都在4至5以内;
* Hailey是四个有趣的人,他的分数不是1就是4。

**作业:尝试总结Angelica和Veronica的余弦相似度**

我们早就总计过Clara的总评分是22.5,它的平方是506.25,除以乐队的数量5,拿到101.25。综合获得:

### 曼哈顿相距

**超前预报一下:r值越大,单个维度的差值大小会对完全距离有更大的熏陶。**

![](img/chapter-2/chapter-2-46.png)

对此Clara和罗Bert,大家能够得到:

![](img/chapter-2/chapter-2-13.png)

![](img/chapter-2/chapter-2-38.png)

除了那么些算法以外,有部分算法的名字在机械学习园地中也平日出现。但他们自己并不算是三个机械学习算法,而是为了化解某块头难点而诞生的。你能够了解他们为上述算法的子算法,用于小幅度升高陶冶进度。当中的意味有:

“`python
>>> recommend(‘Hailey’, users)
[(‘Phoenix’, 4.0), (‘Blues Traveler’, 3.0), (‘Slightly Stoopid’,
2.5)]
“`

恍如的意况是在测算两篇文章的形似度时。比如说我们想找一本和《The Space
Pioneers》相类似的书,方法之一是选择单词出现的频率,即总计种种单词在书中冒出的次数占全书单词的比重,如“the”出现频率为6.13%,“Tom”
0.89%,“space”
0.四分之一。我们得以用这个数据来寻觅一本相近的书。不过,那里同样有多少的稀疏性难题。《The
Space
Pioneers》中有66贰1几个不等的单词,但拉脱维亚语语言中有超越100万个单词,那样一来非零值就很稀有了,也就不能够计算两本书里面包车型大巴相距。

![](img/chapter-2/chapter-2-17.png)

用公式来叙述即:

    def computeNearestNeighbor(self, username):
        “””获取将近用户”””
        distances = []
        for instance in self.data:
            if instance != username:
                distance = self.fn(self.data[username],
                                   self.data[instance])
                distances.append((instance, distance))
        # 按距离排序,距离近的排在前边
        distances.sort(key=lambda artistTuple: artistTuple[1],
                       reverse=True)
        return distances

本人将本章学到的剧情都集聚成了一个Python类,尽管[代码](code/chapter-2/recommender.py)有些长,笔者要么贴在了那边:

![](img/chapter-2/chapter-2-55.png)

![](img/chapter-2/chapter-2-63.png)

抑或试想一下,Billy Bob奥利弗a助教喜欢读书数据挖掘地点的图书以及科学幻想小说,他最贴近的用户是小编,因为本人也欢畅那三种书。然则,小编又是一个贵宾犬的爱好者,所以给《贵宾犬的不说生活》那本书打了很高的分。那样一来,现有的引进方法会将那本书介绍给奥利弗a教师。

就此无论是是曼哈顿相距依然欧几里得距离,Jake都要比Eric离琳达近,那不符合真实景况。

对此罗Bert,我们用相同的主意计算:

## 最终三个公式:余弦相似度

### K最贴近算法

    def convertProductID2name(self, id):
        “””通过产品ID获取名称”””
        if id in self.productid2name:
            return self.productid2name[id]
        else:
            return id

那边本人将奉上最终四个公式:余弦相似度。它在文书挖掘中使用得较多,在一块过滤中也会采纳到。为了演示怎么样选拔该公式,我们换一个示范。那里记录了各种用户播放歌曲的次数,大家用这个多少开始展览推荐介绍:

故而从图片上知道,意见相平等表现为一条直线。皮尔逊相关全面用于度量多少个变量之间的相关性(那里的多少个变量指的是克拉拉和罗伯特),它的值在-1至1以内,1象征完全契合,-1代表完全相左。从直观上明白,最初始的这条直线Pearson相关周详为1,第1张是0.91,第③张是0.81。因而大家选择那或多或少来找到相似的用户。

总计欧几里得距离的章程也是看似的,大家也只取两者都评价过的乐队。

**理念不太一致的情事**

“`python
import codecs
from math import sqrt

![](img/chapter-2/chapter-2-7.png)

想方设法不错,不过如此做也不行。为了诠释这一题材,大家再引入四个人到例子里来:Cooper和凯尔西。他们和Jake都具有特别相像的音乐偏好,个中Jake在我们网站上评论了25首歌曲。

余弦相似度的限量从1到-1,1象征完全合营,-1代表完全相反。所以0.935代表匹配度很高。

![](img/chapter-2/chapter-2-25.png)

![](img/chapter-2/chapter-2-22.png)

故而,1表示Clara和罗伯特的偏爱完全契合。

![](img/chapter-2/chapter-2-9.png)

![](img/chapter-2/chapter-2-44.png)

我们能够看出,Veronica评价过的乐队,Angelica也都评价过了,所以我们并未推荐。

监察和控制学习算法:线性回归,逻辑回归,神经互连网,SVM

余弦相似度的揣测中会略过这一个非零值。它的总括公式是:

焚林而猎措施之一是采用Pearson相关全面。简单起见,大家先看上面包车型大巴数量(和前面包车型客车数量差别):

Clara的总评分是22.5, 罗Bert是15,他们谈空说有了5支乐队,因而:

* 左:笔者尤其喜爱Broken Bells乐队,所以本人给他俩打六分!
* 右:Broken 贝尔s乐队还足以,作者打伍分。

![](img/chapter-2/chapter-2-52.png)

![](img/chapter-2/chapter-2-33.png)

“`python
def minkowski(rating1, rating2, r):
    distance = 0
    for key in rating1:
        if key in rating2:
            distance += pow(abs(rating1[key] – rating2[key]), r)
    return pow(distance, 1.0 / r)

![](img/chapter-2/chapter-2-42.png)

无监察和控制学习算法:聚类算法,降维算法

小编们得以用以下格局来获得有个别用户的评分:

兴许你还隐隐记得勾股定理。另一种总计距离的艺术正是看两点时期的直线距离:

小编们能够做一下测试:

本章大家讲述的推荐方法称为协同过滤。顾名思义,那一个法子是利用旁人的喜好来展开推荐,也正是说,是大家一道发出的推荐介绍。他的干活原理是那样的:假若要推荐一本书给你,笔者会在网站上追寻叁个和您好像的用户,然后将她喜好的书籍推荐给您——比如巴奇加加元的《发条女孩》。

>
嘿,小编想到1个方式。人们给音乐打分是从1到4分,那个尚未打分的音乐就集合给0分好了,那样就能缓解数量稀疏的标题了!

今昔小编想为神秘的X先生推荐一本书,他给《雪崩》打了四星,《龙纹身的女孩》两星。第一个任务是找出哪位用户和她最为相似。我们用距离来表示。

**有个缺陷**

在联合署名过滤中得以使用K最靠近算法来找出K个最相似的用户,以此作为推荐的基本功。分裂的采纳有两样的K值,需求做一些试验来得出。以下给到读者壹当中坚的思绪。

![](img/chapter-2/chapter-2-45.png)

![](img/chapter-2/chapter-2-4.png)

只有运维调节过书中的代码后才能真的通晓那么些主意,以下是部分推行建议:

# 修改computeNearestNeighbor函数中的一行
distance = minkowski(users[user], users[username], 2)
# 那里2象征使用欧几里得距离
“`

![](img/chapter-2/chapter-2-59.png)

曼哈顿相距的长处之一是测算速度快,对于推特(TWTR.US)那样供给总结百万用户之间的相似度时就可怜便于。

![](img/chapter-2/chapter-2-57.png)

    def loadBookDB(self, path=”):
        “””加载BX数据集,path是数据文件地方”””
        self.data = {}
        i = 0
        #
        # 将书籍评分数据放入self.data
        #
        f = codecs.open(path + “BX-Book-Ratings.csv”, ‘r’, ‘utf8’)
        for line in f:
            i += 1
            #separate line into fields
            fields = line.split(‘;’)
            user = fields[0].strip(‘”‘)
            book = fields[1].strip(‘”‘)
            rating = int(fields[2].strip().strip(‘”‘))
            if user in self.data:
                currentRatings = self.data[user]
            else:
                currentRatings = {}
            currentRatings[book] = rating
            self.data[user] = currentRatings
        f.close()
        #
        # 将图书新闻存入self.productid2name
        # 包括isbn号、书名、作者等
        #
        f = codecs.open(path + “BX-Books.csv”, ‘r’, ‘utf8’)
        for line in f:
            i += 1
            #separate line into fields
            fields = line.split(‘;’)
            isbn = fields[0].strip(‘”‘)
            title = fields[1].strip(‘”‘)
            author = fields[2].strip().strip(‘”‘)
            title = title + ‘ by ‘ + author
            self.productid2name[isbn] = title
        f.close()
        #
        #  将用户音讯存入self.userid2name和self.username2id
        #
        f = codecs.open(path + “BX-Users.csv”, ‘r’, ‘utf8’)
        for line in f:
            i += 1
            #print(line)
            #separate line into fields
            fields = line.split(‘;’)
            userid = fields[0].strip(‘”‘)
            location = fields[1].strip(‘”‘)
            if len(fields) > 3:
                age = fields[2].strip().strip(‘”‘)
            else:
                age = ‘NULL’
            if age != ‘NULL’:
                value = location + ‘  (age: ‘ + age + ‘)’
            else:
                value = location
            self.userid2name[userid] = value
            self.username2id[location] = userid
        f.close()
        print(i)

以下是整个用户的乘除结果:

“`python
>>> computeNearestNeighbor(‘Angelica’, users)
[(3.5, ‘Veronica’), (4.5, ‘Chan’), (5.0, ‘Hailey’), (8.0, ‘Sam’), (9.0,
‘Bill’), (9.0, ‘Dan’), (9.5, ‘Jordyn’)]
“`

BP算法,首要行使在神经网络中;

上面大家来看分母:

![](img/chapter-2/chapter-2-10.png)

![](img/chapter-2/chapter-2-6.png)

![](img/chapter-2/chapter-2-8.png)

相关文章