Рубрики
Без рубрики

Проверьте настроение расслоенного канала с помощью машинного обучения

Настроения мысль, мнение, или идея, основанная на чувстве к ситуации, или образ мышления … Теги с машиной, Python, NLTK, Slack.

Настроение

мысль, мнение или идея, основанная на чувстве к ситуации, или способ думать о чем-то

Анализ настроений В контексте этой статьи является использование обработки естественного языка для количественной оценки общего мнения или настроения некоторых текста. Мы будем держать его простым и смотреть только на определение, если какой-то текст положительный или отрицательный. Положительный, как правило, бесплатный на природе. Негативное, как правило, имеет решающее значение в природе. Чтобы добавить какой-то уровень (возможно, непрактичной) практичности, мы посмотрим на анализ текста от Слабый канал.

В этой статье мы будем:

  • Скачать историю чата из общедоступного провисания (который не является обязательным, так как вы действительно можете использовать любой текст)
  • Используйте некоторые инструменты Python для обработки и проверки данных чата
  • Используйте предварительно обученную модель для классификации предложений из чата как положительный или отрицательный
  • Поезд нашей собственной пользовательской модели с некоторыми учебными данными
  • Проверьте нашу собственную модель и используйте его на слабые данные
  • Подготовьте изображение докера с нашей моделью, чтобы мы могли развернуть и использовать его как услугу

Если вы просто хотите посмотреть на какой-нибудь код, Этот репозиторий Github есть все. Jupyter ноутбук В нем также есть много инструкций отсюда. В этой статье мы рассмотрим все шаги в мучительном деталях, поэтому мы действительно понимаем, как все это работает.

Настраивать

Самое простое, что нужно сделать это клон Этот репозиторий Github Отказ Это использует Пипнв управлять зависимостями, поэтому, если вы следуете за Прочти меня И у вас есть недавняя версия Python а также Пипнв Установлен, вы должны быть хороши, чтобы пойти.

git clone git@github.com:ruarfff/slack-sentiment.git

cd slack-sentiment

pipenv sync --dev

pipenv run notebook

Пример в основном работает в Jupyter ноутбук Отказ Конечно, не стесняйтесь использовать свою собственную настройку вместо и копировать код отсюда по мере необходимости. Если вы не хотите использовать пиронв Вы также можете установить все зависимости, перечисленные в PipFile, используя PIP напрямую.

например

pip install jupyter slack-sdk nltk numpy pandas matplotlib

Получить данные из слабых каналов

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

Я не буду слишком много внимания здесь, как Slack Documentation хорошо Но вот краткое объяснение того, как получить историю чата для канала.

Вам нужно будет получить удостоверение личности для канала и токена, чтобы сделать запрос на API Slack.

Чтобы получить идентификатор провисания:

Вероятно, есть более простой способ, но тот, который я использую, для правой кнопкой мыши имя канала в меню канала и выберите «Копировать ссылку». Ссылка будет выглядеть что-то вроде https://your-org.slack.com/Чахивы/C12345. . Идентификатор канала – последний. Это C12345. в примере URL.

Чтобы получить токен:

Настройка пользователя бота после этих инструкций https://api.slack.com/bot-users. . ОАОУТ ДОЛЖЕН ДОЛЖЕН ПОЛУЧИТЬ Каналы: История Отказ Добавьте пользователя BOT на канал, который вы хотите прочитать сообщения. Канал должен быть публичным. Я не проверил ни одно из этого с частными каналами.

Следующий раздел предполагает, что у вас есть 2 переменных среды:

Slack_sentiment_bot_token – токен бота, мы прилагаем ранее.

Slack_sentiment_channel_id – ID канала, который мы получили ранее.

Если вы не знакомы, с тем, как настроить переменные среды, не стесняйтесь просто заменить эти значения с помощью жесткозедированных значений, но, очевидно, будьте осторожны, чтобы не случайно отталкивать ваш токен до Github или что-то в этом роде:) Кстати, у меня есть пост на управляющих секретах при разработке, если вы заинтересованы.

Следующий пример Python получает все слабые сообщения для канала и печатает их:

# Get a timestamp for 7 days ago in a weird way. I need to lean more python. 
# This is only used if we want to limit how far back in time we go for fetching slack messages.
from datetime import timedelta, datetime, date
today = date.today()
week_ago = today - timedelta(days=7)
week_ago_timestamp = (datetime.combine(week_ago, datetime.min.time()) - datetime(1970, 1, 1)) / timedelta(seconds=1)

import os
# Import WebClient from Python SDK (github.com/slackapi/python-slack-sdk)
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
import json
from time import sleep

token = os.environ.get("SLACK_SENTIMENT_BOT_TOKEN")
channel_id = os.environ.get("SLACK_SENTIMENT_CHANNEL_ID")
page_size = 100

client = WebClient(token=token)

conversation_history = []
has_more = True
cursor = None

try:
    while has_more:
        has_more = False
        result = client.conversations_history(
            channel=channel_id, 
            limit=page_size,
            cursor=cursor
            # If you wanted to limit to the last 7 days uncomment the next line and put a , after cursor on the previous line
            # oldest = week_ago_timestamp
        )        
        conversation_history.extend(result["messages"])        
        has_more = result['has_more']
        if result['response_metadata'] is not None:
            print('Still fetching')
            cursor = result['response_metadata']['next_cursor']
            sleep(0.5) # Avoid being rate limited 
    print('Done!')
except SlackApiError as e:
    print("Error getting conversations: {}".format(e))

print(conversation_history)

Подготовьте данные

В технике данных огромное количество времени проводится организация и очистка данных. Slack – это всего лишь один пример широкого спектра чрезвычайно универсальных источников данных. В следующем примере мы будем осторожно опускать наши пальцы на эту работу. Давайте возьмем наши слабые сообщения и немного очистите их. Давайте также сломаем их в предложениях и словах.

Я просто сделаю нашу основную функцию очистки данных здесь, а затем пройти через его линию по линии.

import nltk
from nltk.tokenize import word_tokenize, sent_tokenize
import itertools

def get_test_data():
    all_text = [message['text'] for message in conversation_history if 'text' in message]
    tokenized_text = [sent_tokenize(text) for text in all_text]
    sentences = list(itertools.chain(*tokenized_text))
    tokenized_words = [word_tokenize(sentence) for sentence in sentences]
    words = list(itertools.chain(*tokenized_words))
    return (sentences, words)

main_sentences, main_words = get_test_data()

Построчно

all_text =. [Сообщение [«Текст»] для сообщения в Production_Hissory Если «текст» в сообщении]

Ответ от Slack API хранит текст от провисающего разговора в поле под названием текст. Мы не хотим никакой другой информации, как пользователь, который размещал его или время, когда он был опубликован. Эта линия итерации по поводу каждого ответа и извлекает текст в новый список.

tokenized_text =. [sews_tokedize (текст) для текста во all_text]

Итайте за всеми текстом и используйте предложение NLTK Tokenizer, чтобы разделить текст в предложения.

Предложения (itertools.Chain (* Tokenized_Text))

Предыдущая линия фактически дала нам список списков с Отправить_takenize Возвращает список. Эта линия сглаживает это в один список.

tokenized_words = [word_takenize (предложение) для предложения в предложениях]

Эта линия использовала слово NLTK Tokeniser, чтобы разделить каждое слабые сообщения в словах.

слова (itertools.chain (* Tokenized_words))

Word_Tokenize Возвращает список, чтобы мы получили список списков. Эта линия сглаживает это в один список.

Возврат (предложения, слова)

Здесь мы возвращаем кортеж содержащие предложения и слова. В этом случае кортеж – это просто удобный способ вернуть несколько вещей из функции, не собираясь на беспокойство создания нового типа данных для него.

main_sendents, ()

Затем мы назначаем значения из возвращенного кортежа до 2 новых переменных. Мы будем использовать эти переменные снова на протяжении всей остальной части этого примера.

Используйте предварительно обученную модель

Мы будем тренировать свою собственную модель, и мы рассмотрим, как в ближайшее время развернуть нашу собственную обученную модель. Сначала это может быть полезно посмотреть на пример использования предварительно обученной модели. Мы будем использовать NLTK SentibleIntensionanalyzer Отказ

Вы можете импортировать и настроить модель как:

from nltk import download
import matplotlib.pyplot as plt
from nltk.sentiment import SentimentIntensityAnalyzer

# The SentimentIntensityAnalyzer model needs us to pull down the vader_lexicon
download('vader_lexicon')

sia = SentimentIntensityAnalyzer()

Тогда вы можете принять одно из предложений из нашего Slack «Corpus» и пропустите его в:

sia.polarity_scores(main_sentences[0])["compound"]

Это вернет номер. Если число больше 0, то, мы говорим, что предложение имеет положительное настроение.

Вот пример, который проверяет каждое предложение и рисует круговую диаграмму, показывающую отношение положительных против отрицательных.

num_pos = 0
num_neg = 0

def is_positive(sentence: str) -> bool:
    return sia.polarity_scores(sentence)["compound"] > 0

for s in main_sentences:
    if is_positive(s):
        num_pos +=1
    else:
        num_neg += 1

labels = 'Positive', 'Negative'
sizes = [num_pos, num_neg]

fig1, ax1 = plt.subplots()
ax1.pie(sizes, labels=labels, autopct='%1.1f%%',
        shadow=True, startangle=90)
ax1.axis('equal')

plt.show()

Пример вывода:

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

Очистите данные

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

from nltk.probability import FreqDist

fdist = FreqDist(main_words)

fdist.plot(30,cumulative=False)
plt.show()

Если вы запустите это, вы, вероятно, видите много слов, таких как «я», «если», «я» и т. Д. Также, может быть, некоторые пунктуации. Они не слишком полезны для нашего анализа.

Давайте посмотрим на функцию помощника, чтобы почистить это.

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

Нам понадобится 2 функции. Один для очистки наших данных, удаление шума, таких как бессмысленные слова, пунктуация и т. Д. Затем нам нужна функция для преобразования потока слов в хесмап со словами и количеством случаев.

# Assumes you previously had from nltk import download
download('stopwords')
download('names')

from nltk.corpus import stopwords, names
from string import punctuation

# A large set of names. We lowercase here as we will lowercase our own data first too
name_words = set([n.lower() for n in names.words()])
# A set of stopwords provided by NLTK (if, as etc.)
stop_words = set(stopwords.words("english"))

def clean_words(words):
    return [w for w in [w.lower() for w in words if w.isalpha()] if w not in stop_words and w not in name_words and w not in punctuation]

Давайте разделим Clean_words И посмотри на каждый бит.

W для W в [w.lower () для w в словах, если w.isalpha ()]

Пройдите все слова в массиве слов, которые передаются как аргумент нашей функции. Проверьте, является ли слово буквенно-цифровым (нет специальных символов, которые мы не можем использовать). Затем извлеките это в новый список.

Если w не в stock_words и w не в name_words и w не в пунктуации

Это фильтрует наш список. Мы отфиляем все эти стопорные слова. Мы отфильтровали любые имена и пунктуации. Это дает нам очиститель для работы с.

Вам может быть интересно, что мы здесь делаем? Как мы можем проанализировать данные без контекста? Мы собираемся использовать довольно базовую модель. Мы будем более или менее брать сумки слов с этикетками и обучать модель с ними. Тогда мы будем кормить другие сумки слов к модели и угадают на чувствах. Это ничего не знает о фактических предложениях. Это в основном выходит из частоты слова.

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

def word_counts(words):
    counts = {}
    for word in words:
        counts[word] = counts.get(word, 0) + 1
    return counts

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

Теперь мы можем получить данные готов.

main_words = clean_words(main_words)
main_words_counts = word_counts(main_words)

Вы можете создать новый график распределения и посмотреть, лучше ли он выглядит лучше.

fdist = FreqDist(main_words)
fdist.plot(30,cumulative=False)
plt.show()

Обучение нашей собственной модели

Теперь мы будем тренировать модель, чтобы мы могли назвать себя учеными AI. Мы будем использовать что-то названное Naive Bayes классификатор Отказ NLTK предоставляет нам использовать один для нас. Наша главная работа здесь действительно состоит в том, чтобы предоставить много меченых данных для обучения модели, и библиотека делает остальные. Со временем мы становятся более знакомы с данными, мы могли бы выяснить умные способы изменения его, чтобы улучшить нашу модель. Мы будем кратко посмотрим на эту концепцию, но не входите в него в какой-либо глубине здесь.

В Образец репо Для этого есть некоторые файлы данных. Они также могут быть загружены из Настроения помечены предложения набор данных . Мы также будем использовать некоторые данные, предоставляемые NLTK.

Сначала мы загружаем файлы из репо, используя Библиотека Pandas Отказ

import pandas as pd

amazon = pd.read_csv('sentiment_labelled_sentences/amazon_cells_labelled.txt', names=['review', 'sentiment'], sep='\t') 
imdb = pd.read_csv('sentiment_labelled_sentences/imdb_labelled.txt', names=['review', 'sentiment'], sep='\t') 
yelp = pd.read_csv('sentiment_labelled_sentences/yelp_labelled.txt', names=['review', 'sentiment'], sep='\t') 

Эти файлы должны быть расположены в папке, называемой sentiment_labelled_sendents В том же каталоге, что и скрипт Python, если используется код как есть.

Сейчас мы загружаем отзыв фильма Corpus, предоставленный NLTK.

# Assumes you previously had from nltk import download
download('movie_reviews')

Следующим шагом является принять все эти данные и разделить его на 2 списка. Один список негативных отзывов. Другой положительные отзывы.

reviews = [*amazon['review'].values, *imdb['review'].values, *yelp['review'].values]
sentiment = [*amazon['sentiment'].values, *imdb['sentiment'].values, *yelp['sentiment'].values]

positive_reviews = []
negative_reviews = []

from nltk.corpus import movie_reviews

# Process the corpus data which is split into files with IDs of pos and neg
for f in movie_reviews.fileids('pos'):
    positive_reviews.append((word_counts(clean_words(movie_reviews.words(f))), 'pos'))
for f in movie_reviews.fileids('neg'):
    negative_reviews.append((word_counts(clean_words(movie_reviews.words(f))), 'neg'))

# Process the data we extracted from files into reviews and sentiment lists with matching indexes  
for i, r in enumerate(reviews):
    review_words = word_tokenize(r)
    if sentiment[i] == 1:
        positive_reviews.append((word_counts(clean_words(review_words)), 'pos'))
    else:
        negative_reviews.append((word_counts(clean_words(review_words)), 'neg'))

Теперь, когда у нас есть все эти данные, подготовленные, мы можем тренировать модель.

from nltk.classify import NaiveBayesClassifier

# We will use 80% of the data set for training and 20% for testing
split_pct = .80

pos_split = int(len(positive_reviews)*split_pct)
neg_split = int(len(positive_reviews)*split_pct)

train_set = positive_reviews[:pos_split] + negative_reviews[:neg_split]
test_set = positive_reviews[pos_split:] + negative_reviews[neg_split:]

model = NaiveBayesClassifier.train(train_set)

Здесь мы разделяем данные в наборы обучения и тестирования:

split_pct = .80

pos_split = int(len(positive_reviews)*split_pct)
neg_split = int(len(positive_reviews)*split_pct)

train_set = positive_reviews[:pos_split] + negative_reviews[:neg_split]
test_set = positive_reviews[pos_split:] + negative_reviews[neg_split:]

Трудно знать, какой оптимальный раскол, но у меня есть Прочитайте некоторые обсуждения, указывающие на 80/20, могут быть хорошим началом .

После того, как мы прочитаем наши данные, фактическое обучение модели просто вызывает одну функцию, предоставляемую NLTK:

model = NaiveBayesClassifier.train(train_set)

На данный момент мы можем начать использовать модель для классификации всех этих данных, которые мы подготовили от Slack.

model.classify(main_words_counts)

Это хорошая идея для нас использовать наши тестовые данные и сначала определить точность модели.

from nltk.classify.util import accuracy

print(100 * accuracy(model, test_set))

Когда я проверил модель, она приближалась к точке от 65% до 70%. На данный момент есть некоторая работа для улучшения модели. Я не собираюсь идти в это сейчас, кроме как предоставлять некоторые основные указатели. Возможно, в последующем статье я попаду в это. На данный момент давайте посмотрим на некоторые вещи, которые вы можете сделать, чтобы увидеть, где все нужно улучшить.

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

errors = []
for (data, label) in test_set:
    guess = model.classify(data)
    if guess != label:
        errors.append((label, guess, data))

print(errors[0])

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

Мы можем видеть, какие функции наша модель считает наиболее информативными.

model.show_most_informative_features()

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

Развертывание модели

Теперь мы можем сохранить модель. Вы можете оттолкнуть эту модель где-то и потянуть его в некоторое другое полезное местоположение. Например, как развернуть эту модель в веб-сервисе, используя Docker, см. Readme для этого проекта Отказ

Вы можете себе представить какой-то процесс, посредством которого вы тренируете модель на некотором интервале или на некотором событии и нажимаете модель в расположение, с которого он вытягивается для использования в другом приложении или услуге.

Напишите модель в файл:

import pickle

model_file = open('.models/sentiment_classifier.pickle','wb')
pickle.dump(model, model_file)
model_file.close()

Для полного примера того, как затем использовать эту модель в веб-сервисе, пожалуйста, убедитесь, что посмотрите на Пример репозитория Отказ

Быстрый пример того, как если бы пример сделать следующие действия:

model_file = open('.models/sentiment_classifier.pickle', 'rb')
model = pickle.load(model_file)
model_file.close()

print(100 * accuracy(model, test_set))
model.show_most_informative_features()
model.classify(main_words_counts)

Вот и все. Надеюсь, это было полезно. Спасибо за чтение.

использованная литература

Я написал этот пост, когда я изучал материал из следующих мест:

Обучение и развертывание модели ML как микросервис

Текстовая аналитика для начинающих с использованием NLTK

Анализ настроений: Первые шаги с библиотекой NLTK Python

Обучение модели ML для анализа настроения в Python

Оригинал: “https://dev.to/ruarfff/sentiment-analysis-on-a-slack-channel-using-python-h3a”