Привет, Хабр!
Меня зовут Пётр Мананников я Data Scientist и являюсь участником профессионального сообщества NTA. Представьте ситуацию: вас назначили спикером на мероприятии, и вы даже знаете, о чем хотите рассказать аудитории. Но будет ли публикой воспринят ваш доклад так, как вы себе это представляли? Давайте посмотрим, что может пойти не так, и как это исправить.
Как часто нам приходится выступать с докладом, презентацией, проводить обучение, быть спикером на конференции? Если деятельность напрямую не связана с человеческим общением, навык грамотно доносить свою точку зрения теряется естественным образом. Друзья и близкие зачастую воспринимают нас “как есть”, исключая обратную связь для сохранения отношений. Несмотря на лояльность друзей и коллег, практика публичных выступлений важна и необходима для поддержания способности передавать свои мысли и чувства.
Данное исследование поможет разобраться с нашими вербальными привычками и подсветит зоны роста. К его созданию меня подтолкнул спикер одного из youtube каналов it-направленности. Его речь, наполненная идиомами и вводными словами, мешала восприятию основного полезного контента. Впоследствии родилась идея перевести аудиозаписи роликов в текст и выяснить, какие выражения чаще других перегружают речь. Первой задачей стала транскрибация целевой аудиодорожки, второй – анализ текста, третьей — выводы и работа над ошибками.
Детали исследования с полным кодом я выложил на GitHub.
Транскрибация
Поиск качественного инструмента для анализа аудио свелся к выбору между облачными сервисами. Решения, доступные ‘for free’ в открытых репозиториях, не соответствовали моим ожиданиям по качеству результата. Адекватное решение, способное обрабатывать длинные записи, нашлось только у тындекса. После нетривиальных настроек облака стал доступен API асинхронного распознавания. Настройки сервиса описаны здесь: cloud.yandex.ru/docs/speechkit/. Для теста была записана аудиодорожка с моими впечатлениями за текущий год. Далее в коде используется именно она.
Обработка mp3-файла
import pandas as pd
import requests
import time
import json
from collections import Counter
Подключаемся к cloud.yandex.ru , проходим все стартовые настройки сервера и рабочего пространства. Заливаем аудиозапись в выделенное хранилище облака, назначаем права на чтение сервису Speech_Kit, копируем ссылку на файл, вставляем её в POST-запрос:
# параметры запроса
key = 'ваш API - key yc’
# путь к файлу в бакете YC
filelink = 'https://storage.yandexcloud.net/bucket0011/test_audio.mp3'
body ={
"config": {
"specification": {
"languageCode": "ru-RU"
, "audioEncoding": "MP3"
}
},
"audio": {
"uri": filelink
}
}
header = {'Authorization': 'Api-Key {}'.format(key)}
POST = "https://transcribe.api.cloud.yandex.net/speech/stt/v2/longRunningRecognize"
# структура POST - запроса согласно инструкции API
req = requests.post(POST, headers=header, json=body)
#Получаем ответ от сервера, из которого забирам ID задачи на обработку аудиозаписи.
data = req.json()
id_ = data['id']
print(id_)
Запрашиваем на сервере статус операции раз в 10 секунд и ожидаем окончания процесса распознавания:
for i in range(1000):
# проверяем распознано ли аудио
GET = "https://operation.api.cloud.yandex.net/operations/{id}"
req = requests.get(GET.format(id=id_), headers=header)
req = req.json()
# если распознано — выходим из цикла
if req['done']: break
# если нет — выводим сообщение
print("файл в обработке")
# ждём 10 секунд
time.sleep(10)
#создаем временные хранилища
all_sentenses = []
sentenses_dic = {}
all_text =''
for id,chunk in enumerate(req['response']['chunks']):
# if id%2==1: #актуально только для двухканального аудио
chnk = chunk['alternatives'][0]['text'].lower()
all_text = all_text+chnk+' '
print("Часть распознанного текста: ",all_text[:55])
Переводим весь текст в нижний регистр, чистим от знаков пунктуации:
all_text = all_text.lower()
all_text = all_text.replace('.','').replace(',','').replace(' - ','')
Анализ текста
Ищем уникальные идиомы из двух слов, не подпадающие под стандартные речевые обороты:
def get_word_pairs_dic(data):
dic_words_order1 = {}
counter=0
for num, word in enumerate(data.split()):
dic_words_order1[num] = word
all_p =[]
all_p2 =[]
pair=[]
pair2=[]
p=''
p2=''
cnt2=0
for word in dic_words_order1.items():
pair.append(word[1])
# сочетания двух слов
pair2.append(word[1])
if int(word[0])%2==0:
try:
p = pair[0]+' '+pair[1]
except:
pass
all_p.append(p)
pair=[]
# сочетания двух слов со смещением на 1 слово
if int(word[0])%2==1:
try:
p2 = pair2[0]+' '+pair2[1]
except:
pass
all_p2.append(p2)
pair2=[]
# ------------ чистим списки от пробелов -----------------
try:
all_p.pop(all_p.index(''))
except:
pass
try:
all_p2.pop(all_p2.index(''))
except:
pass
# -------------- удаляем дубликаты ------------------
all_p = list(set(all_p))
all_p2 = list(set(all_p2))
return all_p ,all_p2
all_pairs1 ,all_pairs2 = get_word_pairs_dic(all_text)
Считаем количество повторений пар из 2 слов в тексте:
def pair_counter(pair_list,text):
phrase_dic = dict(zip(pair_list,[text.count(k) for k in pair_list ]))
df_phrase = pd.DataFrame.from_dict(phrase_dic, orient='index')
df_phrase = df_phrase.reset_index().rename(columns={'index': 'word_pair', 0: 'count'})
df_phrase.where(df_phrase['word_pair'].str.len()>5).sort_values(by='count', ascending = False)
return df_phrase
def combine_pairs_count_results(pair_list, pair_list2, text):
df_pairs = pair_counter(pair_list, text)
df_pairs2 = pair_counter(pair_list2, text)
combine_df = pd.concat([df_pairs,df_pairs2])
combine_df = combine_df.where(combine_df['word_pair'].str.len()>5).drop_duplicates()
.sort_values(by='count', ascending = False)
return combine_df
Объединяем результаты и оставляем уникальные:
combine_df = combine_pairs_count_results(all_pairs1, all_pairs2,all_text)
combine_df[:5]
word_pair count
105 это был 6.0
28 в общем 4.0
55 и потом 3.0
81 22 год 3.0
71 был еще 3.0
Промежуточные выводы:
most_popular_phrase = combine_df.iloc[0]['word_pair']
print(f'В тексте часто повторяется фраза "{most_popular_phrase}"')
В тексте часто повторяется фраза "это был"
Если мы ищем конкретные речевые обороты, можно упростить процесс перебора, задав их в явном виде:
bad_word_dic = {}
bad_words = ['то есть','по моему','по-моему','честно говоря','как бы','так сказать','по сути','собственно говоря','как бы'
,'таким образом','как говорится','так далее','как его','так вот','как сказать','на самом деле','в общем-то'
,'в общем','в некотором роде','в принципе','типа того','в самом деле','всё такое','в целом','то есть'
,'это самое','ну вот','ну это','так сказать','да ладно', 'можно сказать', 'как-то','не вопрос','без проблем'
,'как-то так','ничего себе','соответственно']
Создаем словарь повторений выбранных фраз в тексте:
def bad_phrase_counter(text):
dic = dict(zip(bad_words,[text.count(k) for k in bad_words ]))
df = pd.DataFrame.from_dict(dic, orient='index')
df = df.reset_index().rename(columns={'index': 'word', 0: 'count'})
return df
Считаем повторения слов-паразитов в тексте:
df = bad_phrase_counter(all_text)
df.sort_values(by='count', ascending = False)[:5]
word count
16 в общем 4
1 по моему 1
18 в принципе 0
19 типа того 0
20 в самом деле 0
Считаем количество повторений отдельных слов в тексте:
def word_counter(all_sentenses):
all_words = []
for sentance in all_sentenses:
for word in sentance.split(sep=' '):
word = word.lower()
if len(word)>=2:
all_words.append(word)
unique = dict(zip(all_words,[all_words.count(i) for i in all_words]))
df = pd.DataFrame.from_dict(unique, orient='index')
df2 =df.reset_index().rename(columns={'index': 'word', 0: 'count'})
return df2
all_sentenses = all_text.split()
df_words = word_counter(all_sentenses)
df_words.sort_values(by='count', ascending = False)[:5]
word count
71 это 14
12 на 10
33 что 10
56 не 10
22 так 10
Находим самую «вредную» фразу из всего текста, даем рекомендации:
bad_word = str(combine_df.iloc[0]['word_pair'])
bad_word2 = str(combine_df.iloc[1]['word_pair'])
print(f'Самые популярные выражения в вашей речи - "{bad_word}" и "{bad_word2}".')
word_to_post = bad_word.replace(' ','%20')
word_to_post2 = bad_word2.replace(' ','%20')
print(f'Ознакомьтесь, пожалуйста, с их синонимами: \nhttps://sinonim.org/s/
{word_to_post} \nhttps://sinonim.org/s/{word_to_post2}')
Самые популярные выражения в вашей речи - "это был" и "в общем".
Ознакомьтесь, пожалуйста, с их синонимами:
https://sinonim.org/s/это%20был
https://sinonim.org/s/в%20общем
Историческое отступление
Речь Стива Джобса
Давайте немного отвлечемся и для проверки работы скрипта возьмем текст презентации компании Apple 2007 года, где исполнительный директор анонсировал первый мобильный телефон компании с сенсорным дисплеем и функцией геолокации. Анализ текста позволит нам понять, какие фразы Стив Джобс использовал для представления своего нового творения общественности.
Для упрощения процесса загружаем речь спикеров с сайта https://singjupost.com/ по ссылке: https://singjupost.com/wp-content/uploads/2014/07/Steve-Jobs-iPhone-2007-Presentation-Full-Transcript.pdf
Оставляем только речь Стива:
with open ('apple2007_.txt','r') as file:
presentation_text = file.read()
presentation_text = presentation_text.replace('.','').replace(',','').replace(' - ','')
presentation_text = presentation_text.lower()
print(presentation_text[:211])
this is the day i’ve been looking forward to for two and a half years
every once in a while a revolutionary product comes along that changes everything and
apple has been – well first of all one’s very fortunate
Разбиваем текст на пары рядом стоящих слов:
all_p ,all_p2 = get_word_pairs_dic(presentation_text)
all_p[:5]
['completely automatically',
'hold on',
'wide screen',
'mail this',
'are calling']
Считаем количество повторений пар слов:
combine_df2 = combine_pairs_count_results(all_p, all_p2,presentation_text)
combine_df2[:10].sort_values(by='count', ascending = False)
word_pair count
1456 going to 68.0
1375 want to 57.0
2436 i want 47.0
2139 this i 44.0
2444 here a 42.0
2942 this is 40.0
3069 here and 39.0
930 in the 38.0
2777 here i 35.0
2473 you can 33.0
Из приведенного результата видно, что речь Cтива наполнена фразами про намерение, желание и простоту. Этот посыл передается аудитории: и то, как он вдохновленно рассказывает о новом устройстве, и то, как умело использует его в своих руках. Любой человек подсознательно проникнется такой подачей.
Подводя итог
Можно сказать, что речь — сильный инструмент, способный убеждать, если вы используете верные слова и честны с аудиторией.
Данное исследование позволяет легко проанализировать аудиозапись или готовый текст. Получив самые популярные фразы в речи, можно понять, насколько они ценны или, наоборот, бесполезны. Проанализировав результаты, можно улучшить качество высказываний для нашего слушателя, а значит стать приятным собеседником или успешным спикером.
YAKOROLEVAZAMKA
я для подобной задачи использовал библиотеку nltk:
import nltk
string_to_analyze = 'some long text string'
nltk_tokens = nltk.word_tokenize(string_to_analyze)
bigrams = list(nltk.bigrams(nltk_tokens))
trigrams = list(nltk.trigrams(nltk_tokens))
в ней так же есть набор стоп-слов (+слов-паразитов) для большинства языков:
nltk.download('stopwords')
from nltk.corpus import stopwords
Сам файлик со словами лежит где-то в документах и его можно редактировать по необходимости)
Peter120
Да, удобный вариант, но хотелось сделать максимально прозрачно и просто. Плюс не всегда в рамках закрытого контура можно юзать сторонние библиотеки.