INPUT: X - (array) 单词-文本矩阵 K - (int) 设定的话题数 words - (list) 出现频次为前1000的单词列表 iters - (int) 设定的迭代次数 OUTPUT: P_wi_zk - (array) 话题zk条件下产生单词wi的概率数组 P_zk_dj - (array) 文本dj条件下属于话题zk的概率数组
(X, K, words, iters = 10)
| 88 | |
| 89 | #定义概率潜在语义分析函数,采用EM算法进行PLSA模型的参数估计 |
| 90 | def do_plsa(X, K, words, iters = 10): |
| 91 | ''' |
| 92 | INPUT: |
| 93 | X - (array) 单词-文本矩阵 |
| 94 | K - (int) 设定的话题数 |
| 95 | words - (list) 出现频次为前1000的单词列表 |
| 96 | iters - (int) 设定的迭代次数 |
| 97 | |
| 98 | OUTPUT: |
| 99 | P_wi_zk - (array) 话题zk条件下产生单词wi的概率数组 |
| 100 | P_zk_dj - (array) 文本dj条件下属于话题zk的概率数组 |
| 101 | |
| 102 | ''' |
| 103 | M, N = X.shape #M为单词数,N为文本数 |
| 104 | #P_wi_zk表示P(wi|zk),是一个K*M的数组,其中每个值表示第k个话题zk条件下产生第i个单词wi的概率,这里将每个值随机初始化为0-1之间的浮点数 |
| 105 | P_wi_zk = np.random.rand(K, M) |
| 106 | #对于每个话题zk,保证产生单词wi的概率的总和为1 |
| 107 | for k in range(K): |
| 108 | P_wi_zk[k] /= np.sum(P_wi_zk[k]) |
| 109 | #P_zk_dj表示P(zk|dj),是一个N*K的数组,其中每个值表示第j个文本dj条件下产生第k个话题zk的概率,这里将每个值随机初始化为0-1之间的浮点数 |
| 110 | P_zk_dj = np.random.rand(N, K) |
| 111 | #对于每个文本dj,属于话题zk的概率的总和为1 |
| 112 | for n in range(N): |
| 113 | P_zk_dj[n] /= np.sum(P_zk_dj[n]) |
| 114 | #P_zk_wi_dj表示P(zk|wi,dj),是一个M*N*K的数组,其中每个值表示在单词-文本对(wi,dj)的条件下属于第k个话题zk的概率,这里设置初始值为0 |
| 115 | P_zk_wi_dj = np.zeros((M, N, K)) |
| 116 | #迭代执行E步和M步 |
| 117 | for i in range(iters): |
| 118 | print('{}/{}'.format(i+1, iters)) |
| 119 | #执行E步 |
| 120 | for m in range(M): |
| 121 | for n in range(N): |
| 122 | sums = 0 |
| 123 | for k in range(K): |
| 124 | P_zk_wi_dj[m, n, k] = P_wi_zk[k, m] * P_zk_dj[n, k] #计算P(zk|wi,dj)的分子部分,即P(wi|zk)*P(zk|dj) |
| 125 | sums += P_zk_wi_dj[m, n, k] #计算P(zk|wi,dj)的分母部分,即P(wi|zk)*P(zk|dj)在K个话题上的总和 |
| 126 | P_zk_wi_dj[m, n, :] = P_zk_wi_dj[m, n, :] / sums #得到单词-文本对(wi,dj)条件下的P(zk|wi,dj) |
| 127 | #执行M步,计算P(wi|zk) |
| 128 | for k in range(K): |
| 129 | s1 = 0 |
| 130 | for m in range(M): |
| 131 | P_wi_zk[k, m] = 0 |
| 132 | for n in range(N): |
| 133 | P_wi_zk[k, m] += X[m, n] * P_zk_wi_dj[m, n, k] #计算P(wi|zk)的分子部分,即n(wi,dj)*P(zk|wi,dj)在N个文本上的总和,其中n(wi,dj)为单词-文本矩阵X在文本对(wi,dj)处的频次 |
| 134 | s1 += P_wi_zk[k, m] #计算P(wi|zk)的分母部分,即n(wi,dj)*P(zk|wi,dj)在N个文本和M个单词上的总和 |
| 135 | P_wi_zk[k, :] = P_wi_zk[k, :] / s1 #得到话题zk条件下的P(wi|zk) |
| 136 | #执行M步,计算P(zk|dj) |
| 137 | for n in range(N): |
| 138 | for k in range(K): |
| 139 | P_zk_dj[n, k] = 0 |
| 140 | for m in range(M): |
| 141 | P_zk_dj[n, k] += X[m, n] * P_zk_wi_dj[m, n, k] #同理计算P(zk|dj)的分子部分,即n(wi,dj)*P(zk|wi,dj)在N个文本上的总和 |
| 142 | P_zk_dj[n, k] = P_zk_dj[n, k] / np.sum(X[:, n]) #得到文本dj条件下的P(zk|dj),其中n(dj)为文本dj中的单词个数,由于我们只取了出现频次前1000的单词,所以这里n(dj)计算的是文本dj中在单词列表中的单词数 |
| 143 | return P_wi_zk, P_zk_dj |
| 144 | |
| 145 | |
| 146 | if __name__ == "__main__": |