Python与numpy构建融合多阶N-gram的转移矩阵行概率和不为1的问题
多阶n-gram加权词转移矩阵构建实现问题
目标是使用Python结合numpy,融合unigram、bigram、trigram构建词转移矩阵,要求矩阵每一行的概率总和为1。
最初单独实现bigram版转移矩阵时功能运行正常,实现代码如下:
distinct_words = list(word_dict.keys()) dwc = len(distinct_words) matrix = np.zeros((dwc, dwc), dtype=np.float) for i in range(len(distinct_words)): word = distinct_words[i] first_word_idx = i total = 0 for bigram, count in ngrams.items(): word_1, word_2 = bigram.split(" ") if word_1 == word: total += count for bigram, count in ngrams.items(): word_1, word_2 = bigram.split(" ") if word_1 == word: second_word_idx = index_dict[word_2] matrix[first_word_idx,second_word_idx] = count / total
后续计划在原有基础上引入unigram、trigram,对三类n-gram的概率设置固定融合权重:trigram权重0.6、bigram权重0.2、unigram权重0.2。
目前存在两方面问题:
- 现有Python实现代码冗余,结构不够简洁
- 不清楚多类n-gram概率的正确融合方式,无法保证矩阵任意行的概率总和严格为1
当前尝试的融合版本实现代码如下:
distinct_words = list(word_dict.keys()) dwc = len(distinct_words) matrix = np.zeros((dwc, dwc), dtype=np.float) for i in range(len(distinct_words)): word = distinct_words[i] first_word_index = i bi_total = 0 tri_total=0 tri_prob = 0 bi_prob = 0 uni_prob = word_dict[word] / len(distinct_words) if i < len(distinct_words)-1: for trigram, count in trigrams.items(): word_1, word_2, word_3 = trigram.split() if word_1 + word_2 == word + distinct_words[i+1]: tri_total += count for trigram, count in trigrams.items(): word_1, word_2, word_3 = trigram.split() if word_1 + word_2 == word + distinct_words[i+1]: second_word_index = index_dict[word_2] tri_prob = count/bigrams[word_1 + " " + word_2] for bigram, count in bigrams.items(): word_1, word_2 = bigram.split(" ") if word_1 == word: bi_total += count for bigram, count in bigrams.items(): word_1, word_2 = bigram.split(" ") if word_1 == word: second_word_index = index_dict[word_2] bi_prob = count / bi_total matrix[first_word_index,second_word_index] = (tri_prob * .4) + (bi_prob * .2) + (word_dict[word]/len(word_dict) *.2)
实现逻辑参考了公开的n-gram概率矩阵搭建方法,自认为逻辑自洽,但始终未定位到代码的问题根源。
各阶n-gram的统计数据由以下自定义函数生成,输出为以n-gram字符串为键、对应出现次数为值的字典,供问题排查参考:
def get_ngram(words, n): word_dict = {} for i, word in enumerate(words): if i > (n-2): n_gram = [] for num in range(n): index = i - num n_gram.append(words[index]) if len(n_gram) > 1: formatted_gram = "" for word in reversed(n_gram): formatted_gram += word + " " else: formatted_gram = n_gram[0] stripped = formatted_gram.strip() if formatted_gram else n_gram[0] if stripped in word_dict: word_dict[stripped] += 1 else: word_dict[stripped] = 1 return word_dict
内容的提问来源于stack exchange,提问作者iamjane
相关产品推荐
相关产品推荐

