Как создать бота для анализа настроений в электронной почте: учебник по НЛП
Опубликовано: 2022-03-11За последние несколько лет технологии обработки естественного языка стали довольно изощренными. Многие, от технических гигантов до любителей, спешат создавать богатые интерфейсы, которые могут анализировать, понимать и реагировать на естественный язык. Alexa от Amazon, Cortana от Microsoft, Google Home от Google и Siri от Apple — все они нацелены на то, чтобы изменить то, как мы взаимодействуем с компьютерами.
Анализ настроений, подполе обработки естественного языка, состоит из методов, которые определяют тон текста или речи. Сегодня, благодаря машинному обучению и большому количеству данных, собранных из социальных сетей и обзорных сайтов, мы можем обучать модели определять тональность отрывка на естественном языке с достаточной точностью.
В этом руководстве вы узнаете, как создать бота, который может анализировать настроение получаемых им электронных писем и уведомлять вас о сообщениях, которые могут потребовать вашего внимания немедленно.
Анализ настроений в электронных письмах
Бот будет создан с использованием сочетания разработки на Java и Python. Два процесса будут взаимодействовать друг с другом с помощью Thrift. Если вы не знакомы с одним или обоими из этих языков, вы все равно можете продолжить чтение, так как основные понятия этой статьи применимы и к другим языкам.
Чтобы определить, требует ли электронное письмо вашего внимания, бот проанализирует его и определит, есть ли сильный негативный тон. Затем он отправит текстовое оповещение, если это необходимо.
Мы будем использовать Sendgrid для подключения к нашему почтовому ящику и Twilio для отправки текстовых предупреждений.
Анализ настроений: обманчиво простая задача
Есть слова, которые мы ассоциируем с положительными эмоциями, такими как любовь, радость и удовольствие. И есть слова, которые мы связываем с негативными эмоциями, такими как ненависть, печаль и боль. Почему бы не научить модель распознавать эти слова и подсчитывать относительную частоту и силу каждого положительного и отрицательного слова?
Ну, с этим есть пара проблем.
Во-первых, это проблема отрицания. Например, такое предложение, как «Персик неплохой», подразумевает положительную эмоцию, используя слово, которое мы чаще всего ассоциируем с отрицательным. Простая модель мешка слов не сможет распознать отрицание в этом предложении.
Кроме того, смешанные настроения оказываются еще одной проблемой при наивном анализе настроений. Например, такое предложение, как «Персик неплохой, но яблоко действительно ужасное», содержит смешанные чувства разной интенсивности, которые взаимодействуют друг с другом. Простой подход не сможет разрешить комбинированные чувства, различную интенсивность или взаимодействие между чувствами.
Анализ настроений с использованием рекурсивной нейронной тензорной сети
Стэнфордская библиотека обработки естественного языка для анализа настроений решает эти проблемы с помощью рекурсивной нейронной тензорной сети (RNTN).
Алгоритм RNTN сначала разбивает предложение на отдельные слова. Затем он строит нейронную сеть, в которой узлами являются отдельные слова. Наконец, добавляется тензорный слой, чтобы модель могла правильно настроить взаимодействие между словами и фразами.
Вы можете найти наглядную демонстрацию алгоритма на их официальном сайте.
Стэнфордская группа НЛП обучила рекурсивную нейронно-тензорную сеть, используя вручную помеченные обзоры фильмов IMDB, и обнаружила, что их модель способна предсказывать настроение с очень хорошей точностью.
Бот, который получает электронные письма
Первое, что вам нужно сделать, это настроить интеграцию с электронной почтой, чтобы данные можно было передавать вашему боту.
Есть много способов сделать это, но для простоты давайте настроим простой веб-сервер и используем обработчик входящего разбора Sendgrid для передачи электронных писем на сервер. Мы можем пересылать электронные письма на адрес для анализа входящей почты Sendgrid. Затем Sendgrid отправит запрос POST на наш веб-сервер, и мы сможем обрабатывать данные через наш сервер.
Для создания сервера мы будем использовать Flask, простой веб-фреймворк для Python.
В дополнение к созданию веб-сервера нам потребуется подключить веб-службу к домену. Для краткости мы не будем писать об этом в статье. Тем не менее, вы можете прочитать больше об этом здесь.
Создать веб-сервер во Flask невероятно просто.
Просто создайте app.py
и добавьте его в файл:
from flask import Flask, request import datetime app = Flask(__name__) @app.route('/analyze', methods=['POST']) def analyze(): with open('logfile.txt', 'a') as fp_log: fp_log.write('endpoint hit %s \n' % datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')) return "Got it" app.run(host='0.0.0.0')
Если мы развернем это приложение за доменным именем и перейдем к конечной точке «/analyze», вы должны увидеть что-то вроде этого:
> >> requests.post('http://sentiments.shanglunwang.com:5000/analyze').text 'Got it'
Затем мы хотим отправлять электронные письма на эту конечную точку.
Вы можете найти дополнительную документацию здесь, но вы, по сути, хотите настроить Sendgrid в качестве обработчика электронной почты и настроить Sendgrid для пересылки электронной почты на наш веб-сервер.
Вот моя настройка на Sendgrid. Это будет пересылать электронные письма на @sentibot.shanglunwang.com
в виде POST-запросов на «http://sentiments.shanglunwang.com/analyze»:
Вы можете использовать любой другой сервис, поддерживающий отправку входящих электронных писем через веб-перехватчики.
После того, как все настроите, попробуйте отправить электронное письмо на свой адрес Sendgrid. Вы должны увидеть что-то вроде этого в журналах:
endpoint hit 2017-05-25 14:35:46
Замечательно! Теперь у вас есть бот, который может получать электронные письма. Это половина того, что мы пытаемся сделать.
Теперь вы хотите дать этому боту возможность анализировать настроения в электронных письмах.
Анализ настроений по электронной почте с помощью Stanford NLP
Поскольку библиотека Stanford NLP написана на Java, мы хотим создать механизм анализа на Java.
Начнем с загрузки библиотеки Stanford NLP и моделей в Maven. Создайте новый проект Java, добавьте следующее в свои зависимости Maven и импортируйте:
<dependency> <groupId>edu.stanford.nlp</groupId> <artifactId>stanford-corenlp</artifactId> <version>3.6.0</version> </dependency>
Доступ к механизму анализа настроений Stanford NLP можно получить, указав аннотатор настроений в коде инициализации конвейера. Затем аннотацию можно получить в виде древовидной структуры.
Для целей этого урока мы просто хотим узнать общую тональность предложения, поэтому нам не нужно анализировать дерево. Нам просто нужно посмотреть на базовый узел.
Это делает основной код относительно простым:
package seanwang; import edu.stanford.nlp.pipeline.*; import edu.stanford.nlp.util.CoreMap; import edu.stanford.nlp.ling.CoreAnnotations; import edu.stanford.nlp.sentiment.SentimentCoreAnnotations; import java.util.*; public class App { public static void main( String[] args ) { Properties pipelineProps = new Properties(); Properties tokenizerProps = new Properties(); pipelineProps.setProperty("annotators", "parse, sentiment"); pipelineProps.setProperty("parse.binaryTrees", "true"); pipelineProps.setProperty("enforceRequirements", "false"); tokenizerProps.setProperty("annotators", "tokenize ssplit"); StanfordCoreNLP tokenizer = new StanfordCoreNLP(tokenizerProps); StanfordCoreNLP pipeline = new StanfordCoreNLP(pipelineProps); String line = "Amazingly grateful beautiful friends are fulfilling an incredibly joyful accomplishment. What an truly terrible idea."; Annotation annotation = tokenizer.process(line); pipeline.annotate(annotation); // normal output for (CoreMap sentence : annotation.get(CoreAnnotations.SentencesAnnotation.class)) { String output = sentence.get(SentimentCoreAnnotations.SentimentClass.class); System.out.println(output); } } }
Попробуйте несколько предложений, и вы должны увидеть соответствующие аннотации. Вывод кода примера:
Very Positive Negative
Интеграция бота и механизма анализа
Итак, у нас есть программа для анализа настроений, написанная на Java, и почтовый бот, написанный на Python. Как заставить их говорить друг с другом?
Есть много возможных решений этой проблемы, но здесь мы будем использовать Thrift. Мы запустим Sentiment Analyzer в качестве Thrift-сервера и почтового бота в качестве Thrift-клиента.
Thrift — это генератор кода и протокол, используемый для того, чтобы два приложения, часто написанные на разных языках, могли взаимодействовать друг с другом с использованием определенного протокола. Команды Polyglot используют Thrift для создания сетей микросервисов, чтобы использовать лучшее из каждого языка, который они используют.
Чтобы использовать Thrift, нам понадобятся две вещи: файл .thrift
для определения конечных точек службы и сгенерированный код для использования протокола, определенного в файле .proto
. Для сервиса анализатора sentiment.thrift
выглядит так:
namespace java sentiment namespace py sentiment service SentimentAnalysisService { string sentimentAnalyze(1:string sentence), }
Мы можем сгенерировать клиентский и серверный код, используя этот файл .thrift. Бегать:

thrift-0.10.0.exe --gen py sentiment.thrift thrift-0.10.0.exe --gen java sentiment.thrift
Примечание. Я сгенерировал код на компьютере с Windows. Вы захотите использовать соответствующий путь к исполняемому файлу Thrift в вашей среде.
Теперь давайте внесем соответствующие изменения в механизм анализа, чтобы создать сервер. Ваша Java-программа должна выглядеть так:
SentimentHandler.java
package seanwang; public class SentimentHandler implements SentimentAnalysisService.Iface { SentimentAnalyzer analyzer; SentimentHandler() { analyzer = new SentimentAnalyzer(); } public String sentimentAnalyze(String sentence) { System.out.println("got: " + sentence); return analyzer.analyze(sentence); } }
В этом обработчике мы получаем запрос на анализ по протоколу Thrift.
SentimentAnalyzer.java
package seanwang; // ... public class SentimentAnalyzer { StanfordCoreNLP tokenizer; StanfordCoreNLP pipeline; public SentimentAnalyzer() { Properties pipelineProps = new Properties(); Properties tokenizerProps = new Properties(); pipelineProps.setProperty("annotators", "parse, sentiment"); pipelineProps.setProperty("parse.binaryTrees", "true"); pipelineProps.setProperty("enforceRequirements", "false"); tokenizerProps.setProperty("annotators", "tokenize ssplit"); tokenizer = new StanfordCoreNLP(tokenizerProps); pipeline = new StanfordCoreNLP(pipelineProps); } public String analyze(String line) { Annotation annotation = tokenizer.process(line); pipeline.annotate(annotation); String output = ""; for (CoreMap sentence : annotation.get(CoreAnnotations.SentencesAnnotation.class)) { output += sentence.get(SentimentCoreAnnotations.SentimentClass.class); output += "\n"; } return output; } }
Analyzer использует библиотеку Stanford NLP для определения тональности текста и создает строку, содержащую аннотации тональности для каждого предложения в тексте.
SentimentServer.java
package seanwang; // ... public class SentimentServer { public static SentimentHandler handler; public static SentimentAnalysisService.Processor processor; public static void main(String [] args) { try { handler = new SentimentHandler(); processor = new SentimentAnalysisService.Processor(handler); Runnable simple = new Runnable() { public void run() { simple(processor); } }; new Thread(simple).start(); } catch (Exception x) { x.printStackTrace(); } } public static void simple(SentimentAnalysisService.Processor processor) { try { TServerTransport serverTransport = new TServerSocket(9090); TServer server = new TSimpleServer(new Args(serverTransport).processor(processor)); System.out.println("Starting the simple server..."); server.serve(); } catch (Exception e) { e.printStackTrace(); } } }
Обратите внимание, что я не включил сюда файл SentimentAnalysisService.java
, так как это сгенерированный файл. Вы захотите поместить сгенерированный код в место, где остальная часть вашего кода может получить к нему доступ.
Теперь, когда у нас есть сервер, давайте напишем клиент Python для использования сервера.
клиент.py
from sentiment import SentimentAnalysisService from thrift.transport import TSocket from thrift.transport import TTransport from thrift.protocol import TBinaryProtocol class SentimentClient: def __init__(self, server='localhost', socket=9090): transport = TSocket.TSocket(server, socket) transport = TTransport.TBufferedTransport(transport) protocol = TBinaryProtocol.TBinaryProtocol(transport) self.transport = transport self.client = SentimentAnalysisService.Client(protocol) self.transport.open() def __del__(self): self.transport.close() def analyze(self, sentence): return self.client.sentimentAnalyze(sentence) if __name__ == '__main__': client = SentimentClient() print(client.analyze('An amazingly wonderful sentence'))
Запустите это, и вы должны увидеть:
Very Positive
Здорово! Теперь, когда у нас есть сервер, работающий и взаимодействующий с клиентом, давайте интегрируем его с почтовым ботом, создав экземпляр клиента и передав ему электронную почту.
import client # ... @app.route('/analyze', methods=['POST']) def analyze(): sentiment_client = client.SentimentClient() with open('logfile.txt', 'a') as fp_log: fp_log.write(str(request.form.get('text'))) fp_log.write(request.form.get('text')) fp_log.write(sentiment_client.analyze(request.form.get('text'))) return "Got it"
Теперь разверните службу Java на том же компьютере, на котором запущен веб-сервер, запустите службу и перезапустите приложение. Отправьте боту электронное письмо с тестовым предложением, и вы должны увидеть что-то вроде этого в файле журнала:
Amazingly wonderfully positive and beautiful sentence. Very Positive
Анализ электронной почты
Отлично! Теперь у нас есть почтовый бот, способный выполнять анализ настроений! Мы можем отправить электронное письмо и получить тег настроения для каждого отправленного нами предложения. Теперь давайте рассмотрим, как мы можем сделать интеллект действенным.
Для простоты давайте сосредоточимся на электронных письмах, в которых много негативных и очень негативных предложений. Давайте воспользуемся простой системой подсчета очков и скажем, что если электронное письмо содержит более 75% предложений с негативным настроением, мы отметим это как потенциальное тревожное электронное письмо, которое может потребовать немедленного ответа. Давайте реализуем логику подсчета очков в маршруте анализа:
@app.route('/analyze', methods=['POST']) def analyze(): text = str(request.form.get('text')) sentiment_client = client.SentimentClient() text.replace('\n', '') # remove all new lines sentences = text.rstrip('.').split('.') # remove the last period before splitting negative_sentences = [ sentence for sentence in sentences if sentiment_client.analyze(sentence).rstrip() in ['Negative', 'Very negative'] # remove newline char ] urgent = len(negative_sentences) / len(sentences) > 0.75 with open('logfile.txt', 'a') as fp_log: fp_log.write("Received: %s" % (request.form.get('text'))) fp_log.write("urgent = %s" % (str(urgent))) return "Got it"
Приведенный выше код делает несколько предположений, но будет работать в демонстрационных целях. Отправьте пару писем своему боту, и вы должны увидеть анализ электронной почты в журналах:
Received: Here is a test for the system. This is supposed to be a non-urgent request. It's very good! For the most part this is positive or neutral. Great things are happening! urgent = False Received: This is an urgent request. Everything is truly awful. This is a disaster. People hate this tasteless mail. urgent = True
Отправка оповещения
Мы почти закончили!
Мы создали почтового бота, который может получать электронные письма, выполнять анализ настроений и определять, требует ли электронное письмо немедленного внимания. Теперь нам просто нужно отправить текстовое оповещение, когда электронное письмо особенно негативное.
Мы будем использовать Twilio для отправки текстового оповещения. Их Python API, который задокументирован здесь, довольно прост. Давайте изменим маршрут анализа, чтобы он отправлял запрос при получении срочного запроса.
def send_message(body): twilio_client.messages.create( to=on_call, from_=os.getenv('TWILIO_PHONE_NUMBER'), body=body ) app = Flask(__name__) @app.route('/analyze', methods=['POST']) def analyze(): text = str(request.form.get('text')) sentiment_client = client.SentimentClient() text.replace('\n', '') # remove all new lines sentences = text.rstrip('.').split('.') # remove the last period before splitting negative_sentences = [ sentence for sentence in sentences if sentiment_client.analyze(sentence).rstrip() in ['Negative', 'Very negative'] # remove newline char ] urgent = len(negative_sentences) / len(sentences) > 0.75 if urgent: send_message('Highly negative email received. Please take action') with open('logfile.txt', 'a') as fp_log: fp_log.write("Received: " % request.form.get('text')) fp_log.write("urgent = %s" % (str(urgent))) fp_log.write("\n") return "Got it"
Вам нужно будет установить переменные среды для учетных данных вашей учетной записи Twilio и установить номер телефона для вызова, который вы можете проверить. После того, как вы это сделаете, отправьте электронное письмо на конечную точку анализа, и вы должны увидеть текст, отправленный на указанный номер телефона.
И мы закончили!
Обработка естественного языка стала проще благодаря Stanford NLP
В этой статье вы узнали, как создать бота для анализа тональности электронной почты, используя библиотеку Stanford NLP. Библиотека помогает абстрагироваться от всех мельчайших деталей обработки естественного языка и позволяет вам использовать ее в качестве строительного блока для ваших приложений НЛП.
Я надеюсь, что этот пост продемонстрировал одно из многих замечательных потенциальных применений анализа настроений, и что это вдохновит вас на создание собственного приложения НЛП.
Вы можете найти код для бота анализа тональности электронной почты в этом руководстве по НЛП на GitHub.