From 482d4f814c9e6961ed692985a0e5df0372b29168 Mon Sep 17 00:00:00 2001 From: jiangzhonglian Date: Fri, 1 Sep 2017 23:17:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=AE=8C=E6=88=90=20SVD=20?= =?UTF-8?q?=E7=89=A9=E5=93=81=E7=9A=84=E4=B8=BB=E8=A6=81=E7=89=B9=E5=BE=81?= =?UTF-8?q?=E7=A9=BA=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/14.利用SVD简化数据.md | 203 +++++++++++++++++++++++++++++- images/14.SVD/基于SVD.png | Bin 0 -> 14507 bytes images/14.SVD/基于物品相似度.png | Bin 0 -> 10552 bytes input/14.SVD/0_5.txt | 32 +++++ src/python/14.SVD/svdRecommend.py | 78 +++++++++--- 5 files changed, 298 insertions(+), 15 deletions(-) create mode 100644 images/14.SVD/基于SVD.png create mode 100644 images/14.SVD/基于物品相似度.png create mode 100755 input/14.SVD/0_5.txt diff --git a/docs/14.利用SVD简化数据.md b/docs/14.利用SVD简化数据.md index 2382e1af..1765dbe5 100644 --- a/docs/14.利用SVD简化数据.md +++ b/docs/14.利用SVD简化数据.md @@ -155,7 +155,114 @@ def loadExData2(): > 分析数据: 暂时不需要 -> 使用算法: 通过调用 recommend() 函数进行推荐 +> 训练算法: 通过调用 recommend() 函数进行推荐 + +* 基于物品相似度(参考地址:http://www.codeweblog.com/svd-%E7%AC%94%E8%AE%B0/) + +![基于物品相似度](/images/14.SVD/基于物品相似度.png) + +```python +# 基于物品相似度的推荐引擎 +def standEst(dataMat, user, simMeas, item): + """standEst(计算某用户未评分物品中,以对该物品和其他物品评分的用户的物品相似度,然后进行综合评分) + + Args: + dataMat 训练数据集 + user 用户编号 + simMeas 相似度计算方法 + item 未评分的物品编号 + Returns: + ratSimTotal/simTotal 评分(0~5之间的值) + """ + # 得到数据集中的物品数目 + n = shape(dataMat)[1] + # 初始化两个评分值 + simTotal = 0.0 + ratSimTotal = 0.0 + # 遍历行中的每个物品(对用户评过分的物品进行遍历,并将它与其他物品进行比较) + for j in range(n): + userRating = dataMat[user, j] + # 如果某个物品的评分值为0,则跳过这个物品 + if userRating == 0: + continue + # 寻找两个用户都评级的物品 + # 变量 overLap 给出的是两个物品当中已经被评分的那个元素的索引ID + # logical_and 计算x1和x2元素的真值。 + overLap = nonzero(logical_and(dataMat[:, item].A > 0, dataMat[:, j].A > 0))[0] + # 如果相似度为0,则两着没有任何重合元素,终止本次循环 + if len(overLap) == 0: + similarity = 0 + # 如果存在重合的物品,则基于这些重合物重新计算相似度。 + else: + similarity = simMeas(dataMat[overLap, item], dataMat[overLap, j]) + # print 'the %d and %d similarity is : %f'(iten,j,similarity) + # 相似度会不断累加,每次计算时还考虑相似度和当前用户评分的乘积 + # similarity 用户相似度, userRating 用户评分 + simTotal += similarity + ratSimTotal += similarity * userRating + if simTotal == 0: + return 0 + # 通过除以所有的评分总和,对上述相似度评分的乘积进行归一化,使得最后评分在0~5之间,这些评分用来对预测值进行排序 + else: + return ratSimTotal/simTotal +``` + +* 基于SVD(参考地址:http://www.codeweblog.com/svd-%E7%AC%94%E8%AE%B0/) + +![基于SVD.png](/images/14.SVD/基于SVD.png) + +```python +# 基于SVD的评分估计 +# 在recommend() 中,这个函数用于替换对standEst()的调用,该函数对给定用户给定物品构建了一个评分估计值 +def svdEst(dataMat, user, simMeas, item): + """svdEst(计算某用户未评分物品中,以对该物品和其他物品评分的用户的物品相似度,然后进行综合评分) + + Args: + dataMat 训练数据集 + user 用户编号 + simMeas 相似度计算方法 + item 未评分的物品编号 + Returns: + ratSimTotal/simTotal 评分(0~5之间的值) + """ + # 物品数目 + n = shape(dataMat)[1] + # 对数据集进行SVD分解 + simTotal = 0.0 + ratSimTotal = 0.0 + # 奇异值分解 + # 在SVD分解之后,我们只利用包含了90%能量值的奇异值,这些奇异值会以NumPy数组的形式得以保存 + U, Sigma, VT = la.svd(dataMat) + + # # 分析 Sigma 的长度取值 + # analyse_data(Sigma, 20) + + # 如果要进行矩阵运算,就必须要用这些奇异值构建出一个对角矩阵 + Sig4 = mat(eye(4) * Sigma[: 4]) + # 利用U矩阵将物品转换到低维空间中,构建转换后的物品(物品+4个主要的特征) + xformedItems = dataMat.T * U[:, :4] * Sig4.I + # 对于给定的用户,for循环在用户对应行的元素上进行遍历, + # 这和standEst()函数中的for循环的目的一样,只不过这里的相似度计算时在低维空间下进行的。 + for j in range(n): + userRating = dataMat[user, j] + if userRating == 0 or j == item: + continue + # 相似度的计算方法也会作为一个参数传递给该函数 + similarity = simMeas(xformedItems[item, :].T, xformedItems[j, :].T) + # for 循环中加入了一条print语句,以便了解相似度计算的进展情况。如果觉得累赘,可以去掉 + print 'the %d and %d similarity is: %f' % (item, j, similarity) + # 对相似度不断累加求和 + simTotal += similarity + # 对相似度及对应评分值的乘积求和 + ratSimTotal += similarity * userRating + if simTotal == 0: + return 0 + else: + # 计算估计评分 + return ratSimTotal/simTotal +``` + +排序获取最后的推荐结果 ```python # recommend()函数,就是推荐引擎,它默认调用standEst()函数,产生了最高的N个推荐结果。 @@ -178,6 +285,9 @@ def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst): return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[: N] ``` +> 测试 和 使用 该算法,可以自行编写 + +[完整代码地址](https://github.com/apachecn/MachineLearning/blob/master/src/python/14.SVD/svdRecommend.py): #### 要点补充 @@ -201,6 +311,97 @@ def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst): ### 项目案例: 基于 SVD 的图像压缩 +> 收集 并 准备数据 + +将文本数据转化为矩阵 + +```python +# 加载并转换数据 +def imgLoadData(filename): + myl = [] + # 打开文本文件,并从文件以数组方式读入字符 + for line in open(filename).readlines(): + newRow = [] + for i in range(32): + newRow.append(int(line[i])) + myl.append(newRow) + # 矩阵调入后,就可以在屏幕上输出该矩阵 + myMat = mat(myl) + return myMat +``` + +> 分析数据: 分析 Sigma 的长度个数 + +通常保留矩阵 80% ~ 90% 的能量,就可以得到重要的特征并取出噪声。 + +```python +def analyse_data(Sigma, loopNum=20): + """analyse_data(分析 Sigma 的长度取值) + + Args: + Sigma Sigma的值 + loopNum 循环次数 + """ + # 总方差的集合(总能量值) + Sig2 = Sigma**2 + SigmaSum = sum(Sig2) + for i in range(loopNum): + SigmaI = sum(Sig2[:i+1]) + ''' + 根据自己的业务情况,就行处理,设置对应的 Singma 次数 + + 通常保留矩阵 80% ~ 90% 的能量,就可以得到重要的特征并取出噪声。 + ''' + print '主成分:%s, 方差占比:%s%%' % (format(i+1, '2.0f'), format(SigmaI/SigmaSum*100, '4.2f')) +``` + +> 使用算法: 对比使用 SVD 前后的数据差异对比,对于存储大家可以试着写写 + +```python +# 打印矩阵 +def printMat(inMat, thresh=0.8): + # 由于矩阵保护了浮点数,因此定义浅色和深色,遍历所有矩阵元素,当元素大于阀值时打印1,否则打印0 + for i in range(32): + for k in range(32): + if float(inMat[i, k]) > thresh: + print 1, + else: + print 0, + print '' + + +# 实现图像压缩,允许基于任意给定的奇异值数目来重构图像 +def imgCompress(numSV=3, thresh=0.8): + """imgCompress( ) + + Args: + numSV Sigma长度 + thresh 判断的阈值 + """ + # 构建一个列表 + myMat = imgLoadData('input/14.SVD/0_5.txt') + + print "****original matrix****" + # 对原始图像进行SVD分解并重构图像e + printMat(myMat, thresh) + + # 通过Sigma 重新构成SigRecom来实现 + # Sigma是一个对角矩阵,因此需要建立一个全0矩阵,然后将前面的那些奇异值填充到对角线上。 + U, Sigma, VT = la.svd(myMat) + # SigRecon = mat(zeros((numSV, numSV))) + # for k in range(numSV): + # SigRecon[k, k] = Sigma[k] + + # 分析插入的 Sigma 长度 + analyse_data(Sigma, 20) + + SigRecon = mat(eye(numSV) * Sigma[: numSV]) + reconMat = U[:, :numSV] * SigRecon * VT[:numSV, :] + print "****reconstructed matrix using %d singular values *****" % numSV + printMat(reconMat, thresh) +``` + +[完整代码地址](https://github.com/apachecn/MachineLearning/blob/master/src/python/14.SVD/svdRecommend.py): * * * diff --git a/images/14.SVD/基于SVD.png b/images/14.SVD/基于SVD.png new file mode 100644 index 0000000000000000000000000000000000000000..610219d50eb2ae3ea5dfdead58c14ebc211f7801 GIT binary patch literal 14507 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 literal 0 HcmV?d00001 diff --git a/images/14.SVD/基于物品相似度.png b/images/14.SVD/基于物品相似度.png new file mode 100644 index 0000000000000000000000000000000000000000..5c14e779dee3d4e75c3b4f9d0ceedae94282edbf GIT binary patch literal 10552 zcmb7q1yoegqBbZ<3W#(GGRRQUC5_aO0un>l&>(^nj#CJWqsC!88EuAto7V;`>{-m@B*U*;q#YRE7CRUYBHfUYcrHJ@vcjd`3jq zBItgwTlcT}DSfwvO2iv^rZUMO+yKJoW(0`@ zbb+6OFv-yvr21&VbdrxGu%2*X%7?{Dy!p?Ubm>6AgOwb(q2tQ}%5d1nN=DpH5X6&Y z_;}tiLwID{@Ug>`cN^#rHzdAx{h~F-cN<()R+<#F1%j+r|JW>rL0|sBs$~6$toOBE zI;T|9kSCT_y<^6FFmn8+?fk|w82rLlj}Eu92idxx_q3ALhR4d;nLFCFN-*zd(V~p{ zqDNL!nQl2Y10{xuzw@M;wg{GnOs?aO(yBmDo~i`FXRC;jtc_HdV*W1=`^%eo*AQcH zVacbOGJfVkE`-aR^;795dP+Jui9k_lj282aj7=s8q7pMyIpm^$^3b|yh{>3&$1$=@ zyv8Hznk+hgpz5d4<^9$oN&GN(A_+{0Tqjc*>EQLLIvNm?F zERi0;0@3vhju9VB?i8KeyJ;%#0NUd z+0qjd5=8W=42ch)a69Ylj)}=L{b~5qV4x`Kl|_o?KRH`X!1c7`b90}HdyDpAEhN9mE@OAjAceA1MV)#X9F0;KT03VvAk>MqT>GfvjPUaJ*)es;qBHzFv`@i;|_Kg8#|fjAbxAn(r z)3#uk12nkvdgu|AUzF4_%$#<#N4_o(n>Y3fduiM&_RRYb0QZq0+09< zca7p$?zk8;nn58*>Wz+#BUDOs;dE*OX5X7s(Zy>&P&0YDtAx&tXFK>gm6nz#kmt9` z&aa5vuv{>NFnn{Z?(_{wk6RE5OFOm#7`Kxalfxff;s=?Asb%-bESHn+6KB(GyLEi; zrO=>Si&5##>53V-c&T`&sQweap}qK9sYzuUw$#pOwDVULUkD}JVhf7N&Bs5|xd_$p zq0!uoME}-QdbwUb!IabNEsi!(&%WPTI;EWlwzeS+5U&6YR$lI5eyly=c>X%Elr%b5 z&L#@Q#nkjQlLk?@VoPIg>F1yh4ym?JiSfZj6=W~vU8h2dOkX_A1qXP!By!g6NUPE= z$3guxBXU1nrC9}HJ8}D$dR4l02nS3&gwxmMyogI-Vw51U^A7I&8vnSQZ23wwBBx@# zF0AM~f=Ib#%YS|gw)QFF(ve5D^1-_rPfq8FEpRPSh<1Q0B)+Y-g728be6>ti&}R0T z7nhT=;PQjj4v*jY$)bnhDhC>oX3Xxy$o4>toP37Lcz-IoGx7g+kNMYai1My{BH6&x zs`dOCLFLUmVqm{=T`-s55E>@ejQV!7>tJ}jN_xIK zcMgmk{91Cq<$N(OMs%xjFN%M$-eIX#;bnu_Ob=mxBJW*0@_q5}@tY1ddE$Y;!uHCI{%a$VVmkXAxe>N<%Url`RTgA^4zyiBM$mfTT zoF*NAN|REHde`sH#dxpL*DZQ(^QFBm5XYM7t1#_V7P~p=DUSyk2h1^p=_vm!-Q6s) z`}|foMQZ6ec5AgBCGFOQzHe)Z&2K&#Rq#6BLqrZc_#UTkmw)qxzKD~l$A%94N;Wi5 z9ps(Tdwrvg$zaAV8$-i;rxAV`&(`?T6mtsaj@u(Xww@Q+2@U4OfD=GBu z1aD5po_TKOz~&^=j9;}|->=lM2_JQLzR2N2C9)qkF)yt(677;BK%Uc*M5EDdy^xB>gW z1HFHJApZpyZCG+RaIe1E(vCRyIvxod_7zk2_E*}vi$LMQ#sbY*Pzi^=Y)E|d>F50` zODoyb8VeXG5tgdnd43apB7)$GNG#K}fAXE+QBw?YUNTnj`W9@AoeHXk`2;wl(D66Qx2b}PvP z2bA=C{CsGY_DJFbm(Df-?4D+DSlM=m)A*ZIbYOc~dKDdLeJo6hTK64CS~bX6JDLAL zxSR{y#GBsEIU%z&GKiwx=rtJ^j=behXRLrl;Sq(gYE$U8m5P4#Yv#A|632Nz``$0* zf)!C;{}UIbjmczGL4Go-JTx&Z7D1X71PqYpQU4hi`(c_}V+r2@TwH!eraa}ssUNQJ z%OeW}Nh`2k^|oG}IX%Uw>2{<-^G6CCe2^jTkALuOCv4T+I&A!hb~=-n6?N+u3@sfS zgbS=dW$dj^6tS+2ou`Ft!cXfd7o~nIZFA23`Z7v1)R0f$2u~@CM>q?r8B#th)!1yu z3s~jNM+2o|Zy7P1-eF!t!{>(zJpSSz#ZTG~Lu*=U25XEAFa(Q@zh3^dVy10YzLSgu zMVLC0C}m&C<`-)Lq@b8UOQth?IB4{qUKJd`DLI5h}BcZ*xZ4?xN0%q7z|!yM&Fs+iJou(xN< zCxE9Uxb;HOE~rG^YzO&?0SqzliRZk)h>p887D_Q5Jr`tAa71OX92hV+& zSg`_mlq%Rel(`~u2SnMTk>H3U;bbK(7@Ah*M8cb3aD?|#x!i+5>-FFvNHu5uKJcdW ze+V`XyLp|jJdrxRpiEHXx+x4HgbdXDI;7+zvQ4-f@c5R#^m+?#^G<>zW?3Z&z?rcysE=EacldvV$!Rhc zT~(sQ*eKdB@D|=jWUaw{-``6)FX<7XzhzY4S2R-2yaY(B`Dr>hV!4zWc8`#*Qd#!^ z8BysLf0L7j`Ou1(H#J;Q5Gy{YO)CtSW#%9P6(Fc&mowKS{ff@rWr_hTBhQ_mwxkNt zGxl|32nGaA%oFpuqEpo!?wIe_5YIg_3I}2BkehI-9Q^|)N1zgjw=(t`3{sOxf7bID z>^p?>zgovjba5&WBq$#uDzZ=aSsXM7K~q}9*qu>z4%lDUu|{exb=enS1kYEYCl{$I18W@AacCh(Mee+6psw=A$J>5ceXS0u!hG8K+xMwS+QB=1UY`( zwH=d<;Y_y$7p=KEA=jNYKW6zIf5OUWdfDim#g?YSl6&OBU9k-W>N17g7M({#PLWNn zyNv*_)-KVEK4vfjE~RQX=)hsYaA?}C)o*fT>!uk+1?nxbeOzI()izUQd9pDt2me+w z0bp9vK;H-~MFZLl7K0hndURM5ZF)}7jx@>hmlAvc@(&JY3IVs@a~>89oeHouPrVzY zP930jn|B28=}3owE{H%f?cD*s10RE#rteWgHkFWTvx8yX3@9v<~W?2M;p1y zTLO4G72@Uw!Fp4{OvDb(Y1(1on@zc3if`7!>?t*4fwTw0_}`9v{H&cD3yBXKu#L&m zdRZF3A5)3wF%DMtm=A9}&DuC+Z6Lzo=jTTX@Y&Er$()){5fkqY^ltV)Ksy#w3sH6@yVqQ5{shaOd7+mkprxtzF* zj?FQV-MSTmW!<`K|E2$!lX3uGh~lD;KBg%3Op>tABP+C+ zVUVj@bAI@JyNo_Cg`YVG%^bL$9IWXbmeqx=>g7Y;r^hC~9To@|H?=3uDsb4QCkW-8 zlqOqbEG``d%aZ)Jbc<4zsC9}kyH}b{?@Lo9IP*Nn9BSPrA6w|NNf7Y7R2r=OfxGs1 ziCH++85WdY>ON?o#q4V8>J&?%m# z`3(p%P#Kxq=du8TFyq*$5)t-24%f4QSw{--3ZQ)z8|31C_69W%U|mvRRVi3ytXcF) zpl!tu)6?Q7E25I3y^a+0Bvo3DTXe?pwnmw>xyhQ%?|#DA1#{W9 zX684NW>~bo2DQW={YP;AC$)e{7Zgj}4Nbpdos7B1N5iYxDZFQ**B=e9@5QQSJ!xgK z+oFdo)2Tncx6w>U>`Ooed7Lk}C8=3tx=dC+rqh2Tc_xX4Kue5m5tp>c@>u_8U5ADx z!>6E~U_u=%ph-@U<^%aY18`2#2f|8Hxv;6fQkJw#k_7-H_QW6ZfQIWp0zi3twVy6K z3pzQQb8yIpno%UivR(ff@jLUmtLUXkps$xz2EAYH3OBsFoXWo0vA8=e_hajo8bxC; z!{J|eKUPxS>WPfv-+|0|`pnmm!a{y3Ofc8taW96LA<#$aG ztiQXxg6ZK=37`~HiFls;m81Ytww%q{4DuyppkTZ?B@5c+1IZ=1+o4D zB=lSPOvfH_NIdHt$te057W@cUU%!*QXDursl)h(G*=+aqcTFY=g2+hjJwTVf4#i`e zBjAeqwP-k=0LXRLrtCh~cXv1E>~mA*Azpy*S|LyP%J=%B(t@f|!~1^dwSM3GnIT~W zz#RYoh?^s`{9m7z)Bu4LfWq0Db-qgTZFuYnL|>S*c*m;vJEhrk{zPxLua zevD_tIY>i-!03>CG>C5nbqEJ@1JGj^Y$MK7E~+wcspZC(mot$w1gO@CUZi9Gv%l(# zOyG2SNT;P8V?~y#7ZT4id?*QrNvnciJFFI;UbK#QY#+NJXL7IwO_8P91x&7MU%MP@ z52RrN!9|U%gB-X%OQbg`;*8d#T9#o*fpu)Gikyyn)%Gl=S9`av2us1jN0ZJrI!P_> zCnjg(j%UpznV0QNCx;Ym;R|*SE>d!JF#w-rLT%ntnALrC)Jk~lMy;-6m1b|DEU2W8 z;O*!e&@#6zjdT;6&^3PK9+g?8TpAw4!QOE!JQ65;K<+4P4p`gFlr)&HiA?sAD)1B^ zubD@w1km_ZlzFCVW}G)r!?EnQN-!| zP-)c(#TN)Fo98*Nrg0b`0lQKyD6MKw`ME?ON|XM@o$N^`y~o1WpDT_ya6jgLz)5RL z6g7its>ry}k6{dld5be}A`+P+9*kON6m~iFItI|T1+b&OL;5-(iVS~zC20O$J>s=C zZYHbytta4wLg%sAOO@y!u_?@qaRx0;&d^OTA3uSeY2Mqx2}V@AM38$+NNqVQ>VBMV zcU@{(dXO>{#5Q@6bmRPMOWqO@KU>IE9|jc!Hzh}-MB)Y!>R2?Em&6But^JM0^)za# ziNrm-m!c|!(0Zt4fcoM^eT>ZQPJMdMxsNxjABpReb&Tp`7P}!h8rZte@A@`##f=8> z*al3ugMBH`=rMx22J#yy)qC>U<5;_;_A=9Hcv3iYYC|v^+kA|fG|rKeDEpUkG+gZFLk5U8Q1*i#Zj08kQH%qLl)Dp_e{n z+7a6d`IC*ii!(hMMXF+!zCH*8?#VZj=6ZA;2f`{ZN78B3m)cU4V`wqva21CDnK95? z(05W*%aUHTh-x-Dd1jZr;XS^F$~;pv{;&b?AMD$#GUz|NpB=vnj|a7rH+@9#*no4; zO%`*p_q#;Vwo^PPEB`zS9(XX_B9@AO>~EL<779P&hzVE3;3i+bY85`#y8A-7m&-r< zC0=)ZicH=Tn@}m=D&m4I;(3=?(t@Oqss2)v1NG^KcUM)-VuZhe=wCIGF za$n$!@&qtyuRM#_Y-DuaCfvCQ{IN*Bp3V~`oHe_5&+~7sSg0r@17CP8F2GP5)RNe4S+)c^o&TZvvm zz+fhp7@KcIY(i^s|A8Y`_ix)*#((cr^5cs*78IMI`tO~@?f+wCISv9FXmQZ^k82(_ zXaHUZQNdG9booBDdhGju5?=#Zz!xg3#2?T<*5zvGxyYxW(&Y`YyFWRvLw(Pi zmipu5Y0odW%f$}b9%f&T7rO2*wopBV-~i15FJk9+VRq}$0L!<7VD@If7#YwGUUWxB zVLbDGH2(B=3$e>Tv)LUriOH*zi%6wHL3$+4~&fvz?UXw$^ zS*$$}pfmtpPzC+v<RL|`Rl~1JI3;9DSN-62C|2umU3-A0 z-#Yg*B(vW0M<*E|C;4WZfCc5aI@?XxzIaH)6??h1L?Q+3T1Y~2phG){Ac)>n zw*{gG2spB}hJ#n)#3=nnCEzfZK^D!_bzTztou~I(;~cX0(`vCtmV(8k0$;5OUOV7X z_c+>43R6Te!vgMYrW?Sax@k^2;_?Fc;P!{%UU)fRWA%4Jv>6%)|GGi4!B%OxDKl3d zSr$+dG=C^7ZfB$0s@{FTL3`Tes0YuV^eiaIdGtr{jr_z7id+Sdi4Z4=awfqyJg9o@ zT1P`8n$F#dbHU72Nq_8M%|CLZXDJH9IdFfvq`a?W9X!^PG8v{;C6dSX{%GBr5c-6<*$N(~6|{Be1COUi;w-cNU7lBFVid$N#qVKQl;l;vWck)3QxXi}O)YSCi z?L<4gxg7(KsMj$b6y=`*1P7`_13%Jnze+W(Q!On$S~?G;?K^H3tv>apHgB)};A?7{ zhm?9(&w<+=wJJbED9p+c3nXQ^{hMl7i)d1)7m+lbGU#7AQBZ?)k5~{c3}p7{gR{5^ z;>t`KaKIh}7}&5U;k80raiSAdub%SH-6DcZfO4B}*9HLJ%cf8MxFwGy2kn&@B++<` zDunKVAg@PUte}Y2NnVXi&8c|V1F5Epij_utRiXk8P)a)?dkMg77bsWpmYp4`Oy4}w z;Ij|)eB@-;zTv%&I#v~DSxiJ=I?5w`O(OkdR&B;nl*yb6vJ8eQzXC2 z1jaD#!K_IvdZiIoJCf4~czv#SJ!qcmy!RoJPHMr>llN9IF91QzE`GX2kqZh6Df`FA z7X2<*eJoHTa5De#Q3fs>b416vPZ#Jk{$RM+f1ae9aufT+ngH!oTRBpqudoIV(v}VI z`baj4RbT6Gz&ka9Fl{HaREa$e=>b)j3O$@jXJTB#CQA&!OdI|;arU<`(|m82cn zVSH7r$HL`%j6P)*CWVaj0bfY$T$0$ny$t$_ZDNVL342~HYxxhhQOMnCCRQ;}Nvcf_p`Zd}p% zbZB5a4)Sz;CDNEo^AUBjNG0ob{n`g*%m5VB;K+4FuPQ;Sp|bZMlI=yVcB&S-$ITop zUc7sU*3L5=tt7`#TdY^g8Z}+lH0^3)AZy(;Zuw%9(uO1S6Jna+)7+(FhFkO^H~Z`K zruj-|fo0sPEE_L(APgA)T9;~{6oiSY%gM_cyF$1M_*0s6P04@u{%8Z7pi<+#p_V@7 zWC%4@~!0hnX zGzN~bvaz=h-A6hq^d;k;%3e~|@%FXMiDMU9MR;{GEC|0-6Xwl60C?`?`e9bEX;aX+ z8JBvGzTi7nXXfAyHX9U*^3WkIjT^4CdZw2xANdY~@a^fvL)&B>nLPS5{#^2)e!^gt zs$U{@VfV=liMDeMm9-1qGfGd-I_Rje1#75 zSv6pm|7;&BP^*A>{4$_|1Gg!!vXa$nk$_3PDJ{~|blwG9bY-mFQ+ToJoo#aLAKw7~ z!*HWT+g#<=ccO;DIJj#lG>sua6xm7~`!m59Zo(_-GtktRLh47IxNGAezp+|Nqs;zF zMHk?=&b0bz9AYEvDlP61G_tDzt6`qAOJqG3Trq z8u77$g8jNkMLvFhh6?WMTW??_2@{^cLYKw|i9_L#_~q1qfiWpeQ{XgN8PT7R28SQ3 zPeolyGA8`)FUksvRhA}W>u2HZkL^fU*0_&a_st3l6D#;REYw9;)Gd@~fT>3{q~X=x z8DmT{?Vl8_C4Xg+?jS`Uvh`Pt<+?ulq(BV{QxIKoX6C#$4&55mD{z|7&=R@N-9)cg ztEKwnkFSqM0OH`c)avtNl&aK#RyGXC8UI&j08NIqI1OATG(Oe@#u5jY0OtiS#15W7 W66vJqae$+cC=h97sWM5Ufd2z!+xAfa literal 0 HcmV?d00001 diff --git a/input/14.SVD/0_5.txt b/input/14.SVD/0_5.txt new file mode 100755 index 00000000..598a8c0d --- /dev/null +++ b/input/14.SVD/0_5.txt @@ -0,0 +1,32 @@ +00000000000000110000000000000000 +00000000000011111100000000000000 +00000000000111111110000000000000 +00000000001111111111000000000000 +00000000111111111111100000000000 +00000001111111111111110000000000 +00000000111111111111111000000000 +00000000111111100001111100000000 +00000001111111000001111100000000 +00000011111100000000111100000000 +00000011111100000000111110000000 +00000011111100000000011110000000 +00000011111100000000011110000000 +00000001111110000000001111000000 +00000011111110000000001111000000 +00000011111100000000001111000000 +00000001111100000000001111000000 +00000011111100000000001111000000 +00000001111100000000001111000000 +00000001111100000000011111000000 +00000000111110000000001111100000 +00000000111110000000001111100000 +00000000111110000000001111100000 +00000000111110000000011111000000 +00000000111110000000111111000000 +00000000111111000001111110000000 +00000000011111111111111110000000 +00000000001111111111111110000000 +00000000001111111111111110000000 +00000000000111111111111000000000 +00000000000011111111110000000000 +00000000000000111111000000000000 diff --git a/src/python/14.SVD/svdRecommend.py b/src/python/14.SVD/svdRecommend.py index 669429aa..e2a905f6 100644 --- a/src/python/14.SVD/svdRecommend.py +++ b/src/python/14.SVD/svdRecommend.py @@ -150,11 +150,22 @@ def svdEst(dataMat, user, simMeas, item): # 奇异值分解 # 在SVD分解之后,我们只利用包含了90%能量值的奇异值,这些奇异值会以NumPy数组的形式得以保存 U, Sigma, VT = la.svd(dataMat) + + # # 分析 Sigma 的长度取值 + # analyse_data(Sigma, 20) + # 如果要进行矩阵运算,就必须要用这些奇异值构建出一个对角矩阵 Sig4 = mat(eye(4) * Sigma[: 4]) - # 利用U矩阵将物品转换到低维空间中,构建转换后的物品 + + # 利用U矩阵将物品转换到低维空间中,构建转换后的物品(物品+4个主要的特征) xformedItems = dataMat.T * U[:, :4] * Sig4.I - # 对于给定的用户,for循环在用户对应行的元素上进行遍历, + print 'dataMat', shape(dataMat) + print 'U[:, :4]', shape(U[:, :4]) + print 'Sig4.I', shape(Sig4.I) + print 'VT[:4, :]', shape(VT[:4, :]) + print 'xformedItems', shape(xformedItems) + + # 对于给定的用户,for循环在用户对应行的元素上进行遍历 # 这和standEst()函数中的for循环的目的一样,只不过这里的相似度计算时在低维空间下进行的。 for j in range(n): userRating = dataMat[user, j] @@ -205,7 +216,41 @@ def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst): return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[: N] +def analyse_data(Sigma, loopNum=20): + """analyse_data(分析 Sigma 的长度取值) + + Args: + Sigma Sigma的值 + loopNum 循环次数 + """ + # 总方差的集合(总能量值) + Sig2 = Sigma**2 + SigmaSum = sum(Sig2) + for i in range(loopNum): + SigmaI = sum(Sig2[:i+1]) + ''' + 根据自己的业务情况,就行处理,设置对应的 Singma 次数 + + 通常保留矩阵 80% ~ 90% 的能量,就可以得到重要的特征并取出噪声。 + ''' + print '主成分:%s, 方差占比:%s%%' % (format(i+1, '2.0f'), format(SigmaI/SigmaSum*100, '4.2f')) + + # 图像压缩函数 +# 加载并转换数据 +def imgLoadData(filename): + myl = [] + # 打开文本文件,并从文件以数组方式读入字符 + for line in open(filename).readlines(): + newRow = [] + for i in range(32): + newRow.append(int(line[i])) + myl.append(newRow) + # 矩阵调入后,就可以在屏幕上输出该矩阵 + myMat = mat(myl) + return myMat + + # 打印矩阵 def printMat(inMat, thresh=0.8): # 由于矩阵保护了浮点数,因此定义浅色和深色,遍历所有矩阵元素,当元素大于阀值时打印1,否则打印0 @@ -220,25 +265,30 @@ def printMat(inMat, thresh=0.8): # 实现图像压缩,允许基于任意给定的奇异值数目来重构图像 def imgCompress(numSV=3, thresh=0.8): + """imgCompress( ) + + Args: + numSV Sigma长度 + thresh 判断的阈值 + """ # 构建一个列表 - myl = [] - # 打开文本文件,并从文件以数组方式读入字符 - for line in open('testData/testDigits/0_5.txt').readlines(): - newRow = [] - for i in range(32): - newRow.append(int(line[i])) - myl.append(newRow) - # 矩阵调入后,就可以在屏幕上输出该矩阵 - myMat = mat(myl) + myMat = imgLoadData('input/14.SVD/0_5.txt') + print "****original matrix****" # 对原始图像进行SVD分解并重构图像e printMat(myMat, thresh) + # 通过Sigma 重新构成SigRecom来实现 # Sigma是一个对角矩阵,因此需要建立一个全0矩阵,然后将前面的那些奇异值填充到对角线上。 U, Sigma, VT = la.svd(myMat) - SigRecon = mat(zeros((numSV, numSV))) - for k in range(numSV): - SigRecon[k, k] = Sigma[k] + # SigRecon = mat(zeros((numSV, numSV))) + # for k in range(numSV): + # SigRecon[k, k] = Sigma[k] + + # 分析插入的 Sigma 长度 + analyse_data(Sigma, 20) + + SigRecon = mat(eye(numSV) * Sigma[: numSV]) reconMat = U[:, :numSV] * SigRecon * VT[:numSV, :] print "****reconstructed matrix using %d singular values *****" % numSV printMat(reconMat, thresh)