Tekoälyavustettua podcast-litterointia — Part Deux

DEMO II: Aikakoodit litterointiin mahdollisesti tyhmällä tavalla.

Erkki Mervaala
6 min readFeb 17, 2024

Tämä on suoraa jatkoa edellisestä kokeilusta AssemblyAI:n kanssa, joten suosittelen lukemaan sen ensin ennen kuin pulahdat tähän kirjoitukseen.

Terve! Kiva kun pulahdit tähän kirjoitukseen! Tällä kertaa jatketaan alaotsikon osoittamalla teemalla elikkäs eri AA:n transcribe-ominaisuuksien yhdistelemisellä tavoitteena saada lopputulokseksi aikakoodattu koko podcastin kattava litterointi, jossa jokaisen puhujan lausuman (utterance) kohdalla lukee puhujan nimi sekä kyseisen lausuman aikakoodi.

Aloitetaanpa!

Tällä kertaa otetaan käsittelyyn Kristallipallo-podcastin toinen jakso, jonka keskiössä on Valtio. Keskustelijoina ovat lisäkseni ORSI-hankkeen johtaja Liisa Häikiö sekä Dialogiakatemian Kai Alhanen. Ensimmäiset vaiheet ovat samat kuin edeltävässä eli ensin AA ruksuttaa litteroi jakson. Tällä kertaa olin tosin yrittänyt määrittää ennalta jaksolle kustomoidun sanaston nimineen päivineen (custom vocabulary), mutta se ei näyttänyt auttavat edellisessä osassa nähtyjen nimivirheiden kanssa. Olen yhä Mervala, Kai on Kaiso ja Aalahanen, Häikiö on Häikio. Kenties Custom Spelling on parempi lähestymistapa. Noh, kokeilen sitä myöhemmin.

Asiaan!

Haluan siis luoda litteroinnista sellaisen tekstitiedoston, että jokainen uusi puhujan (Speaker) lausuma alkaa aikakoodilla, jota seuraa puhujan nimi ja tämä kyseinen lausuma. Aikakoodit saadaan nypittyä parsettamalla (jäsentämällä) tekstitystiedosto transcribe.export_subtitles_srt() ja sovittamalla siitä löytyvien lausumien aloittavien sanojen aikakoodit yhteen utterances-lausumien kanssa.

Koska AA:n algoritmi ei tiedä, keitä puhujat ovat (saati osaa kirjoittaa niitä oikein), aloitin pikku funktiolla, joka kartoittaa lausujoiden määrän (Speaker A, Speaker B jne.), kertoo käyttäjälle, kuinka monta niitä on, ja sitten esittää kysymyksen, josko käyttäjä haluaa nimetä ne. Esimerkkilauseena kunkin kysyjän kohdalla näytetään pari ensimmäistä lausumaa. Laitoin varulta 1 tai 2, koska olen käyttänyt tätä esitellessä esimerkkinä myös lyhyempiä äänitiedostoja, joissa saattaa olla vain yksi lausuma per henkilö. Alla näkyy, kun nimeän ensimmäiset kaksi puhujaa.


def get_speakers(transcript):
speakers = set()
for utterance in transcript.utterances:
speakers.add(utterance.speaker)
return sorted(speakers) # Return a sorted list of speakers

def rename_speakers_in_transcript(transcript, speaker_names):
for utterance in transcript.utterances:
if utterance.speaker in speaker_names:
utterance.speaker = speaker_names[utterance.speaker]
def ask_speaker_names(speakers, transcript):

speaker_names = {}

# Sort the speakers to ensure they're in alphabetical order (A, B, C...)

sorted_speakers = sorted(speakers)

for speaker in sorted_speakers:

# Find the first one or two utterances for this speaker.

utterances = [utterance for utterance in transcript.utterances if utterance.speaker == speaker]

print(f"Utterances by {speaker}:")

for i, utterance in enumerate(utterances[:2]):

print(f" {i+1}. {utterance.text} \n")

# Ask for a new name for the speaker

print(f"Name {speaker} (up to 50 characters): ", end='')

name = input()[:50] # Limit input to 50 characters

speaker_names[speaker] = name

return speaker_names



# This is where we get the speakers
speakers = get_speakers(transcript)

# This is where we ask for their names...
print(f"There are {len(speakers)} speakers in the transcript. Would you like to name them? Answer yes or no: \n")

response = input().strip().lower()

# This is where we ask for an answer. If answer is not yes/y, names remain as Speaker A, B, C...
if response in ['y', 'yes']:

speaker_names = ask_speaker_names(speakers, transcript)
rename_speakers_in_transcript(transcript, speaker_names)​
Kuvankaappaus eri puhujien nimeämisestä Jupyter Notebookista.

Tämän jälkeisistä funktioista ensimmäinen etsii lausumalle (utterance) aikakoodin .srt-tekstityssegmenttien joukosta ja sitten muovaa aikakoodin muotoon HH:MM:SS jättäen pois oletuksena mukana olevat millisekunnit. Seuraava funktio jäsentää (parse) .srt-sisällön osioihin. Lopuksi AA:n litteroinnista tuotetaan .srt-tekstitystiedosto string-muotoon, jota sitten päästään jäsentämään.

def find_utterance_start_time(srt_segments, utterance_text):
for segment in srt_segments:
if segment['text'] in utterance_text:
# Remove milliseconds from the start time
start_time = segment['start_time'].split(',')[0] # .srt default format HH:MM:SS,xxx
return start_time
return None

# A function to parse the .srt content
def parse_srt_content(srt_content):
segments = []
entries = srt_content.split('\n\n')
for entry in entries:
lines = entry.split('\n')
if len(lines) >= 3:
start_time = lines[1].split(' --> ')[0].split(',')[0] # Remove milliseconds here as well
text = ' '.join(lines[2:])
segments.append({'start_time': start_time, 'text': text})
return segments

# Export and parse the .srt content
srt_content = transcript.export_subtitles_srt() # This is how you get the .srt content as a string
srt_segments = parse_srt_content(srt_content)

Ja sitten lopuksi viimeinen funktio, jossa tuotetaan lausumain listasta (transcript.utterances) ja juuri luodusta srt_segments-jäsentelystä niiden elementtejä (lausumat ja puhujat + aikakoodit*) yhdistelemällä tarpeeksi hyvä litterointi, joka sisältää aikakoodin, lausujan ja lausuman nätissä järjestyksessä rivinvaihtoineen päivineen.

# Function to append time codes and write to a text file
def write_utterances_to_file(transcript, srt_segments, file_path='updated_transcript.txt'):
with open(file_path, 'w', encoding='utf-8') as file:
for utterance in transcript.utterances:
start_time = find_utterance_start_time(srt_segments, utterance.text)
if start_time:
file.write(f"{start_time} {utterance.speaker}: {utterance.text}\n \n")
else:
file.write(f"{utterance.speaker}: {utterance.text}\n \n")

Lopuksi sitten ajetaan write_utterances_to_file(transcript, srt_segments), jonka lopputuloksena saadaan .txt-muotoinen litterointitiedost. Ensimmäiset pari minuuttia litterointia näkee alapuolella. Alla olevaa ei ole editoitu vaan se on suoraan .txt-tiedostosta, joten nimierheet ym. ovat yhäti läsnä. Virheitä voi bongata kuuntelemalla samaan tahtiin kyseistä jaksoa vaikkapa Soundcloudissa tahi Spotifyssa.

00:00:07 Erkki Mervaala: Hei ja tervetuloa Kristallipallolle ensimmäiseen todelliseen jaksoon äänimuotoiselle matkalle kohti ekohyvinvointivaltiota. Minä olen Erkki Mervala ja tänään keskustelemme niinkin suuresta teemasta kuin valtio. Ja aluksi esitellään, ketä me olemme, eli siis Orsi-hankkeen toinen edustaja tässä jaksossa on hankkeen johtaja Liisa Häikio.

Liisa Häikiö: Moi!

00:00:36 Erkki Mervaala: Ja vieraana meillä on tutkija, filosofi-dialogian akatemian johtaja Kai Aalahanen. Tervetuloa.

Kai Alhanen: Moi moi, kiitoksia.

00:00:44 Erkki Mervaala: Ja tänään tosiaan aiheena on valtio. Viime jaksossa keskustelimme enemmän ekohyvinvointivaltiosta, mutta tänään käsittelemme valtiota vähän erilaisten linssien läpi. Aloitetaan tällaisesta ehkä itsestäänselvältä tuntuvastakin väitteestä. Valtio olemme me. Olemmeko?

Liisa Häikiö: Mitä sä Kai ajattelet tästä kysymyksestä?

00:01:08 Kai Alhanen: Mä ajattelen, että se on semmoista kysymys, joka tietyllä tavalla voidaan esittää vaan demokraattisissa yhteiskunnissa, koska demokratia on nyt kuitenkin näistä historiantuntemista valtionmuodoista ainoa, jonka lähtökohtana on se, että kaikki yritetään, pyritään tekemään kansalaisten ja koko kansan ehdoilla. Ja sen takia se on Valtion me voi olla, sillä voi olla mielekässä sisältö vaan tämän tyyppisessä valtion muodossa. Mutta myös demokratioiden sisällä on niin, että edes kaikkein parhaiten toimivimmissa demokratioissa se ei ole itsestäänselvyys, että valtio on me, koska kaikissa poliittisissa yhteisöissä on myös rakenteita ja toimintatapoja, joilla jotkut tietyt ryhmät, joskus jopa yksilöt, pyrkii edistämään omaa etuansa enemmän kuin mitään yhteistä etua. Siinä mielessä minusta tämä kysymys pitää esittää uudestaan aina myöskin demokratioiden sisällä, että millä tavoin tämä valtio, nämä rakenteet, joita meillä on nyt, nämä toimintatavat, missä määrin tämä on nyt jotain semmoista, mikä aidosti palvelee tätä kansaa, josta tämä valtio koostuu. Tietysti se kansakaan ei ole yhtenäinen eikä kansallakaan ole yhteistä tahtoa siinä mielessä, että olisi aina selvää, että mihin yhdessä halutaan suunnata ja millaista yhteiselämää halutaan rakentaa, niin tämäkin on sellainen asia, jota tietenkin koko ajan joudutaan neuvottelemaan ja ratkaisemaan eri tavoin. Eli tietyllä tavalla valtion kuuluisi olla me, mutta se on aina asia, joka pitää uudestaan tarkistaa.

00:02:56 Liisa Häikiö: Demokraattisissa valtioissa on paljon eroa siinä, kuinka paljon ihmiset kokee, että valtio on me tai valtio on mun ystävä tai meidän ystävä tai koostuu siitä, mitä me yhdessä teemme ja millä tavalla me haluamme asioita viedä eteenpäin. Mä veikkaan, että on aika paljonkin demokraattisia valtioita, joissa itse asiassa tämän tapasta perinnettä ei ole. Valtio näyttäytyy aina jollain tavalla vieraana tai vihollisena. Ajattelin, että myös suomalaisessa yhteiskunnassa, jos mietitään valtioa, niin joiltain osin voidaan ajatella, että valtio on ystävä. Hyvinvointivaltiot voidaan kutsua naisten parhaaksi. Jos ei nyt kaveriksi, niin kuitenkin vapauttajaksi tai ylipäätään voidaan ajatella, että valtio tässä koronatilanteessakin luo turvaa ihmisille ja tekee päätöksiä, mitä yksityisesti ei voida tehdä. mutta joka tapauksessa suomalaisessakin valtiossa on kontroli- ja valvontapiirre. Se myös kuuluu valtion luonteeseen. Voisiko olla valtio, joka puhtaasti olisi vain yhteisö?

Tällaista tällä kertaa. Ensi kerralla sitten koetan ehkä työstää enemmän noita väärintulkittuja nimiä ja ehkä muita paranteluja, sillä eihän tämä vielä ole läheskään täydellinen ja aukoton. Lisäksi koodin ja AA:n kikkareiden hyödyntämisenkin näkökulmasta tämä oli myöskin tämmöinen ensimmäinen oma purkkaratkaisuviritelmä, joka oli mitä ilmeisimmin hyvinkin paljon vähemmän fiksu kuin fiksuin mahdollinen ratkaisu itselleni asettamaani ongelmaan. Kommentoi ihmeessä alle se oikea, parempi ratkaisu!

Erkki out.

Osa 3 löytyy täältä.

*Tuossa for-luupissa else: nappaa siis ne kohdat, joihin jostain syystä .srt-jäsennyksestä ei suoraan löydy aikakoodia. Tämä voi johtua siitä, että lausuma on ollut esimerkiksi lyhyytensä vuoksi liian lähellä edellistä kohtaa .srt-aikakoodistossa. Katselen, josko jaksan kehittää tähänkin parempaa ratkaisua.

--

--

Erkki Mervaala
Erkki Mervaala

Written by Erkki Mervaala

Researcher at the Finnish Environment Institute. Doing PhD at Helsinki University. Researcher member @ Puistokatu 4. Writer of many things but here mainly on AI

No responses yet