该文章为笔记
在前文中记录了方差表示的是一组数据相对于平均数的离散程度的博客,一个班的学生成绩方差很大,说明这个班的学生成绩波动很厉害,有的特别好,有的特别差。这里描述的是某一个变量所体现背后的含义(维度或者称为指标)。
相似度与相关性不是一种产物,不能被他们的名字搞混淆;比如两个文本的相似度,在考察两个文本的相似度时,需要分别文本进行分词计算每个词语的词频,形成词频向量。再计算两个词频向量的余弦夹角,根据夹角的值域得出两个文本的相似度。
词频向量中有多个词,每一个词就是向量中的一个维度;而下图中的菜品销售量,某个菜品的销售量是一个维度下含有多个值,不是多个维度。某两个菜品的相关性,是指两个维度的进行比较;而相似度是若干个维度进行比较。
在一个菜馆中,会有一个菜品的销量,关于这个销量。我们想要得出,某个客人点了百合酱蒸风爪,要给他做一个推荐。推荐的规则是与百合酱蒸风爪销量有相关性的菜品。
相关性系数方式计算
样本协方差 =
样本协方差的是表示两个指标之间的亲疏关系,比如指标指标的值成正相关的关系,你增加我也增加,你减少我也减少。
相关性系数的计算公式 = ;表示的是向量的标准差,同理。
相关系数展开式子: =
相关性系数方式的代码实现
=
private double[] alpha;
private double[] beta;
public double correlate() {
if ((alpha == null || alpha.length == 0)
|| (beta == null || beta.length == 0)) {
throw new Exception("alpha或者beta没有初始化");
}
int length = alpha.length;
if (length != beta.length) {
throw new Exception("向量维度必须相等");
}
//向量平均数,平均数求法很简单,就不贴了
double alphaMean = mean(alpha);
double betaMean = mean(beta);
double numerator = 0;
double alphaDenominatorSum = 0;
double betaDenominatorSum = 0;
for (int i = 0; i < length; i++) {
double numericX = Math.pow((alpha[i] - alphaMean), 2d);
double numericY = Math.pow((beta[i] - betaMean), 2d);
alphaDenominatorSum += numericX;
betaDenominatorSum += numericY;
double alphaDeviation = alpha[i] - alphaMean;
double betaDeviation = beta[i] - betaMean;
numerator += alphaDeviation * betaDeviation;
}
return numerator / Math.sqrt(alphaDenominatorSum * betaDenominatorSum);
}
相似度
两个向量之间的相似就是这两个向量之间的夹角
= 计算方式 =,=, =
= ;同理也是这种计算方式
得出结果值就是两个向量的相似度,而且他们的相关性不会超过 [-1, 1] 这个区间(的值域为[-1, 1]);-1意味着两个向量指向的方向正好截然相反,1表示它们的指向是完全相同的,0通常表示它们之间是独立的。
文本A:陈瑞是一个歌手,也是一个男人
分词结果:陈瑞 1,是 2,一个 2,歌手 1,也是 1,男人 1,女人 0
词频向量: [1, 2, 2, 1, 1, 1, 0]
文本B:陈瑞是一个男人,也是一个女人
分词结果:陈瑞 1,是 2,一个 2,歌手 0,也是 1,男人 1,女人 1
词频向量: [1, 2, 2, 0, 1, 1, 1]
通过计算词频向量与得出两文本内容的相似度
中文分词的文章请参考 https://blog.csdn.net/Hello_Ray/article/details/104139024
TF-IDF 相似度优化
TF-IDF算法介绍
依据上面的内容,我们已经可以求出两篇文章的相似度了,但是有一些缺点的。比如根据中文分词选择词频较大的, 然而、得出、的、是的 这些词都是一些语气词或者明显不能表示出意思的词;会对两篇文章的相似度行程干扰。
所以需要做的是,使用一个规则性的方法能够避免这些词选入词频向量中,这个规则性的方法就是TF-IDF算法。TF-IDF算法意义是:某个词其他文章比较少见,然而它在这篇文章中词频很高,那么它很可能就反映了这篇文章的特性,正是我们所需要的关键词。
TF-IDF算法:词频率 与 逆文档频率 相乘得来。
词频率(TF)=
逆文档频率(IDF)=,分母加1 是要避免没有文档有这个词,不然分母就为0了。
TF-IDF与余弦夹角结合计算文章的相似度
第一步:选择文章
在真正的环境下可能是多篇文章,那么我们选择多篇文章,然后提取摘要(一般文章都有摘要,如果没有就选择前几段和后几段)形成语料文章。
第二步:分词
对这些文章进行分词,去重;形成一个词向量。比如
文章A:我/喜欢/看/小说
文章B:我/不/喜欢/看/电视,也/不/喜欢/看/电影
那么变成词向量是 [ 我,喜欢,看,小说,电视,电影,不,也 ] 。例子是两篇文章,词向量比较短,如果是多篇文章词向量会变得特别长。
统计每个文章的词频向量:
文章A:我 1,喜欢 1,看 1,小说 1,电视 0,电影 0,不 0,也 0。
文章B:我 1,喜欢 2,看 2,小说 0,电视 1,电影 1,不 2,也 1。
第三步:计算每个文档中各个词的词频率TF
第四步:计算各个词的逆文档频率IDF
我 log(2/2)=0,喜欢 log(2/2)=0,看 log(2/2)=0,小说 log(2/1)=1,电视 log(2/1)=1,电影 log(2/1)=1,不log(2/1)=1,也 log(2/1)=1。
第五步:计算每个文档中各个词的TF-IDF值
文章A:我 0,喜欢 0,看 0,小说 1,电视 0,电影 0,不 0,也 0。
文章B:我 0,喜欢 0,看 0,小说 0,电视 1,电影 1,不 1,也 1。
第六步:每篇文章根据词向量的TF-IDF选择靠前的N个词
文章A:我 0,喜欢 0,小说 1
文章B:电视 1,电影 1,不 1
第七步: 根据每篇文章选择的 N个词构造词频向量;如 [ 我,喜欢,小说,电视,电影,不 ]
文档新的词频向量
文章A:[1 1 1 0 0 0]
文章B: [1 2 0 1 1 2]
注意:这时构造出来的词频向量中的词频或者词频率,是原来所在文章的词频或者词频率。TF-IDF所做工作是选择一些精简有用的词参与余弦夹角计算。
第八步:计算余弦值,形成一个两两相似度的余弦矩阵(有点类似于协方差矩阵)
第九步: 取出某一篇文章时,提取大小前几位的相似度文章就形成了 推荐 文章
余弦夹角的代码实现
public double cdot() {
if ((alpha == null || alpha.length == 0)
|| (beta == null || beta.length == 0)) {
throw new Exception("alpha或者beta没有初始化");
}
int length = alpha.length;
if (length != beta.length) {
throw new Exception("向量维度必须相等");
}
double sum = 0;
for (int i = 0; i < length; i++) {
double alphaIndex = alpha[i];
double betaIndex = beta[i];
double mult = alphaIndex * betaIndex;
sum += mult;
}
return sum;
}
public double vectorDistence(double[] arr) {
if (arr == null || arr.length == 0) {
throw new Exception("alpha或者beta没有初始化");
}
double sum = 0;
for (int i = 0, len = arr.length; i < len; i++) {
double mult = arr[i] * arr[i];
throws sum += mult;
}
return Math.sqrt(sum);
}
图中部分测试数据
百合酱蒸凤爪:17,11,10,9,4,13,9,9,6,9,6,5,9,10,13,4,6,9,3,8,11,11,4,7,8,4,6,8,8
翡翠蒸香茜饺:6,15,8,6,10,10,7,12,8,11,7,9,7,8,12,8,12,15,10,7,6,6,7,5,8,10,7,5,6
金银蒜汁蒸排骨:8,14,12,6,13,13,13,13,8,13,8,4,11,10,12,12,10,4,13,9,11,5,10,6,12,12,7,11,7
乐膳真味鸡:24,13,13,3,8,16,8,6,3,6,9,7,9,6,10,11,9,12,13,20,8,15,7,7,14,9,11,10,9
蜜汁焗餐包:13,9,8,10,12,8,5,7,8,4,8,11,14,9,9,11,7,13,5,7,7,6,12,8,7,7,8,6
生炒菜心:13,10,3,9,10,9,7,8,4,7,7,9,8,13,11,7,4,9,8,12,8,14,10,15,11,8,7,11,4