On-Ramp Tematisk Analyse v3 erstatter manuell koding og ikke-reproduserbar LLM-koding med en matematisk pipeline som garanterer 100 % identiske resultater ved gjentatte kjøringer på samme data.
Åpne surveysvar inneholder rik innsikt — men å kategorisere dem har alltid krevd vanskelige kompromisser mellom hastighet, konsistens og kostnad.
Hver komponent er best-in-class fra nyeste forskning (2026). Hele pipelinen — med unntak av navngiving — er matematisk deterministisk.
Sammensatte svar splittes i meningsbærende segmenter før embedding — slik at hvert tema fanges separat.
Problemet: Et svar som «billig og god service og anbefalt av venn» inneholder tre distinkte temaer. Å embedde hele setningen som én vektor gir en «gjennomsnittsmening» som ikke fanger enkelt-temaene.
Bransjestatus: De fleste verktøy for automatisert temaanalyse — inkludert Qualtrics Text iQ, Caplena, Medallia, Canvs AI, MonkeyLearn, Kapiche og Chattermill — behandler hele svaret som én enhet og bruker multi-label-klassifisering på responsnivå. Ingen av disse segmenterer svar på sub-setningsnivå før embedding. Det nærmeste unntaket er Thematic (getthematic.com), som splitter på setningsnivå — men stopper der. Vår pipeline går et steg videre: vi segmenterer innenfor setninger ved hjelp av norske diskursmarkører, slik at «billig og god service» splittes til to separate tema-enheter. Selv nyere akademisk forskning (arXiv 2508.19836, 2025) beskriver setningssegmentering som en fremtidig retning, ikke nåværende praksis. Vår tilnærming — sub-setningssegmentering med diskursmarkør-splitting før embedding — går lenger enn noe annet verktøy i markedet og gir prinsipielt grunnlag for multi-koding med full transparens per segment.
Løsningen: WtpSplit / Segment Any Text (Minixhofer et al., EMNLP 2024) er state-of-the-art for setningssegmentering — slår alle baselines inkludert GPT-4. Modellen sat-3l-sm (~50 MB, 3 lag) er optimert for hastighet, MIT-lisensiert, og støtter 85 språk inkludert norsk.
I tillegg: Splitting på norske diskursmarkører som signaliserer temaoverganger — koordinerende («og», «eller», «samt»), adversative («men»), additive («i tillegg», «dessuten») og kausale («fordi»). Lingvistisk forankret i Halliday & Hasan (1976) og Mann & Thompson (1988).
Resultat: «billig og god service og anbefalt av venn» → ["billig", "god service", "anbefalt av venn"] → 3 separate vektorer → 3 separate kategoritilordninger.
WtpSplit laster en 3-lags neural nettverksmodell (~50 MB) via PyTorch. Modellen predikerer segmentgrenser karakter-for-karakter — dette er ikke regelbasert splitting, men en trent transformer som har lært segmenteringsmønstre fra 85 språk. Diskursmarkør-splittingen bruker kompilerte regex-mønstre som supplement.
Konvertering av tekst til semantiske vektorer i 1024-dimensjonalt rom, med instruksjonsbasert domenetilpasning.
Modell: intfloat/multilingual-e5-large-instruct (Microsoft Research) — 560M parametere, 24 transformer-lag, trent på over 1 milliard tekstpar på 100+ språk. Rangert blant toppmodellene på MTEB for flerspråklige oppgaver.
Når et tekstsegment som «god kundeservice» sendes inn, skjer følgende prosessering:
Tokenizeren (XLM-RoBERTa WordPiece) splitter tekst i subword-tokens. Norske sammensatte ord som «kundeservice» deles i kjente sub-enheter («kunde» + «##service»). Hvert token konverteres til en heltalls-ID fra et vokabular på ~250 000 tokens. En attention mask indikerer hvilke posisjoner som er reelle tokens vs. padding.
Transformer-lagene: Token-representasjonene passerer gjennom 24 lag med multi-head self-attention. I hvert lag beregnes attention-vekter mellom alle token-par — slik at «god» kan «se» konteksten fra «kundeservice» og omvendt. Hvert lag har ~23M parametere (attention-matriser, feed-forward nettverk, layer normalization).
Mean pooling + L2-normalisering: Output fra siste transformer-lag er én vektor per token. Mean pooling beregner gjennomsnittsvektoren over alle tokens (vektet med attention mask) → én 1024-dimensjonal vektor per segment. L2-normalisering skalerer vektoren til enhetslengde — nødvendig for at cosine similarity skal fungere korrekt.
Instruksjonsformat: Hvert segment prefixes med Instruct: Classify this Norwegian survey response by theme Question: {spørsmålet}\nQuery: {segmenttekst} — dette gir modellen oppgavekontekst og forbedrer domenetilpasningen vesentlig.
Oppgradering fra v2: v2 brukte multilingual-e5-base (278M params, 768-dim, 12 lag). v3 bruker dobbelt så stor modell med instruksjonsstøtte — vesentlig bedre semantisk diskriminering for korte tekstfragmenter.
Determinisme: Modellen kjører i eval-modus uten dropout eller stokastiske operasjoner. Alle beregninger skjer på CPU med entrådskjøring. Identisk tekst → identisk 1024-dimensjonal vektor, ned til siste desimal (IEEE 754 floating point).
sentence-transformers orkestrerer hele flyten. Under panseret bruker den HuggingFace transformers for modell-arkitekturen (XLMRobertaModel), tokenizers (kompilert i Rust for hastighet) for WordPiece-tokenisering, og torch for all tensor-beregning gjennom 24 transformer-lag. Modellvektene lastes fra safetensors-format.
1024 dimensjoner reduseres til 10 med bevart manifold-struktur — optimalt for etterfølgende clustering.
Teknologi: UMAP — Uniform Manifold Approximation and Projection (McInnes, Healy & Melville, 2018). Bevarer både lokal nabostruktur og global topologi.
Hvorfor UMAP over alternativer? PCA er lineær og mister ikke-lineære relasjoner. t-SNE er ustabil for clustering i høyere dimensjoner og ikke-deterministisk uten pinning. UMAP bevarer manifold-struktur, er skalerbar, og deterministisk med faste parametere.
n_neighbors=15Balanse mellom lokal og global strukturn_components=10Måldimensjonalitetmin_dist=0.0Tett pakking for bedre clusteringmetric=cosineNaturlig for normaliserte embeddingsrandom_state=42Fast seed for reproduserbarhetn_jobs=1Entrådskjøring — eliminerer nondeterminismeumap-learn bruker pynndescent for approximate nearest neighbor-søk (bygger en RP-tree + NNDescent-graf over 1024-dim vektorene). Internalt konstruerer UMAP en vektet k-naboskap-graf som representeres som scipy.sparse matrise, optimerer et lavdimensjonalt layout via stokastisk gradientnedstigning (SGD), og bruker numba for JIT-kompilering av de tunge løkkene til maskinkode.
Automatisk oppdagelse av kategorier uten forhåndsspesifisert antall — robust mot støy og varierende klyngeformer.
Teknologi: HDBSCAN — Hierarchical Density-Based Spatial Clustering of Applications with Noise (Campello, Moulavi & Sander, 2013). Oppdager antall kategorier automatisk — trenger ikke forhåndsspesifisert K.
Algoritme: (1) Beregn gjensidig rekkeviddeavstand mellom alle punkter → (2) Bygg minimalt spenntre (MST) → (3) Konstruer klyngehierarki → (4) Ekstraher stabile klynger via excess of mass (EOM).
Støyhåndtering: Svar som ikke tilhører noen klynge (noise = -1) tilordnes nærmeste sentroid via cosine similarity. Fallback: Hvis HDBSCAN finner < 3 klynger, brukes KMeans (k=10, random_state=42) direkte på 1024-dim embeddings.
min_cluster_size=15Minimum svar per kategorimin_samples=5Konservativitetcore_dist_n_jobs=1Entrådskjøring for determinismehdbscan beregner mutual reachability distance via scipy.spatial.distance, bygger et minimalt spenntre (MST) via scipy.sparse.csgraph.minimum_spanning_tree, og ekstraher stabile klynger med excess-of-mass metoden. Fallback til sklearn.cluster.KMeans (k=10, random_state=42, n_init=10) hvis HDBSCAN finner < 3 klynger.
Nærliggende klynger grupperes i overordnede kategorier — gir to-nivå hierarki for både overblikk og detalj.
Metode: Etter HDBSCAN har identifisert underkategorier, grupperes nærliggende klynger i overordnede kategorier ved hjelp av AgglomerativeClustering med cosine-avstand og average linkage. Antall overordnede grupper bestemmes adaptivt.
Eksempel på resultat:
Sentroidene for hver HDBSCAN-klynge beregnes som gjennomsnittsvektoren av alle tilhørende embeddings. cosine_distances fra scikit-learn beregner avstandsmatrisen mellom sentroidene, og AgglomerativeClustering med average linkage bygger et dendrogram som kuttes ved optimalt antall grupper.
AI-genererte kategorinavn med inklusjonskriterier og nøkkelord — det eneste ikke-deterministiske steget i pipelinen.
For hver klynge velges opptil 10 representative svar og sendes til Claude (Anthropic) med surveyspørsmålet som kontekst. Claude genererer for hvert nivå: kort kategorinavn (2–5 ord), inklusjonskriterium/definisjon, og nøkkelord inkludert dialektvarianter og bøyningsformer.
Viktig: Dette er det eneste ikke-deterministiske steget. Navnene kan variere mellom kjøringer, men grupperingene og frekvensfordelingene er alltid identiske. Brukeren kan manuelt redigere alle kategorinavn, beskrivelser og nøkkelord.
requests sender strukturerte HTTP-forespørsler til Anthropic REST API (api.anthropic.com/v1/messages) med representative svar og surveyspørsmål som kontekst. For hver klynge velges opptil 10 svar som tilhører klyngen, og Claude genererer navn, beskrivelser og nøkkelord.
Hvert segment klassifiseres individuelt via cosine similarity — gir prinsipielt grunnlag for multi-koding med full transparens.
v3-tilnærming: (1) Hvert svar segmenteres → (2) Hvert segment embeddes separat → (3) Hvert segment klassifiseres individuelt mot klyngesentroider via cosine similarity → (4) Unike kategorier samles per svar → (5) Per-segment similarity-score bevares for transparens.
Multi-koding: Naturlig og prinsipielt — hvert segment matcher sin nærmeste kategori. Ikke lenger avhengig av vilkårlige gap-terskler.
Konfidensrapportering: For hvert svar vises hvilke segmenter som matchet hvilken kategori med hvilken score — muliggjør manuell kvalitetskontroll.
Fullstendig deterministisk: Matrisemultiplikasjon + argmax. Identisk input → identisk output.
Hele klassifiseringen koker ned til én numpy matrisemultiplikasjon (@-operatoren) etterfulgt av argmax. Fordi alle embeddings er L2-normalisert, er dot product identisk med cosine similarity — ingen ekstra normalisering trengs. Wilson 95 %-konfidensintervaller beregnes i frontend (JavaScript).
Forskning krever at resultater kan reproduseres. Verken manuell koding eller LLM-koding kan tilby dette — On-Ramp Tematisk Analyse v3 kan.
random_state=42, n_jobs=1 — eliminerer multi-threading nondeterminismecore_dist_n_jobs=1 — single-threaded MST-konstruksjonrandom_state=42, n_init=10(Manuell koding/LLM-basert koding (AI-analyser))
(On-Ramp v3)
| Dimensjon | Manuell koding | LLM-koding | On-Ramp v3 |
|---|---|---|---|
| Reproduserbarhet | 70–85 % inter-coder | ~0 % | 100 % deterministisk |
| Hastighet | Timer / dager | ~ Minutter, men upålitelig | ~2 min / 1 000 svar |
| Skalerbarhet | Lineær kostnad | ~ API-kostnader øker | 50–10 000+ svar |
| Transparens | ~ Avhenger av koder | Black box | Similarity-scorer per segment |
| Multi-koding | ~ Manuell vurdering | ~ Ad hoc LLM-vurdering | Prinsipielt segment-basert |
| Induktiv kategorisering | Mulig men arbeidskrevende | ~ Inkonsistent | Automatisk bottom-up |
| Hierarkisk struktur | Sjelden implementert | ~ Mulig men ustabilt | Automatisk to-nivå |
| Konfidensintervaller | Nei | Nei | Wilson 95 % KI |
| Norskoptimert | Morsmål | ~ Variabelt | Bokmål, nynorsk, dialekter |
| Publiserbar | ~ Med forbehold | Ikke reproduserbar | Fullstendig audit trail |
Spørsmål: «Hvorfor valgte du din nåværende strømleverandør?»
Hvert segment konverteres til en 1024-dimensjonal vektor, reduseres til 10 dimensjoner via UMAP, og tilordnes en klynge via HDBSCAN. Systemet oppdager f.eks. 8 underkategorier automatisk fra 1 000 svar.
→ Svaret kodes til 3 kategorier med full transparens: eksakt similarity-score per segment.
| Hovedkategori | Underkategori | Frekvens | 95 % KI |
|---|---|---|---|
| Pris og økonomi | Spotpris / variabel pris | [15.8 – 20.8] | |
| Fastprisavtale | [6.8 – 10.4] | ||
| Generelt billig | [4.5 – 7.5] | ||
| Kundeservice | God service ved kontakt | [12.5 – 17.1] | |
| Enkel kundeportal / app | [6.8 – 10.4] | ||
| Anbefaling | Anbefalt av noen | [17.2 – 22.4] | |
| Vane / bekvemmelighet | Alltid hatt dem | [11.4 – 15.8] | |
| Miljø / grønt | Fornybar energi | [9.3 – 13.5] |
Pipelinen orkestrerer et økosystem av state-of-the-art Python-biblioteker. Her er den fulle oversikten over hva som faktisk kjører.
Grunnmotoren for all neural nettverksberegning. Kjører de 560M parametrene i embedding-modellen gjennom 24 transformer-lag med multi-head self-attention. Deterministisk via eval-modus og CPU-kjøring uten stokastiske operasjoner.
Laster og kjører XLM-RoBERTa-arkitekturen (E5-large-instruct). Håndterer tokenizer-initialisering, modellvekt-lasting fra safetensors, attention mask-konstruksjon, og forward pass gjennom alle 24 lag.
Rust-kompilert WordPiece-tokenizer med ~250 000 tokens vokabular. Splitter tekst til subword-tokens («kundeservice» → «kunde» + «##service»), konverterer til token IDs, og genererer attention masks — alt i mikrosekunder.
Orkestreringsbiblioteket som binder alt sammen: laster modellen, håndterer batching, kjører instruksjonsbasert encoding, utfører mean pooling over token-representasjoner, og L2-normaliserer til enhetsvektorer.
Neural setningssegmentering (EMNLP 2024). Kjører en 3-lags modell (~50 MB) som predikerer segmentgrenser karakter-for-karakter. Suppleres med kompilerte regex-mønstre for norske diskursmarkører.
Dimensjonsreduksjon fra 1024 → 10 via manifold-læring. Bygger k-NN graf med PyNNDescent (approximate nearest neighbors), konstruerer fuzzy simplicial set, og optimerer layout via SGD. JIT-kompilert med Numba.
Density-basert hierarkisk clustering. Beregner mutual reachability distance, bygger minimalt spenntre via SciPy sparse grafalgoritmer, og ekstraherer stabile klynger med excess-of-mass metoden.
Brukes i 3 steg: AgglomerativeClustering for hierarkisk gruppering, KMeans som fallback-clustering, og cosine_similarity / cosine_distances for all avstandsberegning mellom vektorer.
Grunnfjellet for all numerisk beregning. NumPy for matrisemultiplikasjon (klassifisering), argmax, vektornormalisering. SciPy for sparse matriser (UMAP-grafer), MST-konstruksjon, avstandsmetrikker, og Wilson-konfidensintervaller.
Claude API for det eneste ikke-deterministiske steget: navngiving av klynger og generering av analyserapporter. HTTP-forespørsler via requests til Anthropic REST API med strukturerte prompts.
Backend: Python 3.10, asynkron jobbhåndtering. Frontend: PHP 8.1 + vanilla JS. Cache: filbasert SHA256-nøklet for reproduserbarhet. PHP spawner Python-prosess, frontend poller for resultat.
Hver komponent i pipelinen er forankret i peer-reviewed eller bredt sitert forskning.
Tilgjengelig som SaaS-lisens, on-premise installasjon, eller API-integrasjon. Vi tilpasser løsningen til ditt behov.