Анализ тональности текста на Node.js

ib_gvg4f3vdu0wiwjyhrjctmd8g.jpeg

Всем привет. Тема достаточно интересная и может показаться довольно не простой в реализации. Но я человек практический и хочу прикоснуться к прекрасному особо не напрягаясь. Сегодня мы с вами сделаем «микросервис» для анализа сентиментальности / тональности текста. А походу дела, еще несколько интересных вещей которые помогут вам для подготовки своего текстового обращения к Скайнету.


Интро

Зачем? Это очень хороший вопрос. Но прежде чем на него ответить, давайте немного поумнеем и узнаем, что же такое анализ тональности текста и что такое тональность?


Ана́лиз тона́льности те́кста (сентимент-анализ, англ. Sentiment analysis, англ. Opinion mining) — класс методов контент-анализа в компьютерной лингвистике, предназначенный для автоматизированного выявления в текстах эмоционально окрашенной лексики и эмоциональной оценки авторов (мнений) по отношению к объектам, речь о которых идёт в тексте.

Тональность — это эмоциональное отношение автора высказывания к некоторому объекту (объекту реального мира, событию, процессу или их свойствам/атрибутам), выраженное в тексте. Эмоциональная составляющая, выраженная на уровне лексемы или коммуникативного фрагмента, называется лексической тональностью (или лексическим сентиментом). Тональность всего текста в целом можно определить как функцию (в простейшем случае сумму) лексических тональностей составляющих его единиц (предложений) и правил их сочетания.

by wikipedia


Зачем?

У меня частенько от прочтения википедии больше вопросов чем ответов. Давайте упростим — тональности текста говорит нам о «настроении текста». Например «а ну иди сюда мать твою…» сигнализирует о наличии проблем у слушателя. «Дорогой, я дома» — чуть получше, но по ситуации.

5sxzdlj-cbh9r3axka1xf0lmcwq.gif

Использовать подобный анализ можно для поиска позитивных новостей, для фильтрования негативных комментариев, построение рейтинга продукта по отзывам и так далее. Я думаю общая идея понятна.


Устанавливаем необходимое

Раз мы собрались использовать Node.js, то нам понадобится Express. Вы можете использовать что угодно, Express аля low level и не критичен для поставленной задачи.

npm install -g express-generator

[express-generator](https://expressjs.com/en/starter/generator.html) — это своего рода create-react-app для Express фреймворка.

Генерируем приложение в папке node_nlp:

express node_nlp --no-view

можно упростить две прошлые команды используя npx:

npx express-generator node_nlp --no-view

Для старта приложения переходим в папку, качаем зависимости и запускам:

cd node_nlp
npm install
npm start

Для того, что бы не тратить время на ручную перезагрузку сервера на каждое изменение, давайте поставим и настроим nodemon:

npm install --save nodemon

Небольшие правки в package.json:

"dev": "nodemon ./bin/www"

И для разработки используем:

npm run dev

Давайте еще сразу поставим охапку пакетов, я расскажу зачем они нужны по ходу дела. Просто иначе туториал норовить быть сетапом проекта и я уже забыл о чем пишу со всеми этими npm install.

npm install --save natural apos-to-lex-form spelling-corrector stopword

ydq56yhahqjyit4dpuqgntr0s2i.png


Роутинг

По факту у нас будет всего один ендпоинт, в папке ./routers, есть файл index.js его мы и будем кромсать:

const express = require('express');
const natural = require('natural');

const router = express.Router();

router.post('/', function(req, res, next) {
  const { text } = req.body;
});

module.exports = router;

Простенький POST ендпоинт который принимает body с полем text.


Процессинг

Если вы как и я, хоть как-то соприкасались с созданием скайнета, обрабатывали данные, то наверняка знаете, что процесс подготовки так же важен, как и процесс обработки данных. Нам нужно минимизировать различный шум и возможные ошибки, что бы вы не пошли в сторону говорящего «а ну иди сюда мать твою…».


Избавляемся от сокращений

p6bfb4bi1gveyau9ek44ed7hxhc.gif

Так как «микросервис» у нас будет заниматься анализом тональности английского языка, нам нужно продумать как превратить такие сокращения как I«m, you«re в I am, you are.
Для этого мы будем использовать apos-to-lex-form

const express = require('express');
const natural = require('natural');
const aposToLexForm = require('apos-to-lex-form')

const router = express.Router();

router.post('/', function(req, res, next) {
  const { text } = req.body;
  const lexedText = aposToLexForm(text);
});

module.exports = router;


Конвертируем текст в lowercase (нижний регистр)

Для того, что бы слова ИДИ СЮДА и иди сюда воспринимались одинаково, надо быть уверенным в том, что весь текст в одном регистре.

const casedReview = lexedText.toLowerCase();


Удаляем лишние символы

Для очередного улучшения точности нашего анализа, следует удалить лишние символы, мне трудно сказать какой тональности @#$%^# такие вот символы. По этому удаляем все лишнее и оставляем только буквы.
Используем стандартную функцию JavaScript — replace():

const alphaOnlyReview = casedReview.replace(/[^a-zA-Z\s]+/g, '')


Токенизация

Токенизация это процесс разбиения текста на индивидуальные составляющие. Например слово является токеном предложения, а предложение в свою очередь токеном параграфа.

Здесь на сцену врывается наша главная лошадка Natural.
В данном пакете нам предоставляется инструмент токенизации WordTokenizer:

...

const { WordTokenizer } = natural;
const tokenizer = new WordTokenizer();
const tokenizedReview = tokenizer.tokenize(alphaOnlyReview);

...


Исправляем ошибки

Так как текст может прийти откуда угодно, есть вероятность ошибок. Нам надо попытаться их исправить. С этим нам поможет spelling-corrector.

const express = require('express');
const natural = require('natural');
const aposToLexForm = require('apos-to-lex-form');
const SpellCorrector = require('spelling-corrector');

const router = express.Router();

const spellCorrector = new SpellCorrector();
spellCorrector.loadDictionary();

router.post('/', function(req, res, next) {
  const { text } = req.body;
  const lexedText = aposToLexForm(text);
  const casedReview = lexedText.toLowerCase();
  const alphaOnlyReview = casedReview.replace(/[^a-zA-Z\s]+/g, '')

  const { WordTokenizer } = natural;
  const tokenizer = new WordTokenizer();
  const tokenizedReview = tokenizer.tokenize(alphaOnlyReview);

  tokenizedReview.forEach((word, index) => {
    tokenizedReview[index] = spellCorrector.correct(word);
  });
});

module.exports = router;


Удаляем стоп слова

lp5r_uo_fh_dra2is3ct56ufrzq.gif

Стоп слова, это своего рода слова паразиты. Ну как бы, эээ, ууу, хыы не, что бы прямо такие паразиты, а просто лишние слова, которые не делают абсолютно никакой погоды для нашего тональника. С удалением таких слов нам поможет пакет stopword.

const SW = require('stopword');

...

const filteredReview = SW.removeStopwords(tokenizedReview);


Стемминг (Stemming)

Стемминг, это процесс нормализации слов. Например «giving,» «gave,» and «giver» в простую форму «give».
Мы не будем выделять это в отдельный шаг, так как SentimentAnalyzer который предоставляет нам пакет Natural, может сделать это за нас.


Тональный анализ текста с помощью Natural

Все! Мы добрались. Теперь нашу заяву примет Скайнет и все поймет. Пора скармливать текст в SentimentAnalyzer и понять, позитивно ли мы звучим в столь толерантном обществе или нет.

Тональный анализ работает достаточно не замысловато. Пакет Natural имеет свой словарь слов с «полярностью» слов. Например слово «good» имеет полярность 3, а слово «bad» -3. По факту все этих «очки» суммируются и нормализуется по размеру предложения. По этому собственно мы и сделали столько для отчистки нашего текста от всего лишнего, что бы нечего мешало нам получить адекватную оценку.
Текст позитивный если оценка положительная, негативный если отрицательная и нейтральная если мы получили 0.

SentimentAnalyzer принимает 3 параметра:


  • Язык текста
  • Стримитить или не стримить
  • Словарь (AFINN, Senticon, Pattern), это встроенно

Весь итоговый код с тональным анализом текста в конце:

const express = require('express');
const natural = require('natural');
const aposToLexForm = require('apos-to-lex-form');
const SpellCorrector = require('spelling-corrector');
const SW = require('stopword');

const router = express.Router();

const spellCorrector = new SpellCorrector();
spellCorrector.loadDictionary();

router.post('/', function(req, res, next) {
  const { text } = req.body;
  const lexedText = aposToLexForm(text);
  const casedReview = lexedText.toLowerCase();
  const alphaOnlyReview = casedReview.replace(/[^a-zA-Z\s]+/g, '')

  const { WordTokenizer } = natural;
  const tokenizer = new WordTokenizer();
  const tokenizedReview = tokenizer.tokenize(alphaOnlyReview);

  tokenizedReview.forEach((word, index) => {
    tokenizedReview[index] = spellCorrector.correct(word);
  });

  const filteredReview = SW.removeStopwords(tokenizedReview);

  const { SentimentAnalyzer, PorterStemmer } = natural;
  const analyzer = new SentimentAnalyzer('English', PorterStemmer, 'afinn');
  const analysis = analyzer.getSentiment(filteredReview);

  res.status(200).json({ analysis });
});

module.exports = router;

Мы добавили несколько новых строк. Деструкция natural, для получения необходимых нам инструментов создали переменную для анализатора и присвоили результат переменной analysis.
Параметры SentimentAnalyzer, относительно очевидные. Язык английский так как текст который мы обрабатываем на английском. Cтемпинг слов, который я упоминал выше и словарь который нам предоставляется пакетом Natrual.

Хотелось бы мне сделать UI для этого дела, но протестировать можно в конскольке DevTools:

fetch('/', 
 {
   method: 'POST',
   headers: {
     'Content-Type': 'application/json'
   }, 
  body: JSON.stringify({text: 'hey'})
})

// {"analysis":0}

fetch('/', 
 {
   method: 'POST',
   headers: {
     'Content-Type': 'application/json'
   }, 
  body: JSON.stringify({text: 'hey f*** you'})
})

// {"analysis":-2}

fetch('/', 
 {
   method: 'POST',
   headers: {
     'Content-Type': 'application/json'
   }, 
  body: JSON.stringify({text: 'hey love you'})
})

// {"analysis":1}

Как видим работает :)

Github repo


Заключение

В этой статье мы с вами сделали «микросервис» который анализирует тональность текста. Берите его на любые разборки и анализируйте, что вам говорят оппоненты. Так же мы затронули тему подготовки данных и установили тонну зависимостей. Спасибо за внимание!

Читайте так же


© Habrahabr.ru