Несколько ngrams в матрице перехода, вероятность не добавляется к 1

Я пытаюсь найти способ сделать матрицу перехода, используя униграммы, биграммы и триграммы для заданного текста, используя python и numpy. Вероятности каждой строки должны быть равны единице. Сначала я сделал это с биграммами, и это сработало нормально:

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

Но теперь я хочу сложить униграммы и триграммы и взвесить их вероятности (триграммы * .6, биграммы * .2, униграммы * .2). Я не думаю, что мой питон очень лаконичен, что является одной из проблем, но также я не знаю, как использовать несколько n-грамм (и веса, хотя, честно говоря, веса вторичны), чтобы я мог получить все вероятности из любой заданной строки, чтобы добавить к одному.

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_grams исходят из этого - он просто создает словарь 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

Ваш код не воспроизводится, пожалуйста, добавьте, откуда берется ngrams (API). Также предоставьте образец word_dict из возможных.

Akshay Sehgal 26.12.2020 08:04
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
1
495
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я реализовал образец для вычисления униграмм, биграмм и триграмм. Вы можете использовать zip для простого соединения предметов. Кроме того, Counter используется для подсчета предметов, а defaultdict используется для вероятности предметов. defaultdict важно, когда ключ не отображается в наборе, возвращает ноль. В противном случае вы должны добавить предложение if, чтобы избежать None.

from collections import Counter, defaultdict

def calculate_grams(items_list):
  # count items in list
  counts = Counter()
  for item in items_list:
    counts[item] += 1

  # calculate probabilities, defaultdict returns 0 if not found
  prob = defaultdict(float)
  for item, count in counts.most_common():
    prob[item] = count / len(items_list)

  return prob

def calculate_bigrams(words):
  # tuple first and second items
  return calculate_grams(list(zip(words, words[1:])))

def calculate_trigrams(words):
  # tuple first, second and third items
  return calculate_grams(list(zip(words, words[1:], words[2:])))


dataset = ['a', 'b', 'b', 'c', 'a', 'a', 'a', 'b', 'e', 'e', 'c']

# create dictionary
dictionary = set(dataset)
print("Dictionary", dictionary)

unigrams = calculate_grams(dataset)
print("Unigrams", unigrams)

bigrams = calculate_bigrams(dataset)
print("Bigrams", bigrams)

trigrams = calculate_trigrams(dataset)
print("Trigrams", trigrams)

# Testing
test_words = ['a', 'b']
print("Testing", test_words)

for c in dictionary:
  # calculate each probabilities
  unigram_prob = unigrams[c]
  bigram_prob = bigrams[(test_words[-1], c)]
  trigram_prob = bigrams[(test_words[-2], test_words[-1], c)]
  # calculate total probability
  prob = .2 * unigram_prob + .2 * bigram_prob + .4 * trigram_prob
  print(c, prob)

Выход:

Unigrams defaultdict(<class 'float'>, {'a': 0.36363636363636365, 'b': 0.2727272727272727, 'c': 0.18181818181818182, 'e': 0.18181818181818182})
Bigrams defaultdict(<class 'float'>, {('a', 'b'): 0.2, ('a', 'a'): 0.2, ('b', 'b'): 0.1, ('b', 'c'): 0.1, ('c', 'a'): 0.1, ('b', 'e'): 0.1, ('e', 'e'): 0.1, ('e', 'c'): 0.1})
Trigrams defaultdict(<class 'float'>, {('a', 'b', 'b'): 0.1111111111111111, ('b', 'b', 'c'): 0.1111111111111111, ('b', 'c', 'a'): 0.1111111111111111, ('c', 'a', 'a'): 0.1111111111111111, ('a', 'a', 'a'): 0.1111111111111111, ('a', 'a', 'b'): 0.1111111111111111, ('a', 'b', 'e'): 0.1111111111111111, ('b', 'e', 'e'): 0.1111111111111111, ('e', 'e', 'c'): 0.1111111111111111})

Testing ['a', 'b']
e 0.05636363636363637
b 0.07454545454545455
c 0.05636363636363637
a 0.07272727272727274
Ответ принят как подходящий

Попробуем сделать это на чистом Python наиболее эффективным способом, полагаясь только на списки и словари.

Предположим, у нас есть игрушечный текст, состоящий из 3 слов «а», «б» и «в»:

np.random.seed(42)
text = " ".join([np.random.choice(list("abc")) for _ in range(100)])
text
'c a c c a a c b c c c c a c b a b b b b a a b b a a a c c c b c b b c 
 b c c a c a c c a a c b a b b b a b a b c c a c c b a b b b b b b b a 
 c b b b b b b c c b c a b a a b c a b a a a a c a a a c a a'

Затем, чтобы сделать униграммы, биграммы и триграммы, вы можете действовать следующим образом:

unigrams = text.split()
unigram_counts = dict()
for unigram in unigrams:
    unigram_counts[unigram] = unigram_counts.get(unigram, 0) +1

bigrams = ["".join(bigram) for bigram in zip(unigrams[:-1], unigrams[1:])]
bigram_counts = dict()
for bigram in bigrams:
    bigram_counts[bigram] = bigram_counts.get(bigram, 0) +1

trigrams = ["".join(trigram) for trigram in zip(unigrams[:-2], unigrams[1:-1],unigrams[2:])]
trigram_counts = dict()
for trigram in trigrams:
    trigram_counts[trigram] = trigram_counts.get(trigram, 0) +1

Чтобы включить веса и нормализовать:

weights = [.2,.2,.6]
dics = [unigram_counts, bigram_counts, trigram_counts]
weighted_counts = {k:v*w for d,w in zip(dics, weights) for k,v in d.items()}
#desired output
freqs = {k:v/sum(weighted_counts.values()) for k,v in weighted_counts.items()}

Что у нас есть:

pprint(freqs)

{'a': 0.06693711967545637,
 'aa': 0.02434077079107505,
 'aaa': 0.024340770791075043,
...

Наконец, проверка работоспособности:

print(sum(freqs.values()))

0.999999999999999

Этот код может быть дополнительно настроен, чтобы включить ваши правила токенизации, например, или сделать его короче, проходя по разным граммам одновременно.

Это почти отвечает на мой вопрос, но частоты здесь показывают частоту каждой униграммы, биграммы и триграммы, и я действительно хочу посмотреть, смогу ли я объединить их в матрицу. Например, как я могу сделать так, чтобы для одного слова, если есть биграмма, у нас была некоторая вероятность, но если есть триграмма, у нас была эта вероятность ПЛЮС более взвешенная вероятность триграммы? Я пытаюсь сделать взвешенную цепь Маркова

iamjane 06.01.2021 16:12

>>> but the frequencies here show the frequency of each unigram. Не True. Вы задали вопрос так, что сумма взвешенных вероятностей равна 1. Я ответил на него.

Sergey Bushmanov 06.01.2021 21:55

Другие вопросы по теме