Подготовка расшифровок интервью к качественному кодированию
Расшифруйте интервью и фокус-группы на своём компьютере, обезличьте текст и заведите по одному кейсу на участника, прежде чем кодировать и экспортировать данные в NVivo.
Записи интервью и фокус-групп превращаются в закодированные фрагменты за пять шагов: расшифровка с метками говорящих и временными метками, обезличивание, выбор между целыми расшифровками и фрагментами, загрузка с одним кейсом на участника, а затем кодирование и экспорт. Potato расшифровывает записи на вашем собственном компьютере с помощью WhisperX или faster-whisper, кодирует текст в QDA Mode с заметками и кейсами и экспортирует закодированный проект в NVivo, ATLAS.ti или MAXQDA как файл REFI-QDA .qdpx. Potato не обезличивает расшифровки, поэтому ваша команда заменяет имена и другие идентификаторы до загрузки текста.
Транскрипция превращает записанную речь в текст, а степень подробности расшифровки — это аналитическое решение. Oliver, Serovich и Mason (2005) называют две конвенции (источник 2). Натурализованная транскрипция передаёт каждое высказывание как можно подробнее. Денатурализованная транскрипция исправляет грамматику, убирает запинки и паузы и приводит нестандартный выговор к норме. Диаризация говорящих отмечает, кто и когда говорил, а обезличивание удаляет или заменяет сведения, по которым можно узнать участника. В Potato кейс объединяет объекты, относящиеся к одному участнику, а заметка — это аналитическая запись к расшифровке или к выделенному фрагменту. REFI-QDA — стандарт обмена, который читают и записывают NVivo, ATLAS.ti и MAXQDA.
Это руководство описывает подготовку до и после кодирования. Кодирование расшифровок интервью двумя кодировщиками с расчётом альфы описывает надёжность между двумя кодировщиками, а Как размечать расшифровки Whisper описывает исправление вывода распознавания речи по аудио. Разработка кодбука для качественного исследования описывает составление кодов, а Сравнение программ для качественного анализа данных помогает выбрать инструмент.
Конвенции транскрипции, которые стоит согласовать заранее
McLellan, MacQueen и Neidig (2003) отмечают, что ни один формат транскрипции не подходит для всех видов качественных данных, условий исследования и теоретических подходов и что плохо подготовленные расшифровки могут задержать анализ или повредить ему (источник 1). Письменная конвенция держит всех транскрибаторов и каждый проход исправлений в рамках одних правил, и ваша команда согласует конвенцию до расшифровки первой записи. Конвенция охватывает три решения:
- Детализация. Конвенция определяет, отмечаются ли слова-паразиты, паузы, наложения речи и смех и в какой нотации.
- Метки говорящих. Каждый говорящий получает роль или идентификатор участника, например
InterviewerилиP07, и никогда не получает настоящее имя. - Временные метки. Время в начале каждой реплики позволяет кодировщику найти отрывок в записи.
Локальная транскрипция Potato записывает текст, который выдаёт модель Whisper, и ни одна настройка Potato не просит модель отмечать паузы, наложения речи или слова-паразиты. Поэтому натурализованной расшифровке нужен проход исправлений по записи. Как размечать расшифровки Whisper показывает, как настроить такой проход в Potato.
Расшифровка записей на своём компьютере
Формы информированного согласия и решения этических комитетов часто ограничивают, кто может слушать записи интервью и куда может попасть аудио. potato transcripts --transcribe выполняет диаризацию говорящих с помощью sherpa-onnx, а распознавание речи с помощью WhisperX, если WhisperX установлен, и в противном случае с помощью faster-whisper. Дополнение transcribe устанавливает faster-whisper и sherpa-onnx, а WhisperX ставится отдельно (pip install whisperx). Все три инструмента работают на вашем собственном компьютере без облачных сервисов:
pip install 'potato-annotation[transcribe]'
potato transcripts ./recordings --transcribe --asr-model small.en --diarize --num-speakers 2 -o data/transcripts.jsonКоманда записывает по одному объекту на запись, с говорящим, временем начала, временем окончания и текстом каждой реплики. Модели Whisper бывают от tiny до large-v3, по умолчанию используется base. Модель покрупнее работает медленнее и расшифровывает точнее. Справка Potato отмечает, что интервью двух человек диаризуется заметно лучше, если число говорящих задано через --num-speakers. Тест на 21-секундном синтезированном интервью с двумя голосами вывел такую сводку:
Scanned 1 file(s):
int07.wav local ASR + diarization 5 turns 21.4s 2 speaker(s): SPEAKER_00, SPEAKER_01Potato 2.14.0 выводит название движка вместо local ASR, то есть ASR (whisperx) или ASR (faster-whisper).
Диаризация называет говорящих SPEAKER_00, SPEAKER_01 и так далее, без фиксированного порядка. В тесте интервьюером был SPEAKER_01, и скрипт подготовки ниже сопоставляет каждой метке роль или идентификатор участника. Исходный код самого Potato отмечает, что диаризация на основе кластеризации плохо справляется с одновременной речью. Проверьте метки говорящих в каждой фокус-группе по записи до начала кодирования.
Веса моделей скачиваются при первом использовании. Два файла моделей диаризации занимают вместе около 47 МБ, а объём загрузки Whisper растёт с размером модели. Когда веса уже на машине, транскрипция работает в изолированной от сети среде, как описано в разделе Изолированное и офлайн-развёртывание. Для офлайн-машины скопируйте каталог модели Whisper в формате CTranslate2 и передайте путь к этому каталогу в --asr-model, а POTATO_MODEL_CACHE укажите на папку с моделями диаризации.
Potato кэширует каждую исходную расшифровку рядом с записью как <name>.whisper.json, и в кэше остаются все имена. Храните файлы кэша вместе с записями или передайте --asr-cache, чтобы записывать эти файлы в столь же защищённую папку. Перед транскрипцией запускается обнаружение речевой активности, чтобы Whisper не выдумывал текст на тишине. Это обнаружение может также без предупреждения отбросить тихую речь или шёпот. Если расшифровка оказалась короче записи, запустите команду снова с --asr-no-vad. WhisperX всегда запускает обнаружение речевой активности, поэтому при установленном WhisperX добавьте --asr-engine faster-whisper, чтобы --asr-no-vad подействовал.
Обезличивание расшифровок перед кодированием
В Potato нет инструмента, который находит или заменяет в расшифровке имена, названия мест или другие идентификаторы. Поэтому обезличивание происходит до того, как Potato загрузит текст: каждый экран кодировщика, каждая заметка, каждый отчёт с цитатами и каждый экспорт .qdpx воспроизводят расшифровку в загруженном виде. Единственная очистка текста в Potato выполняется в конце проекта. python -m potato.publish --scrub-pii заменяет адреса электронной почты и номера телефонов на [EMAIL] и [PHONE] в опубликованном наборе данных, а имена эта опция не находит.
UK Data Service рекомендует заменять идентифицирующие сведения псевдонимами или описательными метками вроде «[colleague 1]», а не удалять содержание. То же руководство просит применять каждую замену единообразно во всех расшифровках и вести журнал изменений. Руководство также призывает к сдержанности, потому что чрезмерная анонимизация убирает контекстные детали и снижает интерпретационную ценность данных (источник 4). Saunders, Kitzinger и Kitzinger (2015) описывают этот компромисс на примере интервью с родственниками пациентов в вегетативном состоянии и состоянии минимального сознания (источник 3). Авторы заменили названия больниц метками вроде «Hospital 1» и сообщают, что некоторые меры защиты стоили анализу части богатства данных.
Скрипт подготовки из следующего раздела применяет таблицу замен pseudonyms.csv к каждой расшифровке, и эта таблица одновременно служит журналом анонимизации. Таблица связывает псевдонимы с настоящими именами, поэтому храните таблицу вне папки проекта, которую обслуживает Potato. Таблица ловит только те написания, которые в таблице перечислены. Поэтому человек всё равно читает каждую расшифровку после замены и оценивает косвенные идентификаторы, например место работы или редкую профессию. В тестовой расшифровке таблица заменила имя сестры участника на Ana, а название улицы на «[street name]», а пекарню, где работает сестра, оставила на суждение читателя. После загрузки найдите полнотекстовым поиском Potato каждое имя из журнала. Ни один поиск не должен вернуть ни одной расшифровки.
Целые расшифровки или фрагменты
Единица, которую видит кодировщик, — один объект в файле данных. Целая расшифровка как один объект держит перед кодировщиком ход всего интервью, и экспорт .qdpx тогда записывает по одному источнику на интервью. Проект может вместо этого разбить каждую расшифровку на фрагменты, например по одному объекту на ответ участника, и тогда экспорт записывает по одному источнику на фрагмент. Фрагменты лучше подходят в двух ситуациях:
- Надёжность между кодировщиками. Согласие считается по единицам, которые закодировали оба кодировщика, а фиксированный фрагмент даёт обоим кодировщикам одну и ту же единицу. Поэтому учебник для двух кодировщиков разбивает расшифровки на ответы.
- Фокус-группы. Potato относит каждый объект не более чем к одному кейсу, и поэтому групповая расшифровка, загруженная одним объектом, принадлежит только одному кейсу. Один объект на реплику участника позволяет репликам каждого участника составить кейс этого участника.
Преобразование расшифровок в текст с метками говорящих
Вывод potato transcripts хранит каждую расшифровку как вложенный список реплик, а экспорт .qdpx пропускает любой объект, текст которого не является простой строкой. Поэтому проект, который пойдёт в NVivo, ATLAS.ti или MAXQDA, сначала превращает каждую расшифровку в плоский текст с метками говорящих. Решения, которые применяет скрипт, хранятся в трёх небольших CSV-файлах:
speakers.csv transcript,speaker,name
int07,SPEAKER_01,Interviewer
int07,SPEAKER_00,P07
participants.csv participant_id,site,age_group
P07,rural,35-49
pseudonyms.csv original,replacement
Maria,Ana
Main Street,[street name]Скрипт объединяет идущие подряд сегменты одного говорящего в одну реплику, ставит временную метку в начале каждой реплики, применяет таблицу замен и копирует атрибуты участника в объект. С --excerpts скрипт вместо этого записывает по одному объекту на каждую реплику участника:
"""Turn `potato transcripts` output into speaker-labeled text for coding.
Usage:
python prepare_transcripts.py data/transcripts.json speakers.csv participants.csv \
pseudonyms.csv > data/interviews.jsonl
python prepare_transcripts.py ... --excerpts > data/excerpts.jsonl
"""
import csv
import json
import re
import sys
transcripts, speakers_csv, participants_csv, pseudonyms_csv = sys.argv[1:5]
excerpts = "--excerpts" in sys.argv
names = {(r["transcript"], r["speaker"]): r["name"] for r in csv.DictReader(open(speakers_csv))}
people = {r["participant_id"]: r for r in csv.DictReader(open(participants_csv))}
swaps = [(re.compile(rf"\b{re.escape(r['original'])}\b"), r["replacement"])
for r in csv.DictReader(open(pseudonyms_csv))]
def clock(seconds):
m, s = divmod(int(seconds), 60)
return f"{m // 60:02d}:{m % 60:02d}:{s:02d}"
def pseudonymize(text):
for pattern, replacement in swaps:
text = pattern.sub(replacement, text)
return text
for item in json.load(open(transcripts, encoding="utf-8")):
tid = item["id"]
turns = [] # merge consecutive segments from the same speaker into one turn
for t in item["conversation"]["turns"]:
name = names.get((tid, t.get("speaker")), t.get("speaker") or "Unknown")
if turns and turns[-1]["name"] == name:
turns[-1]["text"] += " " + t["text"]
else:
turns.append({"name": name, "start": t.get("start") or 0, "text": t["text"]})
for t in turns:
t["text"] = pseudonymize(t["text"])
if excerpts: # one item per participant turn, for focus groups
for n, t in enumerate(turns):
if t["name"] in people:
print(json.dumps({"id": f"{tid}-{n:03d}", "transcript": tid,
**people[t["name"]],
"text": f"[{clock(t['start'])}] {t['name']}: {t['text']}"}))
continue
participant = next(t["name"] for t in turns if t["name"] in people)
text = "\n\n".join(f"[{clock(t['start'])}] {t['name']}: {t['text']}" for t in turns)
print(json.dumps({"id": tid, **people[participant], "text": text}))На тестовой записи скрипт записал один объект, здесь он показан с сокращённым текстом:
{"id": "int07", "participant_id": "P07", "site": "rural", "age_group": "35-49", "text": "[00:00:00] Interviewer: Thanks for joining, can you tell me how you get to the clinic?\n\n[00:00:04] P07: I take two buses, and it takes most of the morning, my sister Ana drives me when she can, but she works at the bakery on [street name].\n\n..."}Расшифровки, которые уже существуют в виде файлов, проходят через тот же скрипт. Без --transcribe команда potato transcripts читает 21 формат расшифровок и субтитров, в том числе WhisperX JSON, SubRip, WebVTT, Praat TextGrid и ELAN EAF, полный список приведён на странице Форматы транскриптов. Проект, закодированный в другой программе QDA, переносится через potato import, как описано на странице Импорт проекта.
Кодирование в QDA Mode
Конфигурация ниже настраивает открытое кодирование целых расшифровок с одним кейсом на участника. Конфигурация проходит python -m potato.validate_cli config.yaml --strict в Potato 2.13.1:
annotation_task_name: Clinic access interviews
task_dir: .
output_annotation_dir: annotation_output/
data_files:
- data/interviews.jsonl
item_properties:
id_key: id
text_key: text
user_config:
allow_all_users: true
users: []
# Open coding by one analyst: a codebook that grows while reading, memos, cases, search
qda_mode:
enabled: true
# One case per participant, with the participant's site and age group on the case
cases:
enabled: true
key: participant_id
attributes: [site, age_group]
annotation_ui:
memos: true
search:
enabled: true
annotator_claim: true
annotation_schemes:
- annotation_type: span
name: codes
description: Highlight a passage and apply a code, or press i to create a code from the highlighted words.
codebook: true
labels: [travel to the clinic, cost of care, family support, delaying care]QDA Mode переводит кодбук в режим open. Аналитик добавляет коды по ходу чтения, а четыре метки лишь служат начальным наполнением кодбука. Если выделить отрывок и нажать i, из выделенных слов создаётся код in vivo. Если позже переименовать или объединить код, прежние кодировки останутся под старым названием, как объясняет страница Общий кодбук для качественного кодирования. Блок search включает полнотекстовый поиск, которым выполняется описанная выше проверка имён. Запустите сервер командой potato start config.yaml -p 8000.
Блок cases группирует объекты по participant_id и сохраняет site и age_group на каждом кейсе. Potato 2.13.1 строит кейсы только по полям объектов и не даёт вручную отнести объект к кейсу. Поэтому каждому объекту нужен идентификатор участника. Лог сервера тестового проекта сообщил Cases auto-detect: 1 case(s), 1 instance(s) assigned. Пока идёт кодирование, конечная точка админ-API /admin/api/code_crosstab?attribute=site считает коды по атрибуту кейса, как описано на странице Кейсы и атрибуты кейсов в качественном кодировании.
Заметки открываются в панели Notes у каждой расшифровки. Новая заметка создаётся личной. Кодировщик, который хочет, чтобы остальная команда прочитала заметку, выбирает Shared в меню видимости этой заметки, как описано на странице Аналитические заметки для качественного кодирования. QDA Mode рассчитан на одного аналитика, работающего со всем корпусом. Если два кодировщика кодируют одни и те же фрагменты, следуйте учебнику для двух кодировщиков и руководству Надёжность между кодировщиками в качественном исследовании. Если вторым кодировщиком будет языковая модель, см. Качественное кодирование с помощью LLM.
Экспорт закодированных расшифровок
Три экспорта покрывают обычные результаты работы:
python -m potato.export -c config.yaml -f qdpx -o export/
python -m potato.export -c config.yaml -f quotation_report --option include_memos=true -o export/
python -m potato.export -c config.yaml -f csv -o export/Первая команда записывает файл .qdpx, где каждая расшифровка — отдельный текстовый источник, коды идут плоским списком без иерархии, а каждая кодировка указана вместе с её кодировщиком. Экспорт не включает заметки и кейсы. Поскольку конфигурация явно задаёт cases.enabled и annotation_ui.memos, экспорт выводит предупреждение, в котором названы заметки и кейсы. Вторая команда записывает quotations.csv, по одной строке на каждый выделенный отрывок с кодом, цитатой, смещениями символов, идентификатором расшифровки и кодировщиком, а за ними по одной строке на каждую заметку. Третья команда записывает разметку каждого кодировщика в виде таблицы для R или Python. Атрибуты кейсов остаются в participants.csv, а поля id и participant_id файла данных связывают эти атрибуты с отчётом с цитатами.
Частые вопросы
Расшифровывать интервью дословно или в очищенном дословном виде?
Выбирайте степень подробности исходя из анализа. Oliver, Serovich и Mason (2005) отмечают, что натурализованная, полностью дословная транскрипция чаще всего встречается в конверсационном анализе. Исследованию того, что участники говорили на какую-то тему, обычно достаточно денатурализованной, очищенной расшифровки. Локальная транскрипция Potato записывает текст модели Whisper. Поэтому дословной конвенции нужен проход исправлений по записи.
Когда обезличивать расшифровки интервью?
Обезличивайте расшифровки до начала кодирования, потому что Potato показывает и экспортирует текст ровно в том виде, в каком текст был загружен. В Potato нет инструмента обезличивания. Перед загрузкой примените таблицу замен с единообразными псевдонимами, прочитайте каждую расшифровку в поисках косвенных идентификаторов и найдите в загруженном корпусе каждое исходное имя.
Может ли faster-whisper расшифровать интервью без загрузки аудио в облако?
Да, faster-whisper запускает Whisper на вашем собственном компьютере, а potato transcripts --transcribe с помощью faster-whisper превращает папку с записями в файл данных Potato, если WhisperX не установлен. При установленном WhisperX команда вместо этого запускает WhisperX на вашем собственном компьютере, а --asr-engine faster-whisper оставляет faster-whisper. Веса модели скачиваются при первом использовании, и после первой загрузки транскрипции сеть не нужна.
Что почитать дальше
- Кодирование расшифровок интервью двумя кодировщиками с расчётом альфы — о надёжности между двумя кодировщиками и разрешении расхождений
- Как размечать расшифровки Whisper — об исправлении машинных расшифровок по аудио
- Форматы транскриптов — обо всех форматах расшифровок, которые читает Potato, и параметрах локальной транскрипции
- QDA Mode, Общий кодбук, Аналитические заметки, Кейсы и атрибуты кейсов и Полнотекстовый поиск
- Снимки проекта — о фиксации закодированного проекта как журнала аудита
- Разработка кодбука для качественного исследования
- Надёжность между кодировщиками в качественном исследовании
- Изолированное и офлайн-развёртывание
- Качественные исследования в Potato — все возможности для качественных исследований в одном месте
Источники
- McLellan, E., MacQueen, K. M., & Neidig, J. L. (2003). Beyond the Qualitative Interview: Data Preparation and Transcription. Field Methods, 15(1), 63-84. https://doi.org/10.1177/1525822X02239573
- Oliver, D. G., Serovich, J. M., & Mason, T. L. (2005). Constraints and Opportunities with Interview Transcription: Towards Reflection in Qualitative Research. Social Forces, 84(2), 1273-1289. https://doi.org/10.1353/sof.2006.0023
- Saunders, B., Kitzinger, J., & Kitzinger, C. (2015). Anonymising interview data: challenges and compromise in practice. Qualitative Research, 15(5), 616-632. https://doi.org/10.1177/1468794114550439
- UK Data Service. Anonymisation for text data. Retrieved November 2, 2026, from https://ukdataservice.ac.uk/learning-hub/data-producer-support/preparing-data-for-sharing-and-reuse/anonymising-data-and-protecting-participants/anonymisationfortextdata/