• Feed
  • Explore
  • Ranking
/
/
    💭LLM

    [RAG] 텍스트 분할

    k
    kawaihachiwarae
    2024.12.30
    ·
    7 min read

    8536

    01. 텍스트 분할의 개념과 중요성

    • RAG에서 LOAD 이후의 과정

    • 사용 이유

      • LLM의 input token 길이 제한에 걸리지 않기 위해

      • 참고할만한 정보는 일부이기에 리소스 최적화

      • 할루시네이션 방지

    • chunk를 잘 만들면, 유사도도 잘 잡힐 것이다.

    • 불필요한 답변을 포함하면 제대로된 답변으로 연결되지 않는다.

    • 분할 전략

      • chunk overlap : 겹쳐지는 부분

    02. CharacterTextSplitter

    • 기본적으로 \n\n(문단)을 기준으로 문자 단위로 텍스트를 분할

    • 청크의 크기를 문자 수로 측정

    • 210자 언저리로 분할

    • chunk_size, chunk_overlap 지정

    from langchain_text_splitters import CharacterTextSplitter
    
    text_splitter = CharacterTextSplitter(
        separator="\\n\\n",
        chunk_size=210,
        chunk_overlap=0,
        length_function=len,
    )
    
    # 메타데이터와 문서 함께 전달 
    
    metadatas = [
        {"document": 1},
        {"document": 2},
    ]  # 문서에 대한 메타데이터 리스트를 정의
    documents = text_splitter.create_documents(
        [
            file,
            file,
        ],  # 분할할 텍스트 데이터를 리스트로 전달
        metadatas=metadatas,  # 각 문서에 해당하는 메타데이터를 전달
    )
    
    text_splitter.split_documents([file]) # 문서분할
    text_splitter.split_text(file)[0] # 텍스트 분할
    

    03. RecursiveCharacterTextSplit

    • 가장 많이 쓰이는 분할기

    • 재귀적인 분할로 효율적인 방식

      • 문단(\n\n) → 문장(\n) → 단어(” “) → 글자수 순서로 재귀적 분할

    from langchain_text_splitters import RecursiveCharacterTextSplitter
    
    text_splitter = RecursiveCharacterTextSplitter(
    
        chunk_size=250,
        chunk_overlap=50,
        length_function=len,
        is_separator_regex=False,
    )
    
    • Document 객체로 자르고 싶으면 create_documents 사용

    # text_splitter를 사용하여 file 텍스트를 문서로 분할 
    
    texts = text_splitter.create_documents([file])
    print(texts[0])  # 분할된 문서의 첫 번째 문서를 출력
    print(texts[1])  # 분할된 문서의 두 번째 문서를 출력
    
    • 결국 split_text는 반환값이 문자열, create_documents는 Documents 객체이다.

    04. TokenTextSplitter

    • LLM에 입력될때는 token 개수로 입력되기에, 토큰 개수로 자르는 방식

    • Tokenizer: 다양한 알고리즘으로 청킹 결과가 달라짐

    • 언어모델에는 토큰 제한이 있기에, 토큰 제한을 초과하지 않게 만들어준다.

    tiktoken

    from langchain_text_splitters import CharacterTextSplitter
    
    text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
        chunk_size=300,
        chunk_overlap=0,
    )
    # file 텍스트를 청크 단위로 분할, 약간 클 수 있음
    texts = text_splitter.split_text(file)
    
    • RecursiveCharacterTextSplitter또한 from_tiktoken_encoder를 사용 가능

      • 청크 크기보다 작음을 보장

    TokenTextSplitter

    from langchain_text_splitters import TokenTextSplitter
    
    text_splitter = TokenTextSplitter(
        chunk_size=300,  
        chunk_overlap=0, 
    )
    
    texts = text_splitter.split_text(file)
    print(texts[0]) 
    

    SpaCy

    • 고급 자연어처리를 위한 오픈소스 라이브러리

    import warnings
    from langchain_text_splitters import SpacyTextSplitter
    
    warnings.filterwarnings("ignore")
    
    text_splitter = SpacyTextSplitter(
        chunk_size=200, 
        chunk_overlap=50,  
    )
    

    SentenceTransformers

    • sentence-transformer 모델에 특화된 텍스트 분할기

    from langchain_text_splitters import SentenceTransformersTokenTextSplitter
    
    splitter = SentenceTransformersTokenTextSplitter(chunk_size=200, chunk_overlap=0)
    
    count_start_and_stop_tokens = 2  # 시작과 종료 토큰의 개수를 2로 설정
    
    # 텍스트의 토큰 개수에서 시작과 종료 토큰의 개수를 뺍니다.
    text_token_count = splitter.count_tokens(text=file) - count_start_and_stop_tokens
    print(text_token_count) 
    
    text_chunks = splitter.split_text(text=file)  # 텍스트를 청크로 분할
    

    NLTK

    from langchain_text_splitters import NLTKTextSplitter
    
    text_splitter = NLTKTextSplitter(
        chunk_size=200,  
        chunk_overlap=0,  
    )
    
    texts = text_splitter.split_text(file)
    print(texts[0])
    

    KoNLPy

    • 한글 처리에 유용, 형태소 분석기 포함

    import chunk
    from langchain_text_splitters import KonlpyTextSplitter
    
    text_splitter = KonlpyTextSplitter(chunk_size=200, chunk_overlap=50)
    
    texts = text_splitter.split_text(file)  # 한국어 문서를 문장 단위로 분할
    print(texts[0])
    

    HuggingFace Tokenizer

    
    from transformers import GPT2TokenizerFast
    
    hf_tokenizer = GPT2TokenizerFast.from_pretrained("gpt2")
    
    text_splitter = CharacterTextSplitter.from_huggingface_tokenizer(
        # 허깅페이스 토크나이저를 사용하여 CharacterTextSplitter 객체를 생성
        hf_tokenizer,
        chunk_size=300,
        chunk_overlap=50,
    )
    
    texts = text_splitter.split_text(file)
    

    05. SemanticChunker

    • 텍스트를 의미론적 유사성에 기반해 분할

    • chunk_size, chunk_overlap 옵션을 설정하지 않는다.

    from langchain_experimental.text_splitter import SemanticChunker
    from langchain_openai.embeddings import OpenAIEmbeddings
    
    # OpenAI 임베딩을 사용하여 의미론적 청크 분할기를 초기화
    text_splitter = SemanticChunker(OpenAIEmbeddings())
    

    Breakpoints

    • 분리할 시점을 결정해 작동

    • 기준은 어떻게 설정할까? breakpoint_threshold_type 지정

      • Percentile: 백분위수 기준

      • Standard Deviation: 표준편차 기준

      • Interquartile: 사분위수 기준

    06. Code Splitter

    • code기반 RAG를 하고자할 때 사용

    • RecursiveCharacterTextSplitter.get_separators_for_language(Language.PYTHON) 파이썬 code splitter 지정

      • ['\nclass ', '\ndef ', '\n\tdef ', '\n\n', '\n', ' ', '']

    PYTHON_CODE = """
    def hello_world():
        print("Hello, World!")
    
    hello_world()
    """
    
    python_splitter = RecursiveCharacterTextSplitter.from_language(
        language=Language.PYTHON, chunk_size=50, chunk_overlap=0
    )
    
    python_docs = python_splitter.create_documents([PYTHON_CODE])
    python_docs
    
    • markdown , HTML splitter는 전용 분할기가 따로 있음

    07. MarkdownHeaderTextSplitter

    • Header를 기준으로 분할

    • 분할하는 header를 튜플 형식으로 지정 가능

    • 메타 정보를 같이 줘서 트리구조를 알 수 있다.

    headers_to_split_on = [  
        (
            "#",
            "Header 1",
        ), 
        (
            "##",
            "Header 2",
        ),  
        (
            "###",
            "Header 3",
        ), 
    ]
    
    markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
    
    md_header_splits = markdown_splitter.split_text(markdown_document)
    
    for header in md_header_splits:
        print(f"{header.page_content}")
        print(f"{header.metadata}", end="\\n=====================\\n")
    

    08. HTMLHeaderTextSplitter

    • 구조인식 청크 생성기

    • 마크다운 분할기와 유사

    • 좀 더 재귀적

    headers_to_split_on = [
        ("h1", "Header 1"), 
        ("h2", "Header 2"),
        ("h3", "Header 3"),
    ]
    
    html_splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
    
    html_header_splits = html_splitter.split_text(html_string)
    
    for header in html_header_splits:
        print(f"{header.page_content}")
        print(f"{header.metadata}", end="\\n=====================\\n")
    
    • 다른 splitter와 파이프라인으로 연결할 수 있다. (url을 가져올 때 사용)

    • 때로는 특정 header를 누락할 수 있다.

    09. RecursiveJSONSplitter

    • DFS 알고리즘을 사용해 더 작은 JSON 청크를 생성

    • 리스트 객체는 분할하지 않는다.







    - 컬렉션 아티클