Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

방카@Dev

[AI/Langchain][인프런]모두를 위한 대규모 언어 모델 LLM Part 2 - 랭체인(LangChain)으로 나만의 ChatGPT 만들기 본문

AI

[AI/Langchain][인프런]모두를 위한 대규모 언어 모델 LLM Part 2 - 랭체인(LangChain)으로 나만의 ChatGPT 만들기

방카킴 2024. 7. 13. 15:01

 

 

모두를 위한 대규모 언어 모델 LLM(Large Language Model) Part 2 - 랭체인(LangChain)으로 나만의 ChatGPT 만들

AISchool | 랭체인(LangChain) 라이브러리의 개념과 활용 방법을 학습하고, 랭체인(LangChain) 라이브러리를 이용해서 나만의 ChatGPT를 만들어보는 강의입니다.,  손쉬운 LLM 구현을 위한 랭체인(LangChain), 

www.inflearn.com

나는 유데미에서 이 강의를 들었는데 유데미에는 섹션 15부터 업데이트가 되어 있지 않다. 인프런 강의에는 업데이트되어 있으니 인프런으로 수강하면 된다.

 

 

1.  RAG(Retrieval-Augmented Generation ; 검색증강생성)이란?

- RAG는 LLM의 '사실 관계 오류 가능성'과 '맥락 이해의 한계'를 개선하는데 초점을 맞춘 방법으로, LLM에 외부 지식 베이스를 연결하여 모델의 생성 능력과 사실 관계 파악능력을 향상시키는 기술(https://modulabs.co.kr/blog/retrieval-augmented-generation/)

 

RAG(검색 증강 생성)란? - LLM 단점을 보완하는 기술

LLM(Large Language Model)의 많은 장점에도 불구하고 단점을 보완하기 위한 RAG(검색 증강 생성)이 많은 관심을 받고 있습니다. RAG의 기본 개념, 등장 배경, 원리, 적용 사례 등을 알아보겠습니다.

modulabs.co.kr

- 새로운 지식에 관한 텍스트 데이터 소스를 embedding해서 vector stores에 저장하고, 프롬프트 구성을 진행할 때 외부 데이터 소스로부터 가져온 텍스트 데이터를 함꼐 이용해서 프롬프트를 구성한 뒤 LLM 으로 부터 답변을 얻어냄.

- 전체적인 코드 구현방식은 외부 텍스트 데이터 소스를 document_loader로 불러오고, textSplitter를 이용해 부분 청크로 분리한 다음, embedding 모델로 임베딩으로 바꿔서 vectorstore 에 저장한 후, query String과 유사한 임베딩 벡터를 찾아 유사한 텍스트들을 반환하게 됨

  1. source→load : document-loader module
  2. Load → transform : 더 작은 단위로 나눔(청크분할)
  3. Transform →Embed : embeddings 모듈 사용
  4. embed → rstore : embedding한 모델 벡터스토어에 유사값 찾을 수 있게 저장
  5. store → retriever : 검색도구, 벡터 저장소에서 문서를 검색하는 도구

- retreiver는 llm과 연동하여 query string으로 질문을 주면 retriever가 벡터스토어에서 유사한 청크를 찾아주고 청크텍스트를 토대로 최종적으로 llm이 리턴된 텍스트와 질문을 조합해서 최종답변을 만들어주는 역할 수행

2. DocumentLoaders(from langchain.document_loaders)

1) WebBaseLoader : 웹페이지 스크래핑

2) CSV : CSVLoader(file_path='./mlb_teams_2012.csv', source_column="Team")

3) File Directory : DirectoryLoader('./sample_data', glob="**/*.md")

4) HTML : UnstructuredHTMLLoader("./sample_html.html"), BSHTMLLoader("./sample_html.html")

5) JSON

!pip install jq
from langchain.document_loaders import JSONLoader

from pprint import pprint

loader = JSONLoader(
    file_path='./sample_data/anscombe.json',
    jq_schema='.',  //    jq_schema='.[]', jq_schema='.[].Series'
    text_content=False)

data = loader.load()
pprint(data)

 

6) Markdown : UnstructuredMarkdownLoader(markdown_path)

7) PDF : PyPDFLoader("./llama1_paper.pdf")

8) XML

from langchain_community.document_loaders import UnstructuredXMLLoader

loader = UnstructuredXMLLoader(
    "./example_data/factbook.xml",
)
docs = loader.load()
docs[0]

 

 

3. transformer(RecursiveCharacterTextSplitter)

기본으로 추천하는 Text splitter는 RecursiveCharacterTextSplitter

  • 기본적으로 사용하는 구분 기호 ["\n\n", "\n", " ", ""]
  1. length_function : chunk의 길이를 계산하는 방법을 지정 (문자의 길이 or 토큰의 개수)
  2. chunk_size : (length_function에서 정의한 기준에 따른) chunk의 최대 크기
  3. chunk_overlap : chunk 간에 최대 중복 크기, 자연스러운 연결을 위해서 적절한 길이의 중복이 있는 것이 좋습니다.
  4. add_start_index : 원본 문서내에서 chunk의 시작위치를 metadata에 포함할지 말지를 결정
from langchain.text_splitter import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size = 100,  //청크 크기
    chunk_overlap  = 20,  //중복
    length_function = len,
    add_start_index = True,
)

texts = text_splitter.create_documents([llm_example_text])
print(texts[0])

 

- 토큰으로 구분하기

tiktoken은 OpenAI에서 개발된 BPE 알고리즘을 사용하는 tokenizer

!pip install tiktoken

text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
    chunk_size=100, chunk_overlap=0
)
texts = text_splitter.split_text(llm_example_text)

4. Embedding

- OpenAI 임베딩 모델 사용하는 법

from langchain.embeddings import OpenAIEmbeddings

embeddings_model = OpenAIEmbeddings(openai_api_key=OPENAI_KEY) # text-embedding-ada-002
embeddings = embeddings_model.embed_documents(
    [
        "안녕!",
        "빨간색 공",
        "파란색 공",
        "붉은색 공",
        "푸른색 공"
    ]
)

 

- HuggingFace 오픈소스 임베딩 모델 사용하는 법

from langchain.embeddings import HuggingFaceEmbeddings

model_name = "sentence-transformers/all-mpnet-base-v2"
#model_name = "BAAI/bge-large-en-v1.5"  # (2023.11.16 기준 공개모델 중 LeaderBoard 1위 모델)
#model_name = "jhgan/ko-sroberta-multitask" # (KorNLU 데이터셋에 학습시킨 한국어 임베딩 모델)
model_kwargs = {'device': 'cpu'}
encode_kwargs = {'normalize_embeddings': False}
hf = HuggingFaceEmbeddings(
    model_name=model_name,
    model_kwargs=model_kwargs,
    encode_kwargs=encode_kwargs
)

5. Vector Store

from langchain.vectorstores import Chroma
db = Chroma.from_documents(documents, embeddings_model) //임베딩과 임베딩 저장을 수행

6. Retriever

retriever = vectorstore.as_retriever()
retrieved_docs = retriever.invoke(
    "저출산을 극복한 나라들은 어디가 있어?"
)
print(retrieved_docs[0].page_content)

7. Memory

이전대화내용을 저장하기 위해 memory 기능 제공

prompt에 vector store, retriever result와 함께 memory에 저장된 내용으로 프롬프트 생성

 

- ConversationChain : 유저의 입력과 모델의 출력값을 history로 가지고 있는 built-in memory chain 모듈

from langchain.chains import ConversationChain

conversation = ConversationChain(llm=chat)
conversation.run("이 문장을 영어에서 한국어로 번역하세요 : I love programming.")

 

8. LangChain을 이용하여 요약 실습하기

- 핵심 이슈는 문서들을 어떻게 LLM의 Context Window에 전달할 것인가

 

1) 스터프(Stuff) : 모든 문서를 단일 프롬프트에 넣는 가장 간단한 방식

(https://python.langchain.com/v0.1/docs/modules/chains/#lcel-chains)

from langchain.chains.combine_documents.stuff import StuffDocumentsChain
from langchain.chains.llm import LLMChain
from langchain.prompts import PromptTemplate

# Define prompt
prompt_template = """아래 내용을 간략히 요약해줘.:
"{text}"
요약된 내용:"""
prompt = PromptTemplate.from_template(prompt_template)

# Define LLM chain
llm = ChatOpenAI(temperature=0, model_name="gpt-3.5-turbo-16k", openai_api_key=OPENAI_KEY)
llm_chain = LLMChain(llm=llm, prompt=prompt)

# Define StuffDocumentsChain
stuff_chain = StuffDocumentsChain(llm_chain=llm_chain, document_variable_name="text")

docs = loader.load()
print(stuff_chain.run(docs))

 

2) 맵-리듀스(Map-reduce) : 각 문서를 “맵(map)” 단계에서 개별적으로 요약한 다음, 요약본을 최종 요약으로 “리듀스(reduce)”하는 방식

 

-우선 각 문서를 개별 요약으로 매핑하는 LLMChain을 사용한 후 ReduceDocumentsChain을 사용하여 그 요약들을 하나의 전체 요약으로 결합

from langchain.chains import MapReduceDocumentsChain, ReduceDocumentsChain
from langchain.text_splitter import CharacterTextSplitter

# Map
map_template = """다음은 일련의 문서들입니다.
{docs}
이 문서 목록을 바탕으로, 주요 주제들을 파악해주세요.
도움이 되는 답변:"""

map_prompt = PromptTemplate.from_template(map_template)
map_chain = LLMChain(llm=llm, prompt=map_prompt)

 

 

- ReduceDocumentsChain은 문서 매핑 결과를 취해 단일 출력으로 줄이는 역할

- 일반적인 CombineDocumentsChain(예: StuffDocumentsChain)을 래핑하지만, 그들의 누적 크기가 token_max를 초과하는 경우 문서를 결합하기 전에 축소할 수 있는 능력을 추가

- 이 예제에서는 문서를 결합하기 위해 사용한 체인을 문서를 축소하기 위해서도 재사용할 수 있음

- 따라서, 매핑된 문서들의 누적 토큰 수가 4000 토큰을 초과하는 경우, < 4000 토큰의 배치로 문서들을 재귀적으로 StuffDocumentsChain에 전달하여 배치 요약 생성

- 그리고 그 배치 요약들의 누적 토큰 수가 4000 토큰 미만이 되면, 모든 것을 마지막으로 StuffDocumentsChain에 한 번 더 전달하여 최종 요약을 생성

# Reduce
reduce_template = """다음은 요약들의 집합입니다:
{docs}
이것들을 가져다가 최종적으로 통합된 주요 주제들의 요약으로 정리해주세요.
도움이 되는 답변:
"""
reduce_prompt = PromptTemplate.from_template(reduce_template)
# Run chain
reduce_chain = LLMChain(llm=llm, prompt=reduce_prompt)

# Takes a list of documents, combines them into a single string, and passes this to an LLMChain
combine_documents_chain = StuffDocumentsChain(
    llm_chain=reduce_chain, document_variable_name="docs"
)

# Combines and iteravely reduces the mapped documents
reduce_documents_chain = ReduceDocumentsChain(
    # This is final chain that is called.
    combine_documents_chain=combine_documents_chain,
    # If documents exceed context for `StuffDocumentsChain`
    collapse_documents_chain=combine_documents_chain,
    # The maximum number of tokens to group documents into.
    token_max=4000,
)

 

- 맵과 리듀스 체인을 하나로 결합함

# Combining documents by mapping a chain over them, then combining results
map_reduce_chain = MapReduceDocumentsChain(
    # Map chain
    llm_chain=map_chain,
    # Reduce chain
    reduce_documents_chain=reduce_documents_chain,
    # The variable name in the llm_chain to put the documents in
    document_variable_name="docs",
    # Return the results of the map steps in the output
    return_intermediate_steps=False,
)

text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
    chunk_size=1000, chunk_overlap=0
)
split_docs = text_splitter.split_documents(docs)

 

9. 엔티티 추출하기(Entity Extraction)

reference: https://python.langchain.com/v0.1/docs/use_cases/extraction/

#Schema
schema = {
    "properties": {
        "aggressiveness": {
            "type": "integer",
            "enum": [1, 2, 3, 4, 5],
            "description": "describes how aggressive the statement is, the higher the number the more aggressive",
        },
        "language": {
            "type": "string",
            "enum": ["spanish", "english", "french", "german", "italian", "korean"],
        },
        "sentiment": {
            "type": "string",
            "enum": ["positive", "negative"],
        },
        },
    "required": ["language", "sentiment", "aggressiveness"],
}
	
	#LLM
	llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo", openai_api_key=OPENAI_KEY)
	chain = create_tagging_chain(schema, llm)
	
	inp = "너에게 정말 화가 났어! 너에게 마땅한 대가를 치르게 할거야!"
	chain.run(inp)