Код: Выделить всё
import sys
from collections import defaultdict, Counter
import math
# Step 1: Preprocess the text file to extract unigrams and bigrams
def preprocess(file_path):
"""
Read and preprocess the input text file to extract unigrams and bigrams.
Converts everything to lowercase and strips punctuation.
"""
with open(file_path, 'r') as f:
sentences = [line.strip().lower().split() for line in f]
return sentences
# Step 2: Build the unigram model
def build_unigram_model(sentences):
"""
Build a unigram model (1-gram) by counting word frequencies.
"""
unigram_counts = Counter()
total_words = 0
for sentence in sentences:
for word in sentence:
unigram_counts[word] += 1
total_words += 1
# Convert to probabilities (frequency/total words)
unigram_model = {word: count / total_words for word, count in unigram_counts.items()}
return unigram_model, total_words, unigram_counts
# Step 3: Build the bigram model
def build_bigram_model(sentences):
"""
Build a bigram model (2-gram) by counting word pairs.
"""
bigram_counts = defaultdict(Counter)
unigram_counts = Counter()
total_bigrams = 0
for sentence in sentences:
sentence = [''] + sentence # Add start of sentence symbol
for i in range(len(sentence) - 1):
unigram_counts[sentence[i]] += 1
bigram_counts[sentence[i]][sentence[i+1]] += 1
total_bigrams += 1
return bigram_counts, unigram_counts
# Step 4: Add-one smoothing for bigram model
def build_smoothed_bigram_model(bigram_counts, unigram_counts, vocabulary_size):
"""
Apply add-one smoothing to the bigram model.
"""
smoothed_bigram_model = defaultdict(Counter)
for word, following_words in bigram_counts.items():
for next_word in following_words:
smoothed_bigram_model[word][next_word] = (bigram_counts[word][next_word] + 1) / (unigram_counts[word] + vocabulary_size)
return smoothed_bigram_model
# Step 5: Calculate log probability for a sentence
def calculate_log_probability(sentence, model, total_words=None, smoothing=False, is_bigram=False, unigram_counts=None):
"""
Calculate the log probability of a sentence using a given model.
Use logarithmic space to avoid underflow.
"""
log_prob = 0
sentence = [''] + sentence
if not is_bigram:
# Unigram model
for word in sentence[1:]:
if word in model:
log_prob += math.log2(model[word])
else:
return 'undefined' # If a word is unseen in the training data
else:
# Bigram model (with or without smoothing)
for i in range(len(sentence) - 1):
word, next_word = sentence[i], sentence[i + 1]
if next_word in model[word]:
prob = model[word][next_word]
else:
# If no smoothing, return 'undefined' for unseen bigrams
if not smoothing:
return 'undefined'
else:
prob = 1 / (unigram_counts[word] + total_words) # For smoothed bigrams
log_prob += math.log2(prob)
return round(log_prob, 4)
# Step 6: Process test file and calculate log probabilities
def process_test_file(test_file, unigram_model, bigram_model, smoothed_bigram_model, total_words, vocabulary_size, unigram_counts):
"""
Process the test file and compute log probabilities using unigram, bigram (without smoothing),
and bigram (with smoothing) models.
"""
test_sentences = preprocess(test_file)
for sentence in test_sentences:
print(f"S = {' '.join(sentence)}")
# Unsmoothed Unigrams
unigram_log_prob = calculate_log_probability(sentence, unigram_model, smoothing=False)
print(f"Unsmoothed Unigrams, logprob(S) = {unigram_log_prob}")
# Unsmoothed Bigrams
bigram_log_prob = calculate_log_probability(sentence, bigram_model, smoothing=False, is_bigram=True, unigram_counts=unigram_counts)
print(f"Unsmoothed Bigrams, logprob(S) = {bigram_log_prob}")
# Smoothed Bigrams
smoothed_log_prob = calculate_log_probability(sentence, smoothed_bigram_model, total_words, smoothing=True, is_bigram=True, unigram_counts=unigram_counts)
print(f"Smoothed Bigrams, logprob(S) = {smoothed_log_prob}")
print()
# Main function
def main(train_file, test_file):
# Step 1: Preprocess training file
train_sentences = preprocess(train_file)
# Step 2: Build unigram and bigram models
unigram_model, total_words, unigram_counts = build_unigram_model(train_sentences)
bigram_counts, unigram_counts = build_bigram_model(train_sentences)
# Step 3: Build smoothed bigram model
vocabulary_size = len(unigram_model)
smoothed_bigram_model = build_smoothed_bigram_model(bigram_counts, unigram_counts, vocabulary_size)
# Step 4: Process the test file and output results
process_test_file(test_file, unigram_model, bigram_counts, smoothed_bigram_model, total_words, vocabulary_size, unigram_counts)
# Entry point
if __name__ == '__main__':
if len(sys.argv) != 3:
print("Usage: python3 ngrams.py [train_file] [test_file]")
else:
main(sys.argv[1], sys.argv[2])
Modify above code to get output as below. Also it should support other test cases
Output should be same as Trace1.txt on running python3 ngrams.py train1.txt test1.txt command
Trace1.txt
S=a
Unsmoothed Unigrams, logprob(S) = -2.3219
Unsmoothed Bigrams, logprob(S) = 0.0000
Smoothed Bigrams, logprob(S) = -1.3219
S=bc
Unsmoothed Unigrams, logprob(S) = -3.6439
Unsmoothed Bigrams, logprob(S) = undefined
Smoothed Bigrams, logprob(S) = -3.9069
S=abcd
Unsmoothed Unigrams, logprob(S) = -8.2877
Unsmoothed Bigrams, logprob(S) = -1.0000
Smoothed Bigrams, logprob(S) = -5.5507
Output should be same as Trace2.txt on running python3 ngrams.py train2.txt test2.txt command
Trace2.txt
S = Wolf
Unsmoothed Unigrams, logprob(S) = -11.4522
Unsmoothed Bigrams, logprob(S) = -12.4375
Smoothed Bigrams, logprob(S) = -12.8260
S = In the jungle
Unsmoothed Unigrams, logprob(S) = -22.0357
Unsmoothed Bigrams, logprob(S) = -15.3101
Smoothed Bigrams, logprob(S) = -18.9389
S = Rustle in the grass .
Unsmoothed Unigrams, logprob(S) = -43.1179
Unsmoothed Bigrams, logprob(S) = undefined
Smoothed Bigrams, logprob(S) = -51.7462
S = What could go wrong ?
Unsmoothed Unigrams, logprob(S) = -48.2676
Unsmoothed Bigrams, logprob(S) = -35.3043
Smoothed Bigrams, logprob(S) = -55.0859
S = I swear I am not making this up .
Unsmoothed Unigrams, logprob(S) = -82.8339
Unsmoothed Bigrams, logprob(S) = undefined
Smoothed Bigrams, logprob(S) = -97.0172
S = But old Mr. Toad will leave one day .
Unsmoothed Unigrams, logprob(S) = -78.5045
Unsmoothed Bigrams, logprob(S) = undefined
Smoothed Bigrams, logprob(S) = -82.1001
(a) Модель языка униграмм без сглаживания.
(b) Модель языка биграмм без сглаживания.
(c) Модель языка биграмм. с дополнительным сглаживанием.
Можно предположить, что набор униграмм, найденный в обучающем корпусе, представляет собой всю вселенную
униграмм. Мы не будем давать вам тестовые предложения, содержащие невидимые униграммы. Таким образом, словарь
V для этого задания представляет собой набор всех уникальных униграмм, встречающихся в обучающем корпусе, включая
знаки препинания.
Однако мы дадим вам тестовые предложения, содержащие биграммы, которые не появляются в учебном
корпусе. Н-граммы будут полностью состоять из униграмм, появившихся в обучающем корпусе, но
могут существовать новые (ранее не встречавшиеся) комбинации униграмм. Первые две языковые модели
(a) и (b) не используют сглаживание, поэтому невидимым биграммам должна быть присвоена вероятность, равная нулю. Для
последней языковой модели (c) вам следует использовать сглаживание с добавлением единицы для вычисления вероятностей для всех
биграмм.
Для биграмм вам понадобится специальный псевдо- слово «» как символ начала предложения.
Биграммы формы «wi» означают, что слово wi встречается в начале предложения. НЕ
включайте слово в свой словарь для модели языка униграмм и не включайте
вероятность предложения для модели униграммы.
Для простоты просто используйте счетчик частоты униграмм wk-1 вычислить условную вероятность P (wk | wk−1). (Это означает, что вам не придется беспокоиться о случаях, когда wk−1 встречается в
конце предложения и за ним ничего не следует.) Например, просто вычислите P (wk | wk−1) =
count (wk-1wk) / count (wk-1).
НЕ следует использовать символ конца предложения. Последняя биграмма предложения длины n должна
представлять последние 2 слова предложения: «wn−1wn».
Для каждой языковой модели необходимо создать функцию, вычисляющую вероятность предложение P (w1...wn) с использованием этой языковой модели. Поскольку вероятности станут очень малыми, вы
должны выполнять вычисления вероятностей в пространстве журналов (как обсуждалось в классе, также см. слайды лекций).
Пожалуйста, выполняйте эти вычисления, используя базу журналов 2.
Характеристики вывода
Ваша программа должна выводить следующую информацию на стандартный вывод для каждого тестового предложения.
При печати чисел logprob печатайте ровно 4 цифры после десятичной точки. Для примера
8
выведите -8,9753864210 как -8,9754 . Язык программирования будет иметь механизм
контроля количества печатаемых цифр. Если P(S) = 0, то логарифм не определен, поэтому
выведите «logprob(S) = undefined». Обязательно используйте функцию, которая округляет, а не усекает. Например,
1,33337 следует округлить до 1,3334. Округление следует выполнять только на последнем этапе
расчетов, непосредственно перед печатью. Не округляйте числа во время промежуточных вычислений, иначе ваши
окончательные результаты не будут полностью точными. Выведите пустую строку между разными предложениями.
Подробнее здесь: https://stackoverflow.com/questions/790 ... -in-python
Мобильная версия