前言
本文其实值属于:程序员的数学【AI(人工智能(Artificial Intelligence))oT阶段二】(尚未更新)的一部分内容,本篇把这部分内容单独截取出来,方便大家的观看,本文介绍线性代数高级,读之前建议先看:程序员的数学【线性代数基础】,本文涵盖了一些计算的问题并使用代码进行了实现,安装代码运行环境见博客:最详细的Anaconda Installers 的安装【numpy,jupyter】(图+文),如果你只是想要简单的了解有关线代的内容,那么只需要学习一下博文:NumPy从入门到高级,如果你是跟着博主学习的小伙伴,建议先看博文:数据分析三剑客【AI(人工智能(Artificial Intelligence))oT阶段一(下)】(十万字博文 保姆级讲解),如果你没有
基础,那么还需先修博文:Python的进阶之道【AI(人工智能(Artificial Intelligence))oT阶段一(上)】(十五万字博文 保姆级讲解)
1.特征值(eigenvalue)和特征向量(Feature vector)
1.1 概念定义
🚩两个向量相乘的效果如下:
看图,这张图是垂直偏移的,图说明了向量相乘的效果:
设是
` 阶方阵,如果存在数
和非零
维列向量
,使得
成立,则称
是矩阵(matrix)
的一个特征值(eigenvalue)(eigenvalue),
是特征值(eigenvalue)
对应的特征向量(Feature vector)(eigenvector) 。
矩阵(matrix) 变换向量
。这种变换的特殊之处在于,当它作用于特征向量(Feature vector)
时,
只进行了一次缩放变换,其方向不变(invariant),也没有旋转。
观察发现和
在同一条直线上,但长度不同。这时,我们称
为
的特征向量(Feature vector),
的长度为
乘以
的长度,
为特征值(eigenvalue)。
如果阶方阵
是满秩矩阵(matrix)(Full-rank matrix),那么矩阵(matrix)
有
不同的特征值(eigenvalue)和特征向量(Feature vector)。
import numpy as np
A = np.random.randint(1, 10, size = (4, 4))
display(A)
print('------------------------')
if np.linalg.matrix_rank(A) == 4: # 必须是满秩矩阵(matrix)(Full-rank matrix)
# 实对称矩阵(matrix)特征值(eigenvalue)为实数,非对称矩阵(matrix)和复矩阵(matrix)特征值(eigenvalue)可能为复数
w, v = np.linalg.eig(A) # 返回特征值(eigenvalue)和特征向量(Feature vector)
display(w, v)
w = np.real(w)
v = np.real(v)
print('------------------------')
display(A.dot(v[:, 0]))
display(w[0] * v[:, 0])
实对称矩阵(matrix),将特征值(eigenvalue)和特征向量(Feature vector)求解为实数
# 实对称矩阵(matrix)
import numpy as np
B = np.array([[1, 2, 3],
[2, 5, 8],
[3, 8, 9]])
np.linalg.eig(B)
矩阵(matrix)的秩:矩阵(matrix)通过初等行变换转化为梯形矩阵(matrix),则矩阵(matrix)中非零的行数定义为该矩阵(matrix)的秩,记为
。
经过初等变(equivariance)换,可以看出这个方程没有解。
import numpy as np
X = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 2]])
y = np.array([1, 2, 3])
# 查看矩阵(matrix)秩,输出结果为2
print('矩阵(matrix)X的秩为:',np.linalg.matrix_rank(X))
# 尝试求解报错,说明方程没有唯一解
np.linalg.solve(X,y)
1.2 满秩矩阵(matrix)(Full-rank matrix)
🚩满秩矩阵(matrix)(Full-rank matrix)(non-singular matrix): 设是
阶矩阵(matrix), 若
, 则称
为满秩矩阵(matrix)(Full-rank matrix)。但满秩不局限于
阶矩阵(matrix)。若矩阵(matrix)秩等于行数,称为行满秩;若矩阵(matrix)秩等于列数,称为列满秩。既是行满秩又是列满秩则为
阶矩阵(matrix)即
阶方阵。
根据初等行变换,我们可以得到:
1.3 方程的解
import numpy as np
X = np.array([[0, 2, -1],
[1, -1, 1],
[2, 1, -1]])
y = np.array([1, 0, -2])
# 查看矩阵(matrix)秩
print('矩阵(matrix)X的秩为:',np.linalg.matrix_rank(X))
# 尝试求解报错,说明方程没有唯一解
np.linalg.solve(X,y).round(2)
1.4 特征值(eigenvalue)和特征向量(Feature vector)示例
🚩所有特征值(eigenvalue)的乘积等于的行列式:
import numpy as np
X = np.array([[2, 3, 7],
[1, 5, 8],
[0, 4, 9]])
# w表示特征值(eigenvalue),v表示特征向量(Feature vector)
w,v = np.linalg.eig(X)
print('矩阵(matrix)X的行列式:',np.linalg.det(X))
print('特征值(eigenvalue)累乘值:',np.round(np.real(np.prod(w))))
机器学习(machine learning)中会用到特征值(eigenvalue)和特征向量(Feature vector),比如主成分分析(Principal component analysis(PCA)),
线性判别(RBM discriminative RBM)分析(Linear Discriminant Analysis/LDA),其他算法都会用到它的理论和方法。
二、特征值(eigenvalue)分解(decompose)(Eigenvalue decomposition)
2.1 特征值(eigenvalue)分解(decompose)(Eigenvalue decomposition)定义与操作
🚩特征值(eigenvalue)分解(decompose)(Eigenvalue decomposition)是将矩阵(matrix)分解(decompose)为以下公式:
其中,是由矩阵(matrix)
的特征向量(Feature vector)组成的矩阵(matrix),
是对角矩阵(matrix)(diagonal matrix),对角线上的元素就是特征值(eigenvalue)。
import numpy as np
A = np.array([[7, 8, 4, 3],
[2, 9, 6, 8],
[1, 6, 9, 6],
[7, 9, 7, 3]])
# w(Σ)表示特征值(eigenvalue),v(Q)表示特征向量(Feature vector)
w, v = np.linalg.eig(A)
print('矩阵(matrix)A的特征值(eigenvalue)和特征向量(Feature vector):')
display(w, v)
# 根据特征值(eigenvalue)分解(decompose)(Eigenvalue decomposition)公式可得
print('特征值(eigenvalue)和特征向量(Feature vector)运算反推矩阵(matrix)A:')
display(v.dot(np.diag(w)).dot(np.linalg.inv(v)))
如果矩阵(matrix)是对称矩阵(matrix),那么
是正交(orthogonal)矩阵(matrix),正交(orthogonal)矩阵(matrix)的定义是
的逆等于
的转置
# 实对称矩阵(matrix)
import numpy as np
B = np.array([[1, 2, 3],
[2, 5, 8],
[3, 8, 9]])
# w(Σ)表示特征值(eigenvalue),v(Q)表示特征向量(Feature vector)
w,v = np.linalg.eig(B)
print('矩阵(matrix)A的特征值(eigenvalue)和特征向量(Feature vector):')
display(w, v)
print('特征值(eigenvalue)和特征向量(Feature vector)运算反推矩阵(matrix)A:')
display(v.dot(np.diag(w)).dot(np.linalg.inv(v)))
display(v.dot(np.diag(w)).dot(v.T))
print('Q是正交(orthogonal)矩阵(matrix):')
display(np.linalg.inv(v), v.T)
2.2 特征值(eigenvalue)分解(decompose)(Eigenvalue decomposition)意义
🚩矩阵(matrix)其实是一个线性变换,因为一个矩阵(matrix)和一个向量相乘得到的向量,其实就是等价于向量的一次线性变换。
当矩阵(matrix)是高维的情况下,那么这个矩阵(matrix)就是高维空间下的一个线性变换,这个线性变化可能没法通过图片来表示,但是可以想象,这个变换也同样有很多的变换方向,我们通过特征值(eigenvalue)分解(decompose)(Eigenvalue decomposition)得到的前 N 大特征向量(Feature vector),那么就对应了这个矩阵(matrix)最主要的 N 个变化方向。我们利用这前 N 个变化方向,就可以近似表达这个矩阵(matrix)(变换)。也就是说的:提取这个矩阵(matrix)最重要的特征。
综上所述,特征值(eigenvalue)分解(decompose)(Eigenvalue decomposition)可以得到特征值(eigenvalue)和特征向量(Feature vector)。特征值(eigenvalue)的大小表示特征的重要性,特征向量(Feature vector)表示特征是什么。每个特征向量(Feature vector)都可以理解为一个线性子空间(Subspace),我们可以用这些线性子空间(Subspace)做很多事情。
但是,特征值(eigenvalue)分解(decompose)(Eigenvalue decomposition)也有很多限制,例如变换后的矩阵(matrix)必须是方阵。
3.矩阵(matrix)和向量推导公式
3.1 常见矩阵(matrix)求导公式
🚩矩阵(matrix)推导公式有以下六种:
3.2 向量求导公式
3.3 矩阵(matrix)求导公式
转置公式如下:
,其中
是一个常数
四、奇异值(singular value)分解(decompose)(Singular Value Decomposition)(SVD)
4.1 什么是奇异值(singular value)分解(decompose)(Singular Value Decomposition)
🚩 特征值(eigenvalue)分解(decompose)(Eigenvalue decomposition)是提取矩阵(matrix)特征的好方法,但它只适用于方阵。在现实世界中,我们看到的大多数矩阵(matrix)都不是正方形的。比如有的学生,每个学生的成绩是
,所以
的矩阵(matrix)不一定是方阵。我们怎样才能描述像特征值(eigenvalue)这样的一般矩阵(matrix)的重要特征呢?奇异值(singular value)分解(decompose)(Singular Value Decomposition)用于执行此操作。奇异值(singular value)分解(decompose)(Singular Value Decomposition)是一种可以应用于任何矩阵(matrix)的分解(decompose)方法。
假设(Hypothesis)是
的矩阵(matrix),那么得到的
是
的方阵(里面的向量是正交(orthogonal)的,
里面的向量称为左奇异向量(singular vector)(left singular vector)),
是
的实对角矩阵(matrix)(diagonal matrix)(对角线外面的元素直线都是
,对角线上的元素称为奇异值(singular value)),
是
的矩阵(matrix),里面的向量也是正交(orthogonal)的,里面的向量称为右奇异向量(singular vector)(right singular vector)),从下图体现的大小关系几个相乘矩阵(matrix):
4.2 奇异值(singular value)与特征值(eigenvalue)关系
🚩 特征值(eigenvalue)分解(decompose)(Eigenvalue decomposition):
奇异值(singular value)分解(decompose)(Singular Value Decomposition):
那么奇异值(singular value)和特征值(eigenvalue)是如何对应的呢?首先,我们将矩阵(matrix)和
的转置进行矩阵(matrix)乘法,得到一个方阵。我们可以用这个方阵求特征值(eigenvalue)得到:
这里得到的就是我们上面的右奇异向量(singular vector)(right singular vector)。然后,我们用矩阵(matrix)
和
的转置进行矩阵(matrix)乘法,我们将得到一个方阵。我们可以用这个方阵求特征值(eigenvalue)得到:
这里得到的就是我们上面的左奇异向量(singular vector)(left singular vector)。
此外我们还可以得到:
因为和
是对称矩阵(matrix),所以
是正交(orthogonal)矩阵(matrix)。正交(orthogonal)矩阵(matrix)的定义是
的逆等于
的转置,即
这里是上面提到的奇异值(singular value)。
对和
进行特征值(eigenvalue)分解(decompose)(Eigenvalue decomposition),得到奇异值(singular value)分解(decompose)(Singular Value Decomposition)的结果。但在样本分离中存在一定的问题,因为在分解(decompose)特征值(eigenvalue)时,特征向量(Feature vector)的符号(symbol)并不影响结果,如:
如果在计算过程中取,取上面组成的左奇异矩阵(matrix)
,取
形成右奇异矩阵(matrix)
,此时
。因此,求
时,一定要按
求,以保证
。
解决方案:
1.计算特征值(eigenvalue):,根据结合律
,可以化简为:得到左奇异矩阵(matrix)
。根据上面的分解(decompose)公式,奇异矩阵(matrix)就是特征值(eigenvalue)的平方根。
2.间接求右奇异矩阵(matrix):求
因为:
所以:,因为
是正交(orthogonal)矩阵(matrix),所以
5.解决奇异值(singular value)分解(decompose)(Singular Value Decomposition)
5.1 方式一
🚩根据上面对应的公式,进行奇异值(singular value)分解(decompose)(Singular Value Decomposition):
import numpy as np
A = np.array([[ 3, 4, 5, 5],
[ 7, 5, 3, 6],
[ 6, 5, 7, 7],
[ 4, 9, 8, 9],
[ 5, 10, 5, 7]])
# 左奇异矩阵(matrix)
sigma,U = np.linalg.eig(A.dot(A.T))
# 降序排列后,逆序输出
index = np.argsort(sigma)[::-1]
# 将特征值(eigenvalue)对应的特征向量(Feature vector)也对应排好序
sigma = sigma[index]
U = U[:, index]
print('左奇异矩阵(matrix)如下:')
display(U)
# 计算奇异值(singular value)矩阵(matrix)
sigma = np.sqrt([s if s > 0 else 0 for s in sigma])
print('奇异值(singular value)为:')
display(sigma[:4])
# 计算右奇异矩阵(matrix)
# 奇异值(singular value)矩阵(matrix)特殊,斜对角线有值,其余都是0
# 奇异值(singular value)矩阵(matrix)的逆矩阵(matrix)
sigma_inv = np.diag([1 / s if s > 0 else 0 for s in sigma])
V = sigma_inv.dot((U.T).dot(A))
print('右奇异矩阵(matrix)如下:')
display(V[:4])
print('使用奇异值(singular value)分解(decompose)(Singular Value Decomposition)还原矩阵(matrix)A:')
U.dot(np.diag(sigma)).dot(V).round(0)
5.2 方式二
🚩根据提供的方法,进行奇异值(singular value)求解
import numpy as np
A = np.array([[ 3, 4, 5, 5],
[ 7, 5, 3, 6],
[ 6, 5, 7, 7],
[ 4, 9, 8, 9],
[ 5, 10, 5, 7]])
u, s, v =np.linalg.svd(A) # 奇异值(singular value)分解(decompose)(Singular Value Decomposition)
display(u, s, v)
六、奇异值(singular value)分解(decompose)(Singular Value Decomposition)性质
🚩奇异值(singular value)类似于特征值(eigenvalue)。它们在矩阵(matrix)
中也是从大到小排列的,
的归约非常快。很多情况下,前
甚至
的奇异值(singular value)之和占了所有奇异值(singular value)。总和大于
。也就是说,我们也可以用最大的奇异值(singular value)来近似描述矩阵(matrix),这里是一个部分奇异值(singular value)分解(decompose)(Singular Value Decomposition):
是一个比
和
小得多的数。这样的矩阵(matrix)乘法表示如下:
右边三个矩阵(matrix)相乘的结果会是一个接近的矩阵(matrix),其中
越接近
,相乘结果越接近
。这三个矩阵(matrix)的面积之和(在存储方面,矩阵(matrix)面积越小,存储容量(capacity)越小)远小于原始矩阵(matrix)
。如果我们想压缩空间来表示原始矩阵(matrix)
,我们存储这里只要遵循三个矩阵(matrix):
,
,
。
用白话来说,顾名思义,可能无法快速理解其本质,叫“奇异值(singular value)”,那我们换一种说法,叫“主特征值(eigenvalue)”,你可能很快就明白了。
对于非奇异(满秩)矩阵(matrix),对应特征值(eigenvalue);对于奇异矩阵(matrix),需要奇异值(singular value)分解(decompose)(Singular Value Decomposition),对应奇异值(singular value)。对于奇异矩阵(matrix),将与其转置
相乘,得到一个方阵,然后求其特征值(eigenvalue)。值得注意的是,在对非奇异矩阵(matrix)进行奇异值(singular value)分解(decompose)(Singular Value Decomposition)(
)时,得到的奇异值(singular value)实际上是特征值(eigenvalue)。
import numpy as np
A = np.array([[ 3, 4, 5, 5],
[ 7, 5, 3, 6],
[ 6, 5, 7, 7],
[ 4, 9, 8, 9],
[ 5, 10, 5, 7]])
u, s, v =np.linalg.svd(A)
m, n = A.shape
Σ = np.concatenate([np.diag(s), np.full(shape = (m - n,n), fill_value = 0)])
print('通过奇异值(singular value)分解(decompose)(Singular Value Decomposition)还原原数据:')
display(u.dot(Σ).dot(v))
# 抽取一部分奇异值(singular value),近似表示
print('用前r大的奇异值(singular value)来近似描述矩阵(matrix):')
r = 2
display(u[:, :r].dot(Σ[:r, :r]).dot(v[:r]).round(1))
七、SVD进行数据压缩
🚩根据奇异值(singular value)分解(decompose)(Singular Value Decomposition),进行数据压缩,以图片为例:
首先我们需要下载一张图片:
链接:https://pan.baidu.com/s/1pyywQWO4U4WWxtuAhAQHMQ?pwd=l7vv
提取码:l7vv
import numpy as np
import matplotlib.pyplot as plt
# 图片压缩
def pic_compress(r, img):
u, sigma, vt = np.linalg.svd(img)
compress_img = np.dot(np.dot(u[:, :r], np.diag(sigma[:r])), vt[:r, :])# 还原图像
size = u.shape[0] * r + r * r + r * vt.shape[1] # 压缩后大小
return compress_img, size
filename = './bird.jpg'
img = plt.imread(filename)[:, :, 0] # 取其中的一个颜色通道
compress_img, size = pic_compress(30, img)
print('原图尺寸:' + str(img.shape[0] * img.shape[1]))
print('压缩尺寸:' + str(size))
# 图片绘制
fig, ax = plt.subplots(1, 2,figsize = (12,4))
ax[0].imshow(img,cmap = 'gray')
ax[0].set_title('before compress')
ax[1].imshow(compress_img,cmap = 'gray')
ax[1].set_title('after compress')
plt.show()
八、SVD进行PCA降维(dimensionality reduction)
import numpy as np
from sklearn import datasets
r = 2 # 筛选特征个数
X,y = datasets.load_iris(return_X_y = True)
print('原始数据特征个数是:', X.shape[1])
# 1、去中心化
mean_ = np.mean(X, axis=0)
X -= mean_
# 2、奇异值(singular value)分解(decompose)(Singular Value Decomposition)
u, s, v = np.linalg.svd(X)
# 3、符号(symbol)翻转(flip)(如果为负数,那么变成正值)
max_abs_cols = np.argmax(np.abs(u), axis = 0)
display(max_abs_cols)
signs = np.sign(u[max_abs_cols, range(u.shape[1])])
display(signs)
u *= signs
# 4、降维(dimensionality reduction)特征筛选
u = u[:, :r]
# 5、归一化(Normalization)
u = (u - u.mean(axis = 0)) / u.std(axis = 0, ddof = 1) # ddof计算样本标准差(standard deviation)
display(u[:5])
print('经过PCA降维(dimensionality reduction),数据特征个数是:', u.shape[1])
九、SVD进行矩阵(matrix)求逆(invert)
9.1 SVD求逆(invert)矩阵(matrix)原理
🚩在矩阵(matrix)求逆(invert)过程中,通过将矩阵(matrix)变换为正交(orthogonal)空间。不同的奇异值(singular value)和奇异值(singular value)向量代表矩阵(matrix)中不同的线性独立(或独立)条目。
分解(decompose)矩阵(matrix),形式如下:
奇异值(singular value)矩阵(matrix)为:
当用方法进行求逆(invert)时,会使得求逆(invert)运算变得非常简单,这是因为通过 SVD 求逆(invert),只需要对奇异值(singular value)求倒数即可,而左奇异矩阵(matrix)
和右奇异矩阵(matrix)
都是正交(orthogonal)矩阵(matrix),有
,
。因此,其求逆(invert)形式为:
=
奇异值(singular value)矩阵(matrix)的逆是:
由上可知,的求逆(invert)是原奇异值(singular value)的倒数,这使得通过
求矩阵(matrix)的求逆(invert)非常简单:奇异值(singular value)的逆,奇异矩阵(matrix)的转置。
9.2 SVD求逆(invert)代码演示
import numpy as np
A = np.array([[ 3, 4, 5, 5],
[ 7, 5, 3, 6],
[ 6, 5, 7, 7],
[ 4, 9, 8, 9],
[ 5, 10, 5, 7]])
# A是奇异矩阵(matrix)
print('矩阵(matrix)A的形状是:', A.shape)
print('矩阵(matrix)A的秩是:')
display(np.linalg.matrix_rank(A))
display(np.linalg.inv(A)) # 无法直接求解逆矩阵(matrix)
使用奇异值(singular value)分解(decompose)(Singular Value Decomposition)求解逆矩阵(matrix)
import numpy as np
import warnings
warnings.filterwarnings('ignore')
A = np.array([[ 3, 4, 5],
[ 7, 5, 3],
[ 6, 5, 7],
[ 4, 9, 8],
[ 5, 10, 5]])
u, s, v = np.linalg.svd(A)
display(u, s, v)
m, n = A.shape
# 奇异值(singular value)求倒数
# sigma = np.concatenate([np.diag(s),np.full(shape = (m-n,n),fill_value = 0)],axis = 0)
# sigma = sigma**(-1)
# cond = np.isinf(sigma)
# sigma[cond] = 0
sigma = np.diag(np.concatenate([s**(-1),[0]*(m-n)]))[:,:3]
# 逆矩阵(matrix)求解
B = v.T.dot(sigma.T).dot(u.T)
print('矩阵(matrix)B是A的逆矩阵(matrix),两个进行矩阵(matrix)运算得到单位矩阵(matrix)(identity matrix):')
display(B.dot(A).round(5))
十、SVD进行协同过滤(collaborative filtering)
10.1 协同过滤(collaborative filtering)
🚩协同过滤(collaborative filtering)是一种从大量用户给出的兴趣爱好集合中预测用户的偏好和兴趣的技术。 它的基本假设(Hypothesis)是,对某个事物,人们过去喜欢那么将来也会喜欢。 协作过滤可以用于推荐系统(recommender system),该推荐系统(recommender system)自动向用户建议他/她的首选产品。
推荐系统(recommender system)的任务是联系用户和信息,一方面帮助用户发现对自己有价值的信息,另一方面让信息展示在对它感兴趣的用户面前,从而实现信息消费者和信息生产者的双赢。 .
10.2 干饭人
🚩以下是用户午餐外卖的倾向得分。 表示数据缺失,需要使用
分解(decompose)来补全和预测缺失值。数据如下:
外卖意向 | 寿司 | 牛肉拉面 | 麻辣烫 | 黄焖鸡米饭 | 排骨饭 |
---|---|---|---|---|---|
张三 | 2 | 1 | 3 | 2 | 5 |
李四 | 3 | 5 | 2 | 4 | 3 |
王五 | 1 | 3 | 1 | 1 | 4 |
赵六 | 0 | 2 | 4 | 5 | 2 |
Michael | 4 | 4 | 5 | 0 | 5 |
Sara | 5 | 5 | 2 | 3 | 1 |
John | 1 | 1 | 4 | 2 | 2 |
Daniel | 2 | 4 | 3 | 5 | 4 |
Po | 1 | 3 | 3 | 2 | 1 |
辰chen | 5 | 0 | 1 | 3 | 5 |
奇异值(singular value)分解(decompose)(Singular Value Decomposition)算法可用于矩阵(matrix)逼近问题。如果分解(decompose),中间矩阵(matrix)取部分特征值(eigenvalue)(只取前几个最大的特征值(eigenvalue)),这样就可以逼近原矩阵(matrix)了。基于这个思想,奇异值(singular value)分解(decompose)(Singular Value Decomposition)可以用来预测用户从外卖点订餐的倾向得分。
10.3 SVD进行协同过滤(collaborative filtering)
import numpy as np
food = np.mat([[2, 1, 3, 2, 5],
[3, 5, 2, 4, 3],
[1, 3, 1, 1, 4],
[0, 2, 4, 5, 2],
[4, 4, 5, 0, 5],
[5, 5, 2, 3, 1],
[1, 1, 4, 2, 2],
[2, 4, 3, 5, 4],
[1, 3, 3, 2, 1],
[5, 0, 1, 3, 5]])
u, sigma, v = np.linalg.svd(food)
# 选取前2大特征值(eigenvalue),做近似表达
food_result = np.mat(u[:, :2]) * np.mat(np.diag(sigma[:2])) * np.mat(v[:2, :])
food_result.round(1)
根据SVD分解(decompose),对缺失值数据,进行了预测,结果如下:
外卖意向 | 寿司 | 牛肉拉面 | 麻辣烫 | 黄焖鸡米饭 | 排骨饭 |
---|---|---|---|---|---|
张三 | 2 | 1 | 3 | 2 | 5 |
李四 | 3 | 5 | 2 | 4 | 3 |
王五 | 1 | 3 | 1 | 1 | 4 |
赵六 | 1.2 | 2 | 4 | 5 | 2 |
Michael | 4 | 4 | 5 | 2.3 | 5 |
Sara | 5 | 5 | 2 | 3 | 1 |
John | 1 | 1 | 4 | 2 | 2 |
Daniel | 2 | 4 | 3 | 5 | 4 |
Po | 1 | 3 | 3 | 2 | 1 |
辰chen | 5 | 1.4 | 1 | 3 | 5 |
文章出处登录后可见!