Pilkkomisstrategiat: kiinteä koko, lauseet ja rekursiivisuus
Toteutatte ja vertaatte kiinteän kokoisia, lauserajoja noudattavia ja rekursiivisia merkkipohjaisia tekstinjakajia sekä ymmärrätte, miten palan koko ja limitys vaikuttavat haun laatuun.
Pilkkomisstrategiat: kiinteä koko, lauseet ja rekursiivisuus on ilmainen AI Engineering Academy-oppitunti CoddyKitissä. Tämä on oppitunti 2/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu AI Engineering Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. AI Engineering Academy-kurssilla on yhteensä 4 oppituntia.
Miksi osiin jakamisen laatu on tärkeää
Osiin jakaminen tarkoittaa ladattujen dokumenttien pilkkomista pienemmiksi osiksi, jotka mahtuvat LLM:n konteksti-ikkunaan ja jotka voidaan indeksoida ja hakea erikseen. Jakotapa vaikuttaa haun laatuun enemmän kuin lähes mikään muu tekijä. Jos osa jakaa vastauksen kahteen osaan, kumpikaan osa ei yksin riitä vastaamaan kysymykseen. Jos osa yhdistää kaksi toisiinsa liittymätöntä aihetta, se haetaan molempia aiheita koskevissa kyselyissä, mutta siitä ei ole hyötyä kummassakaan.
Kiinteän kokoiset osat
Kiinteän kokoinen osiin jakaminen pilkkoo tekstin täsmälleen N merkin tai N tokenin osiin lause- tai kappalerajoista riippumatta. Se on yksinkertaisin strategia ja nopea toteuttaa. Suurin haittapuoli on, että se usein katkaisee lauseet keskeltä, jolloin osat alkavat tai päättyvät kesken ajatuksen. Tämä sopii tiiviiseen ja yhdenmukaisesti muotoiltuun tekstiin, kuten tietokantavientiin, mutta tuottaa heikon hakulaadun kerronnallisessa tekstissä tai teknisessä dokumentaatiossa.
def fixed_size_chunks(text, chunk_size=500, overlap=50):
'''Split text into fixed-size character chunks with overlap'''
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
chunk = text[start:end]
chunks.append(chunk)
start += chunk_size - overlap # overlap keeps context at boundaries
return chunks
example = 'This is a long document. ' * 100
chunks = fixed_size_chunks(example, chunk_size=200, overlap=20)
print(f'Produced {len(chunks)} chunks, first: {chunks[0][:80]}...')Peittävyyden lisääminen kiinteän kokoisiin osiin
Kiinteän kokoisten osien tärkein parannus on peittävyys: jokainen osa jakaa N merkkiä edellisen osan kanssa. Näin osan rajan lähellä oleva tieto esiintyy molemmissa vierekkäisissä osissa. Kun päällekkäisyyttä on 50–100 tokenia, rajan ylittävä lause tallentuu kokonaisuudessaan ainakin toiseen osista. Suurempi peittävyys parantaa kattavuutta, mutta kasvattaa indeksin kokoa ja hakutulosten sisältämää päällekkäistä sisältöä.
# Overlap example with a sentence at the boundary
text = 'A B C D E F G H I J'
chunk_size = 6 # characters
overlap = 2
i = 0
while i < len(text):
print(repr(text[i:i+chunk_size]))
i += chunk_size - overlap
# Output shows overlapping content:
# 'A B C D'
# 'C D E F'
# 'E F G H'
# Each adjacent pair shares 2 charsLauseen rajojen kohdalta jakaminen
Lauseen rajojen kohdalta jakaminen käyttää NLP-kirjastoja, kuten nltk- tai spacy-kirjastoa, lauseiden loppujen tunnistamiseen ennen jakamista. Näin yhtäkään lausetta ei katkaista keskeltä. Lauseita kerätään, kunnes seuraavan lauseen lisääminen ylittäisi tavoitekoon, minkä jälkeen aloitetaan uusi osa. Tuloksena syntyy osia, jotka sisältävät aina kokonaisia ajatuksia ja joiden upotusten laatu on huomattavasti parempi kuin merkkeihin perustuvassa jakamisessa.
import nltk
nltk.download('punkt', quiet=True)
def sentence_chunks(text, max_tokens=300):
sentences = nltk.sent_tokenize(text)
chunks = []
current = []
current_len = 0
for sent in sentences:
sent_tokens = len(sent.split())
if current_len + sent_tokens > max_tokens and current:
chunks.append(' '.join(current))
current = []
current_len = 0
current.append(sent)
current_len += sent_tokens
if current:
chunks.append(' '.join(current))
return chunksRekursiivinen merkkipohjainen tekstin jakaminen
Rekursiivinen merkkipohjainen jakaminen on tuotannon RAG-järjestelmissä yleisimmin käytetty strategia. Se kokeilee erotinmerkkejä hierarkkisessa järjestyksessä: ensin kappaleenvaihtoja (\n\n), sitten rivinvaihtoja (\n), lauseen päättäviä pisteitä, välilyöntejä ja lopuksi yksittäisiä merkkejä. Se jakaa tekstin suurimmasta merkityksellisestä kohdasta, jolla osa pysyy tavoitekoon alapuolella, ja kunnioittaa siten dokumentin rakennetta mahdollisimman hyvin.
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # target size in characters
chunk_overlap=200, # overlap between consecutive chunks
separators=['\n\n', '\n', '. ', '! ', '? ', ' ', ''],
length_function=len
)
with open('document.txt') as f:
text = f.read()
chunks = splitter.split_text(text)
print(f'Split into {len(chunks)} chunks')
for i, chunk in enumerate(chunks[:3]):
print(f'Chunk {i}: {len(chunk)} chars — {chunk[:60]}...')Tokeneiden huomioiminen jaossa
Merkkimäärä on epätarkka arvio tokenimäärästä. 1000 merkin osa voi sisältää 200 tai 400 tokenia sanojen pituuden ja kielen mukaan. Tarkkaa hallintaa varten jakakaa teksti tokenimäärän perusteella tiktokenilla. Tämä on tärkeää, jotta osat mahtuvat mallin konteksti-ikkunaan ja kustannukset voidaan arvioida tarkasti. LangChainin TokenTextSplitter käyttää tiktokenia tokeneiden huomioimiseen perustuvassa jakamisessa.
from langchain_text_splitters import TokenTextSplitter
import tiktoken
# Split by actual token count, not characters
splitter = TokenTextSplitter(
encoding_name='cl100k_base', # GPT-4 tokenizer
chunk_size=256, # target tokens per chunk
chunk_overlap=32 # overlap in tokens
)
chunks = splitter.split_text(text)
# Verify token count
enc = tiktoken.get_encoding('cl100k_base')
for chunk in chunks[:3]:
tokens = len(enc.encode(chunk))
print(f'Chunk: {tokens} tokens')Sopivan osakoon valitseminen
Osakoko on tärkeä hyperparametri. Pienet osat (100–200 tokenia) ovat täsmällisiä — ne sisältävät tiiviisti rajattua tietoa, joka upotetaan hyvin. Ne menettävät kuitenkin ympäröivää kontekstia, joten LLM:llä ei välttämättä ole riittävästi tietoa vastauksen muodostamiseen. Suuret osat (500–1000 tokenia) tarjoavat enemmän kontekstia, mutta niiden upotukset keskiarvoistavat laajemman aiheen, mikä vaikeuttaa niiden hakemista tarkkoihin kyselyihin. Useimmat tuotantojärjestelmät käyttävät 256–512 tokenia ja testaavat koon empiirisesti omalla aineistollaan.
Kontekstin lisääminen osiin
Tehokas parannus on kontekstuaaliset osien otsakkeet: liittäkää dokumentin otsikko ja osion otsikko kunkin osan tekstin alkuun ennen upottamista. Tällöin upotus sisältää osan sisällön lisäksi myös tiedon siitä, mistä kohdasta dokumenttia se on peräisin. Osa, jonka teksti on Edut ja lomakäytäntö: Työntekijöille kertyy vuosittain 15 päivää..., löytyy lomakäytäntöä koskevissa kysymyksissä paljon tarkemmin kuin sama teksti ilman otsaketta.
def create_contextual_chunks(doc, splitter):
title = doc['metadata'].get('title', '')
section = doc['metadata'].get('section', '')
text = doc['text']
raw_chunks = splitter.split_text(text)
contextual_chunks = []
for chunk in raw_chunks:
# Prepend document context to each chunk
context_header = f'{title}\n{section}\n\n' if title else ''
contextual_chunks.append({
'text': context_header + chunk,
'metadata': doc['metadata']
})
return contextual_chunksStrategioiden vertailu omalla aineistolla
Mikään yksittäinen osiin jakamisen strategia ei ole yleisesti paras. Rakentakaa nopea arviointi: ottakaa 20 kysymystä, joiden vastaukset tunnette, suorittakaa haku kullakin jakamisstrategialla ja mitatkaa, kuinka usein oikea osa sijoittuu viiden parhaan tuloksen joukkoon (hit rate@5). Tämän valmistelu vie tunnin ja säästää viikkojen arvailulta. Huomaatte usein, että tietty dokumenttimuoto (tiivis juridinen teksti verrattuna jäsenneltyyn tekniseen dokumentaatioon) suosii selvästi yhtä strategiaa muiden sijaan.
def evaluate_chunking_strategy(questions_and_answers, retriever):
hits = 0
for qa in questions_and_answers:
results = retriever.retrieve(qa['question'], top_k=5)
result_texts = [r['text'] for r in results]
# Check if answer text appears in any retrieved chunk
if any(qa['answer'] in text for text in result_texts):
hits += 1
hit_rate = hits / len(questions_and_answers)
print(f'Hit rate@5: {hit_rate:.1%} ({hits}/{len(questions_and_answers)})')
return hit_rateErityisten dokumenttityyppien käsittely
Jotkin dokumenttityypit tarvitsevat erityistarkoitukseen suunnitellun osiin jakamisen. Kooditiedostot tulee jakaa funktio- tai luokkarajojen, ei merkkimäärän, perusteella. Markdown-tiedostot tulee jakaa otsakerajojen kohdalta niin, että kukin osa vastaa yhtä osiota. Taulukot tulee säilyttää ehjinä yhtenä osana (taulukon jakaminen keskeltä tekee sisällöstä tulkintakelvotonta). Suunnitelkaa jakamisstrategia aineistonne dokumenttityyppien perusteella sen sijaan, että käyttäisitte kaikille samaa jakajaa.
from langchain_text_splitters import MarkdownHeaderTextSplitter
# Split Markdown by header hierarchy
md_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=[
('#', 'h1'),
('##', 'h2'),
('###', 'h3')
]
)
with open('documentation.md') as f:
md_text = f.read()
# Each chunk gets metadata from its heading hierarchy
chunks = md_splitter.split_text(md_text)
for chunk in chunks[:3]:
print('Section:', chunk.metadata)
print('Text:', chunk.page_content[:80])
print()Osien alkuperäketjun seuranta
Jokainen osa tarvitsee pysyvän yksilöllisen tunnisteen, joka johdetaan lähdedokumentista ja sijainnista. Tämän tunnisteen avulla voitte päivittää muuttuneet osat ilman koko aineiston uudelleenindeksointia. Lähdepolun ja osan indeksin deterministinen tiiviste toimii hyvin. Tallentakaa metatietoihin myös osan sijainti (dokumentin X osa 3/12) — tämä auttaa kokoamaan osiot uudelleen, kun useita vierekkäisiä osia haetaan yhdessä.
import hashlib
def assign_chunk_ids(chunks, source_path):
for i, chunk in enumerate(chunks):
key = f'{source_path}::chunk_{i}'
chunk_id = hashlib.sha256(key.encode()).hexdigest()[:16]
chunk['id'] = chunk_id
chunk['metadata']['chunk_index'] = i
chunk['metadata']['total_chunks'] = len(chunks)
return chunks
# IDs are stable across re-runs if source and position match
chunks = sentence_chunks(text)
chunks = [{'text': c, 'metadata': {}} for c in chunks]
assign_chunk_ids(chunks, 'docs/policy_v3.pdf')Pikatarkistus
Testatkaa tämän oppitunnin AI Engineering -käsitteiden ymmärtämistä.
Oppitunnin yhteenveto
Tässä oppitunnissa opitte, että kiinteän kokoinen osiin jakaminen on yksinkertaista, mutta katkaisee lauseet keskeltä, lauseen rajojen kohdalta jakaminen säilyttää kokonaiset ajatukset, rekursiivinen merkkipohjainen jakaminen kunnioittaa dokumentin rakennetta ja on yleisin tuotannossa käytetty vaihtoehto sekä että edistyneisiin tekniikoihin kuuluvat tokeneiden huomioiminen jaossa, kontekstuaaliset otsakkeet, Markdown- ja kooditiedostojen erityisjakajat sekä pysyvät osatunnisteet vaiheittaisia päivityksiä varten. Seuraavaksi upotamme nämä osat ja tallennamme ne vektoritietokantaan.
Opi Python tekoälytuutorin avulla — ilmaiseksi
Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.
- Kurssit
- 30
- Oppitunnit
- 120
Usein kysytyt kysymykset
Onko oppitunti ”Pilkkomisstrategiat: kiinteä koko, lauseet ja rekursiivisuus” ilmainen?
Kyllä – oppitunnin ”Pilkkomisstrategiat: kiinteä koko, lauseet ja rekursiivisuus” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko AI Engineering Academy-kurssin, päivitä CoddyKit PROhon. AI Engineering Academy-kurssilla on yhteensä 4 oppituntia.
Mitä opin oppitunnilla ”Pilkkomisstrategiat: kiinteä koko, lauseet ja rekursiivisuus”?
Toteutatte ja vertaatte kiinteän kokoisia, lauserajoja noudattavia ja rekursiivisia merkkipohjaisia tekstinjakajia sekä ymmärrätte, miten palan koko ja limitys vaikuttavat haun laatuun. Harjoittelet AI Engineering Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.
Tarvitsenko kokemusta aloittaakseni AI Engineering Academy-opiskelun?
Aiempi kokemus ei ole tarpeen. CoddyKitin AI Engineering Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 2/4.
Kuinka kauan ”Pilkkomisstrategiat: kiinteä koko, lauseet ja rekursiivisuus”-oppitunnin suorittaminen kestää?
Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.
Voinko kirjoittaa ja suorittaa koodia tällä AI Engineering Academy-oppitunnilla?
Kyllä. Jokainen AI Engineering Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.
Kaikki tämän kurssin oppitunnit
- Dokumenttien lataus ja tekstin poiminta
- Pilkkomisstrategiat: kiinteä koko, lauseet ja rekursiivisuus
- Indeksointi: palojen upottaminen ja tallentaminen
- Kysy, hae ja generoi