from langchain_community.document_loaders import PyPDFLoader
# PDF 로더 초기화
pdf_loader = PyPDFLoader('./data/transformer.pdf')
# 동기 로딩
pdf_docs = pdf_loader.load()
print(f'PDF 문서 개수: {len(pdf_docs)}')
- 출력
PDF 문서 개수: 15
print(f'첫 번째 문서: {pdf_docs[1]}')
- 출력
첫 번째 문서: page_content='1 Introduction
Recurrent neural networks, long short-term ...
long_text = pdf_docs[1].page_content
print(f'첫 번째 문서의 텍스트 길이: {len(long_text)}')
- 출력
첫 번째 문서의 텍스트 길이: 4257
가장 기본적인 분할 방식
문자 수를 기준으로 텍스트를 분할
단순하지만 문맥을 고려하지 않는다는 단점이 있음
설치: pip install langchain_text_splitters
/ poetry add langchain_text_splitters
from langchain_text_splitters import CharacterTextSplitter
# 텍스트 분할기 초기화 (기본 설정값 적용 )
text_splitter = CharacterTextSplitter(
# CharacterTextSplitter의 기본 설정값
separator = "\n\n", # 청크 구분자: 두 개의 개행문자
is_separator_regex = False, # 구분자가 정규식인지 여부
# TextSplitter의 기본 설정값
chunk_size = 4000, # 청크 길이
chunk_overlap = 200, # 청크 중첩
length_function = len, # 길이 함수 (문자열 길이)
keep_separator = False, # 구분자 유지 여부
add_start_index = False, # 시작 인덱스 추가 여부
strip_whitespace = True, # 공백 제거 여부
)
# 텍스트 분할 - split_text() 메서드 사용
texts = text_splitter.split_text(long_text)
# 분할된 텍스트 개수 출력
print(f'분할된 텍스트 개수: {len(texts)}')
# 첫 번째 분할된 텍스트 출력
print(f'첫 번째 분할된 텍스트: {texts[0]}')
- 출력
분할된 텍스트 개수: 1
첫 번째 분할된 텍스트: 1 Introduction
Recurrent neural networks, long short ...
from langchain_text_splitters import CharacterTextSplitter
# 문장 구분자를 개행문자로 설정
text_splitter = CharacterTextSplitter(
separator = "\n", # 청크 구분자: 개행문자
chunk_size = 1000, # 청크 길이
chunk_overlap = 200 # 청크 중첩
)
# split_documents() 메서드 사용 : Document 객체를 여러 개의 작은 청크 문서로 분할
chunks = text_splitter.split_documents([pdf_docs[1]]) # 첫 번째 문서만 분할
# 분할된 텍스트 개수 출력
print(f'분할된 텍스트 개수: {len(chunks)}')
# 각 청크의 텍스트 길이 출력
for i, chunk in enumerate(chunks):
print(f'청크 {i+1}의 텍스트 길이: {len(chunk.page_content)}')
# 첫 번째 청크의 텍스트 출력
print(f'첫 번째 청크의 텍스트: {chunks[0].page_content}')
- 출력
분할된 텍스트 개수: 6
청크 1의 텍스트 길이: 933
청크 2의 텍스트 길이: 995
청크 3의 텍스트 길이: 902
청크 4의 텍스트 길이: 907
청크 5의 텍스트 길이: 996
청크 6의 텍스트 길이: 385
첫 번째 청크의 텍스트: 1 Introduction
Recurrent neural networks, long short-term memory [13] and gated recurrent [7] neural networks
... has achieved
from langchain_text_splitters import RecursiveCharacterTextSplitter
# 재귀적 텍스트 분할기 초기화
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # 청크 크기
chunk_overlap=200, # 청크 중 중복되는 부분 크기
length_function=len, # 글자 수를 기준으로 분할
separators=["\n\n", "\n", " ", ""], # 구분자 - 재귀적으로 순차적으로 적용
)
# split_documents() 메서드 사용 : Document 객체를 여러 개의 작은 청크 문서로 분할
chunks = text_splitter.split_documents(pdf_docs)
print(f"생성된 텍스트 청크 수: {len(chunks)}")
print(f"각 청크의 길이: {list(len(chunk.page_content) for chunk in chunks)}")
print()
# 각 청크의 시작 부분과 끝 부분 확인 - 5개 청크만 출력
for chunk in chunks[:5]:
print(chunk.page_content[:200])
print("-" * 100)
print(chunk.page_content[-200:])
print("=" * 100)
print()
- 출력
생성된 텍스트 청크 수: 52
각 청크의 길이: [986, 910, 975, 452, 933, 995, 902, 907, 996, 385, 924, 954, 216, 923, 900, 950, 992, 913, 908, 870, 945, 975, 946, 997, 196, 980, 980, 946, 938, 999, 943, 920, 734, 958, 946, 945, 617, 983, 988, 994, 624, 944, 909, 940, 913, 983, 924, 924, 845, 812, 815, 818]
Provided proper attribution is provided, Google hereby grants permission to
reproduce the tables and figures in this paper solely for use in journalistic or
scholarly works.
Attention Is All You Need
----------------------------------------------------------------------------------------------------
the encoder and decoder through an attention
...
from langchain_text_splitters import CharacterTextSplitter
# 문장을 구분하여 분할 - 정규표현식 사용 (문장 구분자: 마침표, 느낌표, 물음표 다음에 공백이 오는 경우)
text_splitter = CharacterTextSplitter(
separator=r'(?<=[.!?])\s+', # 각 Document 객체의 page_content 속성을 문장으로 분할
chunk_size=1000,
chunk_overlap=200,
is_separator_regex=True, # 구분자가 정규식인지 여부: True
keep_separator=True, # 구분자 유지 여부: True
)
# split_documents() 메서드 사용 : Document 객체를 여러 개의 작은 청크 문서로 분할
chunks = text_splitter.split_documents(pdf_docs) # 모든 문서를 분할
print(f"생성된 텍스트 청크 수: {len(chunks)}")
print(f"각 청크의 길이: {list(len(chunk.page_content) for chunk in chunks)}")
print()
# 각 청크의 시작 부분과 끝 부분 확인 - 5개 청크만 출력
for chunk in chunks[:5]:
print(chunk.page_content[:200])
print("-" * 100)
print(chunk.page_content[-200:])
print("=" * 100)
print()
- 출력
생성된 텍스트 청크 수: 52
각 청크의 길이: [996, 945, 986, 211, 908, 819, 796, 934, 962, 241, 999, 901, 842, 963, 972, 75, 789, 960, 853, 949, 995, 900, 938, 985, 302, 925, 945, 990, 755, 999, 914, 979, 854, 980, 944, 995, 415, 885, 964, 988, 714, 944, 918, 992, 907, 983, 994, 852, 873, 812, 815, 818]
Provided proper attribution is provided, Google hereby grants permission to
reproduce the tables and figures in this paper solely for use in journalistic or
scholarly works.
Attention Is All You Need
----------------------------------------------------------------------------------------------------
r and decoder through an attention
...
# 첫번째 문서 객체의 텍스트 길이
len(pdf_docs[0].page_content)
- 출력
2859
from langchain_text_splitters import RecursiveCharacterTextSplitter
# TikToken 인코더를 사용하여 재귀적 텍스트 분할기 초기화
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
encoding_name="cl100k_base",
model_name="gpt-4o-mini",
chunk_size=300,
chunk_overlap=0,
)
# split_documents() 메서드 사용 : Document 객체를 여러 개의 작은 청크 문서로 분할
chunks = text_splitter.split_documents([pdf_docs[0]]) # 첫 번째 문서만 분할
print(f"생성된 청크 수: {len(chunks)}")
print(f"각 청크의 길이: {list(len(chunk.page_content) for chunk in chunks)}")
# 각 청크의 시작 부분과 끝 부분 확인
for chunk in chunks[:5]:
print(chunk.page_content[:50])
print("-" * 50)
print(chunk.page_content[-50:])
print("=" * 50)
print()
- 출력
생성된 청크 수: 3
각 청크의 길이: [1145, 1374, 338]
Provided proper attribution is provided, Google he
--------------------------------------------------
ng more parallelizable and requiring significantly
==================================================
less time to train. Our model achieves 28.4 BLEU o
--------------------------------------------------
countless long days designing various parts of and
==================================================
implementing tensor2tensor, replacing our earlier
--------------------------------------------------
, CA, USA.
arXiv:1706.03762v7 [cs.CL] 2 Aug 2023
==================================================
import tiktoken
# tokenizer = tiktoken.get_encoding("cl100k_base")
tokenizer = tiktoken.encoding_for_model("gpt-4o-mini")
for chunk in chunks[:5]:
# 각 청크를 토큰화
tokens = tokenizer.encode(chunk.page_content)
# 각 청크의 단어 수 확인
print(len(tokens))
# 각 청크의 토큰화 결과 확인 (첫 10개 토큰만 출력)
print(tokens[:10])
# 토큰 ID를 실제 토큰(문자열)로 변환해서 출력
token_strings = [tokenizer.decode([token]) for token in tokens[:10]]
print(token_strings)
print("=" * 50)
print()
- 출력
280
[110436, 7937, 118839, 382, 5181, 11, 5800, 43378, 36800, 14158]
['Provided', ' proper', ' attribution', ' is', ' provided', ',', ' Google', ' hereby', ' grants', ' permission']
==================================================
289
[2695, 1058, 316, 8513, 13, 5339, 2359, 136969, 220, 2029]
['less', ' time', ' to', ' train', '.', ' Our', ' model', ' achieves', ' ', '28']
==================================================
83
[105849, 289, 33686, 17, 102370, 11, 39866, 1039, 11965, 3490]
['implement', 'ing', ' tensor', '2', 'tensor', ',', ' replacing', ' our', ' earlier', ' code']
==================================================
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("BAAI/bge-m3")
tokenizer
- 출력
XLMRobertaTokenizerFast(name_or_path='BAAI/bge-m3', vocab_size=250002, model_max_length=8192, is_fast=True, padding_side='right', truncation_side='right', special_tokens={'bos_token': '<s>', 'eos_token': '</s>', 'unk_token': '<unk>', 'sep_token': '</s>', 'pad_token': '<pad>', 'cls_token': '<s>', 'mask_token': '<mask>'}, clean_up_tokenization_spaces=True, added_tokens_decoder={
0: AddedToken("<s>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
1: AddedToken("<pad>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
2: AddedToken("</s>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
3: AddedToken("<unk>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
250001: AddedToken("<mask>", rstrip=False, lstrip=True, single_word=False, normalized=False, special=True),
}
)
# 토크나이저 인코딩 - 문장을 토큰(ID)으로 변환
tokens = tokenizer.encode("안녕하세요. 반갑습니다.")
print(tokens)
- 출력
[0, 107687, 5, 20451, 54272, 16367, 5, 2]
# 토큰을 출력 (토큰 ID를 실제 토큰(문자열)로 변환)
print(tokenizer.convert_ids_to_tokens(tokens))
- 출력
['<s>', '▁안녕하세요', '.', '▁반', '갑', '습니다', '.', '</s>']
# 디코딩 - 토큰을 문자열로 변환
print(tokenizer.decode(tokens, skip_special_tokens=True))
- 출력
안녕하세요. 반갑습니다.
from langchain_text_splitters import RecursiveCharacterTextSplitter
# Huggingface 토크나이저를 사용하여 재귀적 텍스트 분할기 초기화
text_splitter = RecursiveCharacterTextSplitter.from_huggingface_tokenizer(
tokenizer=tokenizer,
chunk_size=300,
chunk_overlap=0,
)
# split_documents() 메서드 사용 : Document 객체를 여러 개의 작은 청크 문서로 분할
chunks = text_splitter.split_documents([pdf_docs[0]]) # 첫 번째 문서만 분할
print(f"생성된 청크 수: {len(chunks)}")
print(f"각 청크의 길이: {list(len(chunk.page_content) for chunk in chunks)}")
print()
for chunk in chunks[:5]:
# 각 청크를 토큰화
tokens = tokenizer.encode(chunk.page_content)
# 각 청크의 단어 수 확인
print(len(tokens))
# 각 청크의 토큰화 결과 확인 (첫 10개 토큰만 출력)
print(tokens[:10])
# 토큰 ID를 실제 토큰(문자열)로 변환해서 출력
token_strings = tokenizer.convert_ids_to_tokens(tokens[:10])
print(token_strings)
print("=" * 50)
print()
- 출력
생성된 청크 수: 4
각 청크의 길이: [676, 1064, 1077, 39]
180
[0, 123089, 71, 27798, 99, 179236, 83, 62952, 4, 1815]
['<s>', '▁Provide', 'd', '▁proper', '▁at', 'tribution', '▁is', '▁provided', ',', '▁Google']
==================================================
241
[0, 158, 137089, 289, 108, 82451, 33120, 7, 450, 26698]
['<s>', '▁con', 'volution', 'al', '▁ne', 'ural', '▁network', 's', '▁that', '▁include']
==================================================
243
[0, 6, 246232, 647, 71723, 127752, 5, 32036, 214, 12989]
['<s>', '▁', '∗', 'E', 'qual', '▁contribution', '.', '▁List', 'ing', '▁order']
==================================================
19
[0, 187, 1542, 4371, 22950, 20773, 9513, 177200, 334, 966]
['<s>', '▁ar', 'X', 'iv', ':17', '06.', '03', '762', 'v', '7']
==================================================
SemanticChunker는 텍스트를 의미 단위로 분할하는 특수한 텍스트 분할도구
단순 길이 기반이 아닌 의미 기반으로 텍스트를 청크화하는데 효과적
breakpoint_threshold_type: Text Splitting의 다양한 임계값(Threshold) 설정 방식 (통계적 기법)
설치: pip install langchain_experimental
/ poetry add langchain_experimental
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai.embeddings import OpenAIEmbeddings
# 임베딩 모델을 사용하여 SemanticChunker를 초기화
text_splitter = SemanticChunker(
embeddings=OpenAIEmbeddings(model="text-embedding-3-small"), # OpenAI 임베딩 사용
breakpoint_threshold_type="gradient", # 임계값 타입 설정 (gradient, percentile, standard_deviation, interquartile)
)
chunks = text_splitter.split_documents([pdf_docs[0]])
print(f"생성된 청크 수: {len(chunks)}")
print(f"각 청크의 길이: {list(len(chunk.page_content) for chunk in chunks)}")
print()
tokenizer = tiktoken.get_encoding("cl100k_base")
for chunk in chunks[:5]:
# 각 청크를 토큰화
tokens = tokenizer.encode(chunk.page_content)
# 각 청크의 단어 수 확인
print(len(tokens))
# 각 청크의 내용을 확인
print(chunk.page_content[:100])
print("=" * 50)
print()
- 출력
생성된 청크 수: 2
각 청크의 길이: [1741, 1117]
429
Provided proper attribution is provided, Google hereby grants permission to
reproduce the tables and
==================================================
238
∗Equal contribution. Listing order is random. Jakob proposed replacing RNNs with self-attention and
==================================================