一、概述
本次作业分成两部分,异常检测和推荐系统。工作量真的挺大的。
共有六个函数:Estimate Gaussian Parameters、Select Threshold、Collaborative Filtering Cost、Collaborative Filtering Gradient、Regularized Cost、Regularized Gradient。
二、分析
1、异常检测
异常检测是无监督学习中的一种,给定训练集默认都是正常样本,然后根据概率,在测试集中挑出异常样本。
其假设十分简单,假设样本的所有参数均是正态分布,且各自独立,那么,某个给定样本出现的概率应该为其个参数出现概率的积,若这个积太小,那么就说明这个样本是异常样本。
通过上面说的原理可以看出,这个检测算法十分简单,就是利用训练集中的各参数,分别建模,模型都是正态分布模型,有两个参数:和
,前者就是求参数的平均值,后者则是求方差。注意这里的方差不是样本方差,其除数是m而不是m-1。
对于算法求出概率的阈值,利用交叉验证集进行选择即可。
在这期间,可能会出现以下几个问题,我们来一一说明。
首先,若是参数不符合正态分布,那该怎么办?
当然是对其进行某种运算,使得其分布近似为正态分布即可,比如说对数化或指数化等。
其次,如果某个或某几个特性的取值,对于正常样本和异常样本看起来不明显,那该怎么办?
选择几个不明显的参数,将他们相除、相乘等以设计新的参数代替他们,新的参数要有较好的区分度。这是一种方法。
另外一种就是采用多元正态分布,而不是采用当前的标准正态分布。多元正态分布的优点是可以自己找出参数间的关系,而不是默认它们是独立的。比如说,正常来讲,参数a和b的变化趋势应该是类似的,若是出现a大b小的情况,那么则是异常,然后,对于各自独立的假设,可能a和b的取值都在正常范围内,那么就无法检测出;然而,多元正态则会检测出这一点,从而认为这种概率较小。但是,多元正态分布过于复杂,计算起来较为麻烦。
最后,什么时候用异常检测,什么时候用分类算法呢?
一般来说,当正常样本很多,异常样本很少时用异常检测;如果二者数量五五开那就用分类算法。另外,如果异常样本千奇百怪,那么也优先用异常检测。如果未来出现的异常样本和目前已有的异常样本区别很大,即目前已有的异常样本无法描述全部异常样本空间,那么也优先使用异常检测。
接下来就是具体的算法实现。
estimateGaussian函数用于求出参数的和
,其代码如下:
mu=(sum(X)/m)';
sigma2=var(X,1,1);
注意var的参数,一个是按列求解,一个是说明是方差而不是样本方差。
selectThreshold函数用于选择较好的阈值,代码如下:
for epsilon = min(pval):stepsize:max(pval)
ytemp=(pval<epsilon);%异常用1表示
tp=sum((ytemp==yval)&(ytemp==1));%说是异常,实际也是异常
fp=sum((ytemp~=yval)&(ytemp==1));%说是异常,实际不是异常
fn=sum((ytemp~=yval)&(ytemp==0));%说不是异常,实际是异常
prec=tp/(tp+fp);
rec=tp/(tp+fn);
F1=2*prec*rec/(prec+rec);
if F1>bestF1
bestF1=F1;
bestEpsilon=epsilon;
end
end
注意,此处评价算法好坏也是利用F1值,因此,每选择一个阈值,都要计算出它对应的F1值,然后选择最大的作为阈值。这里的tp、fp、fn值很容易弄混,要仔细求解。
这样就完成了一个简单的异常检测算法。
2、推荐系统
推荐系统算法有两个方向,Content-based recommendations和Collaborative filtering。即基于内容的推荐和协同过滤。
前者的主要思想是对推荐的物品抽象出n个特性,对于推荐算法面向的用户,收集他们对于不同特性的喜好,然后拟合不同物品的特性参数。简而言之,该思想是已知“用户的好恶”,去拟合“物品的特性”。
该算法的主要难点在于收集用户的喜好,暂且不谈收集的难度,隐私的侵犯等问题,即使收集到了,也无法确定这是用户的真实喜好,毕竟很多人无法对自己的喜好进行细分的量化。
后者的主要思想是对推荐的物品抽象出n个特性,对每个特性赋值,然后拟合不同用户对于不同特性的喜好。简而言之,该思想是已知“物品的特性”,去拟合“用户的好恶”。
该算法客服了前者的缺点,毕竟,对于某个具体的物品抽象并量化参数,要比对用户进行此类操作简单得多。因为该算法的优越性,主要研究的也是它。
协同过滤算法主要分为以下几步:
①、收集用户的好恶和物品的特性这两类参数。并使用theta描述用户的好恶,使用x描述物品的特性;
②、对theta和x初始化,初始化类似神经网络权值的初始化;
③、利用已有的theta和训练集,求出x的迭代值;
④、利用x的迭代值和训练集,求出theta的迭代值;
⑤、重复③和④,直到误差收敛。
注意,theta和x的更新是分先后的,也就是theta0→x1→theta1→x2→...,实际上,也可以同时更新theta和x。
更新theta的公式如下:
更新x的公式如下:
同时更新它们俩的公式如下:
仔细看会发现,第三个公式就是前两个公式相加的和。要想更新它,就需要同时为theta和x赋初值了。
既然代价函数已经有了,下一步就是求偏导,公式如下:
然后利用fmincg等函数迭代求解即可。
注意,存在这样一种情况,某一用户对某一物品一无所知,他也没有打分,这时就需要利用打分矩阵R来标识这种情况。R中元素Rij为1,表示第i个用户对第j个物品有打分,为0表示没有打分。
接下来是具体实现。所有的功能都在函数cofiCostFunc中,其代码如下:
for i=1:size(R,1)%i是第几个用户,j是第几部电影,k是第几个参数,对于X_grad的每个值,等于用户对所有电影的预测误差乘以对应电影的参数的和
idx=find(R(i,:)==1);
Thetatemp=Theta(idx,:);
Ytemp=Y(i,idx);
X_grad(i,:)=((X(i,:)*Thetatemp'-Ytemp)*Thetatemp)+lambda*X(i,:);
end
for j=1:size(R,2)
idx=find(R(:,j)==1);
Ytemp=Y(idx,j);
Xtemp=X(idx,:);
Theta_grad(j,:)=((Xtemp*Theta(j,:)'-Ytemp)'*Xtemp)+lambda*(Theta(j,:));
end
Stemp=(X*Theta'-Y).^2;
J=sum(sum(R.*Stemp))/2+sum(sum(Theta.^2))*lambda/2+sum(sum(X.^2))*lambda/2;
debug这玩意花了我好久,关键就是公式看不明白。下面一一解释:
第一个循环用于计算x的偏导数。x是什么?x用于描述物品的特性。先仅考虑x的某一个值xik。xik表示第i个物品的第k个特性参数。那么在公式
中,∑下面的限制就表示,对于某个具体物品的x值的拟合,仅考虑那些对该物品打过分的人的打分。也就是R=1的用户。这些用户打分的预测值减去实际值,乘以用户对物品第k个特性的好恶参数,求和,加上正则化项,就是偏导数。
若是向量化,那就有点难理解。需要把theta和y中,对第i个物品打过分的用户的theta和y都筛出来,得到两个矩阵。然后相乘再相减。强烈推荐在写这个函数时多次利用size函数,否则很容易出现缺少转置或者维数弄错的情况。
第二个循环用于计算theta的偏导数。theta是什么?theta是用来描述用户的好恶的。同样仅考虑theta的某一个值thetajk。thetajk表示第j个用户对第k个物品特性的好恶。那么在公式
中,对于某个具体用户的theta值的拟合,要仅考虑该用户打过分的物品。也就是说R=1的物品。该用户对这些物品打分的预测值减去实际值,乘以物品的特性参数,求和,再加上正则化项,就是偏导数。
若是向量化,注意与上一个循环的区别,上一个循环筛选theta和y,这个循环筛选X和y。
实在是有点难写出来。
这一步完成后,利用fmincg迭代即可。
另外还有几个小问题,比如说,为解决某用户对于所有物品都没有打分的情况,需要将所有打分减去平均值,这被称为Mean
normalization等。
三、总结
本次作业实现了异常检测与推荐系统的简单应用。原理都不复杂,仔细理解之后,耐心一些,还是好写的。