Мы продолжаем курс «Обработка данных: обработка естественных языков на языке Python».
В данной лекции мы напишем код для синонимайзера текста. Если вы не хотите писать код, а просто его просмотреть, вы можете перейти по адресу, зайти в папку nlp_class и найти файл article_spinner.py. Мы снова будем рассматривать отзывы на электронику на Amazon.
На этот раз нам понадобится библиотека NLTK, генератор случайных чисел для отыскания вероятностей слов и, поскольку обзоры Amazon находятся в формате XMS, парсер Beautiful Soup.
import nltk
import random
import numpy as np
from bs4 import BeautifulSoup
Из примера с анализом тональности скопируем код для загрузки положительных отзывов.
positive_reviews = BeautifulSoup(open(‘electronics/positive.review’).read())
positive_reviews = positive_reviews.findAll(‘review_text’)
Прежде всего соберём все триграммы. Создадим словарь с ключом в виде предыдущего и последующего слов. Значением же будет возможное значение среднего слова. Для этого пройдёмся по всем отзывам, при этом преобразуем все слова в нижний регистр, поскольку нам не нужны два варианта одного и того же слова. Для этой же цели используем токенайзер из библиотеки NLTK.
trigrams = {}
for review in positive_reviews:
s = review.text.lower()
tokens = nltk.tokenize.word_tokenize(s)
for i in xrange(len(tokens) – 2):
k = (tokens[i], tokens[i+2])
if k not in tragrams:
trigrams[k] = []
trigrams[k].append(tokens[i+1])
Следующее, что мы должны сделать, – преобразовать наши возможные средние слова в вектор вероятностей. Для этого создадим ещё один словарь и пройдёмся по всем триграммам. В данном случае неважно, существует ли только одно возможное среднее слово, поскольку оно всё равно будет заменяться.
trigrams_probabilities = {}
for k, words in trigrams.iteritems():
if len(set(words)) > 1:
d = {}
n = 0
for w in words:
if w not in d:
d[w] = 0
d[w] += 1
n += 1
for w, c in d.iteritems():
d[w] = float(c) / n
trigrams_probabilities[k] = d
Теперь нам необходимо написать функцию, которая бы делала случайную выборку из вероятностей триграмм. Назовём её random_sample. Аргументом её будет слово, а значением – вероятность этого слова.
def random_sample(d):
r = random.random()
cumulative = 0
for w, p in d.iteritems():
cumulative += p
if r < cumulative:
return w
Следующим делом напишем функцию test_spinner для проверки синонимайзера. Она будет случайным образом выбирать отзыв, а затем пытаться изменить её. Отзыв и его модификация будет выводиться на экран, чтобы мы могли сравнить их.
def test_spinner():
review = random.choise(positive_reviews)
s = review.text.lower()
print ‘’Original:’’, s
tokens = nltk.tokenize.word.tokenize(s)
for i in xrange(len(tokens) -2):
if random.random() < 0.2:
k = (tokens[i], tokens[i*2])
if k in trigrams_probabilities:
w = random_sample(trigrams_probabilities[k])
tokens[i+1] = w
print ‘’Spun:’’
Тут надо сказать, что я предварительно проверял синонимайзер, и оказалось, что для вывода изменённого предложения необходимо сделать кое-что ещё. NLTK почему-то любит токенизировать ещё и знаки препинания – это могут быть пробелы, апострофы, точки, запятые, знаки доллара и восклицательные знаки. Поэтому надо добавить ещё строку
print ‘’ ‘’.join(tokens).replace(‘’ .’’, ‘’.’’).replace(‘’ ‘‘’, ‘’‘’’).replace(‘’ ,’’, ‘’,’’).replace(‘’$ ‘’, ‘’$’’).replace(‘’ !’’, ‘’!’’)
Итак, давайте проверим наш синонимайзер.
Первое предложение «У меня была небольшая проблема с распознаванием моей системой, когда я устанавливал впервые…» – ничего не изменено. Вообще, из всего текста изменено выражение «превосходная чёткость и яркость» на «превосходная чёткость и профессионализм», что бессмысленно.
Попробуем ещё раз. Имеем: «Пятьдесят три являются преемниками принтеров HP» – бессмыслица. «Оба они принимают только несколько карт памяти Secure Digital ёмкостью 1 Гб», «Поскольку операционная система имеет только приблизительно 1-Гб адресное пространство файловой системы», «После примерно трёх флэш-карт Secure Digital»…
Я бы рекомендовал вам проверить работу синонимайзера ещё и самостоятельно, чтобы вы могли посмтотреть на различные получаемые результаты. В большинстве случаев они бессмысленные и довольно смешные.
Итак, о чём это нам говорит? Такой результат показыват со всей определённостью, что контекст должен учитываться в гораздо большей мере, чем лишь два соседних слова. Это также показывает, что наше марковское предположение не соответсвует действительности. Вполне вероятно, что мы проделали лишь 5% работы, необходимой для того, чтобы создать действительно хороший синонимайзер текста.
Из своего опыта и результатов, которые я вижу в топе Google, такой ещё не создан.