Speech to Text - сопоставить метку докладчика с соответствующей расшифровкой в ответе JSON

1

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

Он показывает транскрипцию, высказывание каждого слова с метками времени и ярлыками динамиков для каждого динамика speaker 0 и speaker 2 в разговоре.

   {
    "results": [
        {
            "alternatives": [
                {
                    "timestamps": [
                        [
                            "the",
                            6.18,
                            6.63
                        ],
                        [
                            "weather",
                            6.63,
                            6.95
                        ],
                        [
                            "is",
                            6.95,
                            7.53
                        ],
                        [
                            "sunny",
                            7.73,
                            8.11
                        ],
                        [
                            "it's",
                            8.21,
                            8.5
                        ],
                        [
                            "time",
                            8.5,
                            8.66
                        ],
                        [
                            "to",
                            8.66,
                            8.81
                        ],
                        [
                            "sip",
                            8.81,
                            8.99
                        ],
                        [
                            "in",
                            8.99,
                            9.02
                        ],
                        [
                            "some",
                            9.02,
                            9.25
                        ],
                        [
                            "cold",
                            9.25,
                            9.32
                        ],
                        [
                            "beer",
                            9.32,
                            9.68
                        ]
                    ],
                    "confidence": 0.812,
                    "transcript": "the weather is sunny it time to sip in some cold beer "
                }
            ],
            "final": "True"
        },
        {
            "alternatives": [
                {
                    "timestamps": [
                        [
                            "sure",
                            10.52,
                            10.88
                        ],
                        [
                            "that",
                            10.92,
                            11.19
                        ],
                        [
                            "sounds",
                            11.68,
                            11.82
                        ],
                        [
                            "like",
                            11.82,
                            12.11
                        ],
                        [
                            "a",
                            12.32,
                            12.96
                        ],
                        [
                            "plan",
                            12.99,
                            13.8
                        ]
                    ],
                    "confidence": 0.829,
                    "transcript": "sure that sounds like a plan"
                }
            ],
            "final": "True"
        }
    ],
    "result_index":0,
    "speaker_labels": [
        {
            "from": 6.18,
            "to": 6.63,
            "speaker": 0,
            "confidence": 0.475,
            "final": "False"
        },
        {
            "from": 6.63,
            "to": 6.95,
            "speaker": 0,
            "confidence": 0.475,
            "final": "False"
        },
        {
            "from": 6.95,
            "to": 7.53,
            "speaker": 0,
            "confidence": 0.475,
            "final": "False"
        },
        {
            "from": 7.73,
            "to": 8.11,
            "speaker": 0,
            "confidence": 0.499,
            "final": "False"
        },
        {
            "from": 8.21,
            "to": 8.5,
            "speaker": 0,
            "confidence": 0.472,
            "final": "False"
        },
        {
            "from": 8.5,
            "to": 8.66,
            "speaker": 0,
            "confidence": 0.472,
            "final": "False"
        },
        {
            "from": 8.66,
            "to": 8.81,
            "speaker": 0,
            "confidence": 0.472,
            "final": "False"
        },
        {
            "from": 8.81,
            "to": 8.99,
            "speaker": 0,
            "confidence": 0.472,
            "final": "False"
        },
        {
            "from": 8.99,
            "to": 9.02,
            "speaker": 0,
            "confidence": 0.472,
            "final": "False"
        },
        {
            "from": 9.02,
            "to": 9.25,
            "speaker": 0,
            "confidence": 0.472,
            "final": "False"
        },
        {
            "from": 9.25,
            "to": 9.32,
            "speaker": 0,
            "confidence": 0.472,
            "final": "False"
        },
        {
            "from": 9.32,
            "to": 9.68,
            "speaker": 0,
            "confidence": 0.472,
            "final": "False"
        },
        {
            "from": 10.52,
            "to": 10.88,
            "speaker": 2,
            "confidence": 0.441,
            "final": "False"
        },
        {
            "from": 10.92,
            "to": 11.19,
            "speaker": 2,
            "confidence": 0.364,
            "final": "False"
        },
        {
            "from": 11.68,
            "to": 11.82,
            "speaker": 2,
            "confidence": 0.372,
            "final": "False"
        },
        {
            "from": 11.82,
            "to": 12.11,
            "speaker": 2,
            "confidence": 0.372,
            "final": "False"
        },
        {
            "from": 12.32,
            "to": 12.96,
            "speaker": 2,
            "confidence": 0.383,
            "final": "False"
        },
        {
            "from": 12.99,
            "to": 13.8,
            "speaker": 2,
            "confidence": 0.428,
            "final": "False"
        }
    ]
}

Простите проблемы с отступом (если есть), но JSON действителен, и я пытаюсь сопоставить каждый транскрипт с соответствующей меткой динамика.

Я хочу что-то вроде ниже. JSON выше - около 20 000 строк, а его кошмар - это ярлык динамика, основанный на отметках времени и словах, и вместе со transcript.

[
    {
        "transcript": "the weather is sunny it time to sip in some cold beer ",
        "speaker" : 0
    },
    {
        "transcript": "sure that sounds like a plan",
        "speaker" : 2
    }

]  

То, что я пробовал до сих пор: данные JSON хранятся в файле с именем example.json. Я смог поместить каждое слово и его соответствующую метку времени и ярлыка динамика в список кортежей (см. Вывод ниже):

import json
# with open('C:\\Users\\%USERPROFILE%\\Desktop\\example.json', 'r') as f:
    # data = json.load(f)

l1 = []
l2 = []
l3 = []

for i in data['results']:
    for j in i['alternatives'][0]['timestamps']:
        l1.append(j)

for m in data['speaker_labels']:
     l2.append(m)

for q in l1:
    for n in l2:
        if q[1]==n['from']:
            l3.append((q[0],n['speaker'], q[1], q[2]))
print(l3)

Это дает результат:

 [('the', 0, 6.18, 6.63),
 ('weather', 0, 6.63, 6.95),
 ('is', 0, 6.95, 7.53),
 ('sunny', 0, 7.73, 8.11),
 ("it's", 0, 8.21, 8.5),
 ('time', 0, 8.5, 8.66),
 ('to', 0, 8.66, 8.81),
 ('sip', 0, 8.81, 8.99),
 ('in', 0, 8.99, 9.02),
 ('some', 0, 9.02, 9.25),
 ('cold', 0, 9.25, 9.32),
 ('beer', 0, 9.32, 9.68),
 ('sure', 2, 10.52, 10.88),
 ('that', 2, 10.92, 11.19),
 ('sounds', 2, 11.68, 11.82),
 ('like', 2, 11.82, 12.11),
 ('a', 2, 12.32, 12.96),
 ('plan', 2, 12.99, 13.8)]

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

Мне также удалось получить транскрипты в списке, но теперь, как извлечь ярлык динамиков для каждой расшифровки из приведенного выше списка. Звук speaker 0 и speaker 2 для каждого слова, к сожалению, я бы хотел, чтобы они были для каждой transcript.

for i in data['results']:
    l4.append(i['alternatives'][0]['transcript'])

Это дает результат:

["the weather is sunny it time to sip in some cold beer ",'sure that sounds like a plan']

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

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

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

Теги:
python-3.x
speech-to-text

3 ответа

1
Лучший ответ

используя панды, вот как я справился с этим сейчас.

предполагая, что данные хранятся в словаре под названием data

import pandas as pd

labels = pd.DataFrame.from_records(data['speaker_labels'])

transcript_tstamps = pd.DataFrame.from_records(
    [t for r in data['results'] 
       for a in r['alternatives'] 
       for t in a['timestamps']], 
    columns=['word', 'from', 'to']
)
# this list comprehension more-efficiently de-nests the dictionary into
# records that can be used to create a DataFrame

df = labels.merge(transcript_tstamps)
# produces a dataframe of speakers to words based on timestamps from & to
# since I knew I wanted to merge on the from & to columns, 
# I named the columns thus when I created the transcript_tstamps data frame
# like this:
    confidence  final   from  speaker     to     word
0        0.475  False   6.18        0   6.63      the
1        0.475  False   6.63        0   6.95  weather
2        0.475  False   6.95        0   7.53       is
3        0.499  False   7.73        0   8.11    sunny
4        0.472  False   8.21        0   8.50     it's
5        0.472  False   8.50        0   8.66     time
6        0.472  False   8.66        0   8.81       to
7        0.472  False   8.81        0   8.99      sip
8        0.472  False   8.99        0   9.02       in
9        0.472  False   9.02        0   9.25     some
10       0.472  False   9.25        0   9.32     cold
11       0.472  False   9.32        0   9.68     beer
12       0.441  False  10.52        2  10.88     sure
13       0.364  False  10.92        2  11.19     that
14       0.372  False  11.68        2  11.82   sounds
15       0.372  False  11.82        2  12.11     like
16       0.383  False  12.32        2  12.96        a
17       0.428  False  12.99        2  13.80     plan

после объединения данных динамика и слова необходимо группировать последовательные слова одним и тем же динамиком вместе для вывода текущего динамика. например, если массив динамиков выглядел как [2,2,2,2,0,0,0,2,2,2,0,0,0,0], нам нужно было бы сгруппировать первые четыре 2 вместе, затем следующие три 0, затем три 2 а затем остальные 0.

сортируйте данные по ['from', 'to'] а затем настройте фиктивную переменную для этого типа current_speaker следующим образом:

df = df.sort_values(['from', 'to'])
df['current_speaker'] = (df.speaker.shift() != df.speaker).cumsum()

отсюда, группа current_speaker, агрегирует слова в предложение & конвертировать в json. Там немного дополнительного переименования для исправления выходных json-ключей

transcripts = df.groupby('current_speaker').agg({
   'word': lambda x: ' '.join(x),
   'speaker': min
}).rename(columns={'word': 'transcript'})
transcripts[['speaker', 'transcript']].to_json(orient='records')
# produces the following output (indentation added by me for legibility):
'[{"speaker":0,
  "transcript":"the weather is sunny it\ time to sip in some cold beer"},    
 {"speaker":2,
  "transcript":"sure that sounds like a plan"}]'

Чтобы добавить дополнительные данные, когда начинается/заканчивается стенограмма, вы можете добавить min/max из/в группу

transcripts = df.groupby('current_speaker').agg({
   'word': lambda x: ' '.join(x),
   'speaker': min,
   'from': min,
   'to': max
}).rename(columns={'word': 'transcript'})

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

  • 0
    Это выглядит как многообещающее решение. Я проверю это и сообщу вам результат.
  • 0
    @ user8212173, смотрите обновленный ответ, который также обрабатывает случай, когда динамики чередуются.
Показать ещё 7 комментариев
-1

Это то, что я пытался использовать JS
Посмотрите, работает ли это для вас аналогичным образом с помощью python

var resultTimestampLen = 0;

arrLen = JSON.parse(sTot_resuts.results.length);
for(var i = 0; i<arrLen; i++){

    speakerLablefrom = sTot_resuts.speaker_labels[resultTimestampLen].from;

    speakerLabelto = sTot_resuts.speaker_labels[resultTimestampLen].to;

    speakerId = sTot_resuts.speaker_labels[resultTimestampLen].speaker;


    var findSpeaker = new Array();
    findSpeaker = sTot_resuts.results[i].alternatives[0].timestamps[0];

    var timeStampFrom = findSpeaker[1];

    var timeStampto = findSpeaker[2];


      if(timeStampFrom === speakerLablefrom && timeStampto === speakerLabelto){
        console.log('Speaker '+sTot_resuts.speaker_labels[resultTimestampLen].speaker + ' ' + sTot_resuts.results[i].alternatives[0].transcript);
        var resultsTimestamp = new Array();
        resultsTimestamp = sTot_resuts.results[i].alternatives[0].timestamps.length;

        resultTimestampLen = resultsTimestamp+resultTimestampLen;
      }else{
        console.log('resultTimestampLen '+resultTimestampLen + 'speakerLablefrom '+speakerLablefrom + 'speakerLabelto '+speakerLabelto + 'timeStampFrom '+timeStampFrom + 'timeStampto '+timeStampto);
      }
}
-1

Я сделал это, бросив слова в диктовку, основанный на их метке, и они сопоставляли их с их динамиками:

times = {}
for r in data['results']:
    for word in r['alternatives'][0]['timestamps']:
        times[(word[1], word[2])] = word[0]

transcripts = {}
for r in data['speaker_labels']:
    speaker = r['speaker']
    if speaker in transcripts:
        transcripts[speaker].append(times[(r['from'], r['to'])])
    else:
        transcripts[speaker] = [times[(r['from'], r['to'])]]

print([{'speaker': k, 'transcript': ' '.join(transcripts[k])} for k in transcripts])

Он работает на примере, предоставленном 1,000,000 раз в ~ 12,34 секунды, поэтому, надеюсь, он будет достаточно быстрым для того, что вы хотите.

  • 0
    И FWIW, решение для панд заняло около 30 секунд, прежде чем я начал писать это, пока ............. будет обновляться. Тем не менее, вполне возможно (почти определенно), что ответ панд просто имеет много накладных расходов, что не позволяет ему работать 1 000 000 раз. Не могли бы вы связать нас с шаблоном JSON большего размера для тестирования?
  • 0
    @ user8212173, больший набор данных позволит решателям создавать менее ошибочные решения, а также проверять эффективность. это будет оценено.
Показать ещё 7 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню