상세 컨텐츠

본문 제목

기본_LLM_Chain_( 1 )

경험 리뷰/한국경제 with Tossbank

by shok11 2024. 9. 21. 00:50

본문

728x90
반응형

 

 

 

 

LLM Chain의 구성 요소 ( Prompt + LLM )

Prompt : 사용자 또는 시스템에서 제공하는 입력

사용자 또는 시스템에서 제공하는 입력 값이다. 프롬프트는 특정 작업을 수행하도록 지시하거나 정보를 요청하는 형태로 제공된다. LLM은 이 프롬프트를 기반으로 텍스트를 생성하거나 분석한다.

프롬프트를 효과적으로 사용하는 것이 생성형 인공지능의 능력을 최대치로 발휘시키는 열쇠이다.

프롬프트 요청 방식

  • 질문형 프롬프트: 특정 질문에 대한 답변을 요청하는 입력 ("지구의 자전 주기는?")
  • 지시형 프롬프트: 특정 작업을 요청하는 입력 ("이 문서를 요약해 줘")
  • 생성형 프롬프트: 창의적인 콘텐츠 생성을 요청하는 입력 ("SF 소설의 첫 장면을 작성해 줘")
  • 분석형 프롬프트: 텍스트나 데이터의 분석을 요구하는 입력 ("이 문장의 감정을 분석해 줘")

,,, 등등 다양한 방식이 존재한다. 모델에게 역할을 부여하는 역할기반 프롬프트 ( Role-based-Prompt )도 자주 보인다.

LLM: GPT, Gemini 등의 대규모 언어 모델 (언어 이해 및 생성)

LLM(대규모 언어 모델)은 대량의 텍스트 데이터를 분석하여 다음 단어 또는 문장을 예측한다.

수억 개 이상의 파라미터를 가지고 있으며, 방대한 텍스트 데이터로 사전 학습을 진행한다.

이러한 대규모 모델은 텍스트 생성, 번역, 감정 분석, 질문 응답, 텍스트 요약 등 다양한 자연어 처리 작업을 수행한다.

LLM의 학습 과정

- 사전 학습 ( Pre-training ) : 대규모 텍스트 데이터로 모델을 사전 학습. 언어의 구조와 문법, 단어 간의 관계를 파악한다.

- 파인 튜닝 ( Fine-tuning ) : 사전 학습된 모델을 특정 작업에 맞게 가중치를 조정하는 단계.

LLM 체인의 작동 방식

"프롬프트 생성 => LLM 처리 => 응답 반환"의 세 단계로 나눌 수 있다. 각 단계는 LLM이 자연어를 이해하고 사용자에게 적절한 응답을 생성하는 데 중요한 역할을 한다. 생성된 응답은 애플리케이션으로 반환되며, 직접접인 답변, 텍스트, 요약된 정보, CSV,, 등 다양한 형태를 취할 수 있다.

LLM Chain

사용자가 문자열을 입력하면 프롬프트 템플릿이 이를 기반으로 프롬프트를 자동으로 완성하고, LLM을 호출해 텍스트 출력을 반환하는 방식이다.

도시 이름을 입력하면 그 도시의 유명한 관광지를 리턴하는 LLM Chain을 구현한 예시.

 

Chain을 이용한, LLM 워크 플로우

LLM 기반 애플리케이션을 개발할 때, 한 번의 LLM 호출로 결과를 낼 수도 있지만, 보다 정교한 LLM 애플리케이션의 경우, LLM의 출력을 다음 LLM의 입력으로 넣어서 답변을 낼 수도 있고, 입력 프롬프트에 알맞은 프롬프트나 LLM을 선택하게 할 수 있다.

Python Coding을 해주는 LLM에서 API 파이썬 코드를 생성한 후에, 코드에 맞는 Unit Test 코드를 생성하는 LLM을 호출.
공부를 도와주는 챗봇이 질문의 종류에 따라서 적절한 LLM을 호출하는 구조.

 

 

환경 구성

라이브러리 설치

 

!pip install -q langchain langchain-openai tiktoken

 

 

Langchain : 다양한 LLM을 연동하고 체인을 구성할 수 있는 프레임워크.

OpenAI : GPT와 같은 OpenAI의 언어 모델과 상호작용하기 위한 라이브러리.

Tiktoken : 토큰화 및 텍스트 처리에 사용되는 라이브러리.

OpenAI 인증키 설정

https://openai.com/index/openai-api/

 

API Key( GPT )는 여기서 발급받으면 된다.

import os
os.environ["OPENAI_API_KEY"] = "your-openai-api-key-here"

 

 

LLM Chain

Prompt + LLM

기본 체인은 사용자가 입력한 텍스트를 LLM으로 전달하고, LLM이 이를 처리하여 응답을 반환하는 방식으로 동작한다.

다양한 자연어 처리 작업을 수행할 수 있다.

from langchain_openai import ChatOpenAI

# model
llm = ChatOpenAI(model="gpt-3.5-turbo-0125")

# chain 실행
result = llm.invoke("지구의 자전 주기는?")

invoke 메서드는 지정된 프롬프트를 LLM에 전달하여 즉시 응답을 반환하는 함수이다.

호출할 때마다 LLM을 실행해 결과를 생성하며, LLM의 특성에 따라 동일한 프롬프트라도 응답이 조금씩 달라질 수 있다.

result.content # 텍스트만 추출

API 호출 시 결과를 result에 저장해서 따로 불러오지 않으면, 호출할 때마다 과금이 발생한다. LLM은 확률 기반으로 응답을 생성하기 때문에, 실행할 때마다 문장의 구조가 미세하게 달라진다.

 

from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_template("You are an expert in astronomy. Answer the question. : {input}")
prompt
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-3.5-turbo-0125")

# chain 연결 (LCEL)
chain = prompt | llm

# chain 호출
chain.invoke({"input": "지구의 자전 주기는?"})

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

# prompt + model + output parser
prompt = ChatPromptTemplate.from_template("You are an expert in astronomy. Answer the question. : {input}") # "너는 천문학의 전문가야"라는 프름프트 템플릿 / 시스템 메시지 (2)
llm = ChatOpenAI(model="gpt-3.5-turbo-0125")
output_parser = StrOutputParser() # 텍스트만 가져오겠다. (3)

# LCEL chaining
chain = prompt | llm | output_parser # 출력 순서

# chain 호출
chain.invoke({"input": "지구의 자전 주기는?"}) # 사용자의 인풋을 받겠다. (1)

 

멀티 체인 ( Multi Chain )

여러 개의 체인을 연결하거나 복합적으로 작용하는 것은 멀티 체인 ( Multi Chain ) 구조를 통해서 이루어진다.

예제에서는 2개의 체인 (chain1, chain2)를 정의하고 연결하는 과정이다.

prompt1 = ChatPromptTemplate.from_template("translates {korean_word} to English.") # 한국어 단어를 입력 받을 수 있는 단어를 문자열 포멧팅을 사용해서 지정 => 영어로 번역하라는 프름포트 작성
prompt2 = ChatPromptTemplate.from_template(
    "explain {english_word} using oxford dictionary to me in Korean."
)

llm = ChatOpenAI(model="gpt-3.5-turbo-0125")

chain1 = prompt1 | llm | StrOutputParser()

chain1.invoke({"korean_word":"맛집"})

chain2 = (
    {"english_word": chain1}
    | prompt2
    | llm
    | StrOutputParser() # 문자열 (strings)을 출력해주는 Parser
)

chain2.invoke({"korean_word":"맛집"}) # invoke 실행 => "korean_word"에 맛집 전달 => chain1 맛집 영어 출력 "english_word"에 전달 => prompt2의 "english_word"에 전달 => LLM에 전달 => llm => StrOutputParser() 문자열 출력

첫 번째 체인(chain 1)은 한국어 단어를 영어로 번역한다. 두 번째 체인(chain 2)은 번역된 단어를 english_word라는 변수에 저장해서 입력으로 제공하고, 영어 단어의 뜻을 한국어로 설명하는 작업을 수행한다.

 

 

Prompt

prompt Template

  • PromptTemplate + LLMs (단일 문장 입력 -> 단일 문장 출력)
  • Python의 문자열 포맷팅 구문을 사용해서 문자열 프롬포터를 위한 템플릿을 생성.
  • 내용 : 지시사항, 몇 가지 예시, 특정 맥락 및 질문 등
from langchain_core.prompts import PromptTemplate

# 'name'과 'age'라는 두 개의 변수를 사용하는 프롬프트 템플릿을 정의
template_text = "안녕하세요, 제 이름은 {name}이고, 나이는 {age}살입니다." # 문자열 포멧팅을 사용해서 두 개의 입력 변수 설정

# PromptTemplate 인스턴스를 생성
prompt_template = PromptTemplate.from_template(template_text) # 생성된 문자열을 PromptTemplate에 전달

# 템플릿에 값을 채워서 프롬프트를 완성
filled_prompt = prompt_template.format(name="이승훈", age=26)

filled_prompt

# 문자열 템플릿 결합 (PromptTemplate + PromptTemplate + 문자열) / str이기 때문에 +가 사용 가능하다.
combined_prompt = (
              prompt_template
              + PromptTemplate.from_template("\n\n그만하고 싶어요 집에 갈래요.")
              + "\n\n{language}로 번역해주세요."
)

combined_prompt

combined_prompt.format(name="이승훈", age=26, language="중국어") # 각각의 매게변수에 값을 입력

from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser

llm = ChatOpenAI(model="gpt-3.5-turbo-0125")
chain = combined_prompt | llm | StrOutputParser()
chain.invoke({"age":26, "language":"중국어", "name":"이승훈"})

프롬프트는 사용자와 언어 모델 간의 대화에서 질문이나 요청의 형태로 제시되는 입력문이다. 만약 잘 작동하는 프롬프트를 발견했다면, 이를 재사용하거나 기본 템플릿으로 활용하는 것이 좋다.

 

ChatPromptTemplate

  • chat Prompt Templates + ChatModels (여러 메시지 입력 => 단일 메시지 출력)
  • 채팅 메시지를 원소로 갖는 리스트 형태
  • 구성 : 각 채팅 메시지는 역할(role)과 내용(content)이 짝을 이루는 형태
    • 예시 : OpenAI는 AI Assistant, Human, System 등의 역활 (role)로 구성

 

예시에서는

  • System message : 시스템 기능을 설명.
  • Human message : 사용자의 질문을 나타낸다.
  • AI message : AI 모델의 응답을 제공

만 사용하지만

  • Function message : 특정 함수의 호출 결과
  • Tool message : 도구 호출의 결과

다양한 유형이 존재한다.

# 2-튜플 형태의 메시지 목록으로 프롬프트 생성 (type, content)

from langchain_core.prompts import ChatPromptTemplate

chat_prompt = ChatPromptTemplate.from_messages([
    ("system", "이 시스템은 천문학 질문에 답변할 수 있습니다."),
    ("user", "{user_input}"),
])

messages = chat_prompt.format_messages(user_input="태양계에서 가장 큰 행성은 무엇인가요?")
messages

chain = chat_prompt | llm | StrOutputParser()

chain.invoke({"user_input": "태양계에서 가장 큰 행성은 무엇인가요?"})

 

Message 구성 방법

# MessagePromptTemplate 활용

from langchain_core.prompts import SystemMessagePromptTemplate,  HumanMessagePromptTemplate # 역활에 따라서 Prompt Template을 사용할 수 있다.

chat_prompt = ChatPromptTemplate.from_messages(
    [
        SystemMessagePromptTemplate.from_template("이 시스템은 천문학 질문에 답변할 수 있습니다."),
        HumanMessagePromptTemplate.from_template("{user_input}"),
    ]
)


messages = chat_prompt.format_messages(user_input="태양계에서 가장 큰 행성은 무엇인가요?")
messages

chain = chat_prompt | llm | StrOutputParser() # 템플릿은 재사용성, 사용의 유연성 때문에 사용하는 것이다. chain을 구성해서 simple하게 구현 하였다.

chain.invoke({"user_input": "태양계에서 가장 큰 행성은 무엇인가요?"})

템플릿은 재사용성, 사용의 유연성을 위해서 사용한다는 점을 항상 기억하며 공부하자.

 

 

Lang Chain 모델 유형

랭체인 문서에 따르면 LLM과 Chat Model 클래스는 각각 다른 형태의 입력과 출력을 다루는 언어 모델을 나타낸다.

각기 다른 특성과 용도를 가지고 있어서 요구사항에 맞게 선택하여 사용해야 한다.

  • LLM : 단일 요청에 대한 복잡한 출력 / 텍스트 문자열 -> 텍스트 문자열 / 광범위한 언어 이해 및 텍스트 생성에 유리
  • Chat Model : 사용자와의 상호작용 / 리스트 형태의 메시지를 입력으로 받는다. -> 하나의 메시지를 출력한다. / 대화형 상황에 최적화 대화의 맥락을 유지함에 중점 (대화 내용을 기억한다.)

LLM

from langchain_openai import OpenAI

llm = OpenAI()

llm.invoke("제주도의 대표적인 관광지 3군데를 추천해주세요.")

 

Chat Model

from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

chat = ChatOpenAI()

chat_prompt = ChatPromptTemplate.from_messages([ # 메시지의 리스트를 구성
    ("system", "이 시스템은 여행 전문가입니다."), # 시스템 메시지
    ("user", "{user_input}"), # 유저 메시지로 구별 해서 튜플 형태로 조합하고 리스트 형태로 배열을 만들어서 => from_messages에 전달한다
])

chain = chat_prompt | chat
chain.invoke({"user_input": "안녕하세요? 제주도의 대표적인 관광지 3군데를 추천해주세요."})

Chat Model은 대화형 상황에 최적화된 모델이기 때문에 친근한 출력값을 확인할 수 있다.

 

 

LLM Chain의 LLM 모델 파라미터 설정

  • Temperature : 생성되는 텍스트의 다양성을 조정 / 값이 작으면 일관된 출력 값이 크면 다양한 출력
  • Max Tokens : 생성항 최대 토큰 수
  • Top P : 생성 과정에서 상위 P% 토큰만을 고려하는 방식 / 다양성 조절
  • Frequency Penalty : 값이 클수록 이미 등장한 단어나 구절이 다시 등장할 확률을 감소시킴 0~1
  • Presence Penalty : 값이 클수록 새로운 단어의 사용이 장려 0~1
  • Stop Sequences : 특정 단어나 구절이 등장할 경우에 생성을 멈추도록 설정

https://sundg0162.tistory.com/19

 

01. LLM 모델이란?

개요저번 글인 생성형 AI 소개에 이어 써보겠습니다.LLM 모델이 기억하는 원리나 각종 파라미터 등에 대해 적어볼겁니다. 상당히 흥미로운 주제이지만 논문 쓰는 것 같고 막 그럽니다.    LLM이

sundg0162.tistory.com

쉽게 풀어주셨네요.

 

모델에 직접 파라미터를 전달 ( 모델 생성 시점 )

from langchain_openai import ChatOpenAI

# 모델 파라미터 설정
params = {
    "temperature": 0.7,         # 생성된 텍스트의 다양성 조정
    "max_tokens": 100,          # 생성할 최대 토큰 수
    "frequency_penalty": 0.5,   # 이미 등장한 단어의 재등장 확률
    "presence_penalty": 0.5,    # 새로운 단어의 도입을 장려
    "stop": ["\n"]              # 정지 시퀀스 설정
}

# 모델 인스턴스를 생성할 때 설정
model = ChatOpenAI(model="gpt-3.5-turbo-0125", **params)

# 모델 호출
question = "태양계에서 가장 큰 행성은 무엇인가요?"
response = model.invoke(input=question)

# 전체 응답 출력
print(response)

모델에 직접 파라미터를 전달 ( 모델 호출 시점 )

# 모델 파라미터 설정
params = {
    "temperature": 0.7,         # 생성된 텍스트의 다양성 조정
    "max_tokens": 10,          # 생성할 최대 토큰 수
}

# 모델 인스턴스를 호출할 때 전달
response = model.invoke(input=question, **params)

# 문자열 출력
print(response.content)

 

모델에 추가적인 파라미터를 전달 ( bind 메서드 사용 )

- bind 메서드를 사용하여 체인에 새로운 파라미터를 추가하여 연결 가능

- 다양한 상황에 맞게 모델의 동작을 제어

from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_messages([
    ("system", "이 시스템은 천문학 질문에 답변할 수 있습니다."),
    ("user", "{user_input}"),
])

model = ChatOpenAI(model="gpt-3.5-turbo-0125", max_tokens=100)

messages = prompt.format_messages(user_input="태양계에서 가장 큰 행성은 무엇인가요?")

before_answer = model.invoke(messages)

# # binding 이전 출력
print(before_answer)

# 모델 호출 시 추가적인 인수를 전달하기 위해 bind 메서드 사용 (응답의 최대 길이를 10 토큰으로 제한)
chain = prompt | model.bind(max_tokens=10)

after_answer = chain.invoke({"user_input": "태양계에서 가장 큰 행성은 무엇인가요?"})

# binding 이후 출력
print(after_answer)

 

 

 


 

RAG (Retrieval-Augmented Generation)

  • 모델의 학습 데이터에 포함되지 않은 데이터를 사용 (환각 방지)
  • 외부 데이터를 검색(retrieval) 한 후, 생성(generation) 단계에서 LLM에 전달
  • 모델은 주어진 콘텍스트나 질문에 더 적합하고 풍부한 정보를 반영한 답변을 생성

논문: https://arxiv.org/abs/2005.11401

 

Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks

Large pre-trained language models have been shown to store factual knowledge in their parameters, and achieve state-of-the-art results when fine-tuned on downstream NLP tasks. However, their ability to access and precisely manipulate knowledge is still lim

arxiv.org

 

라이브러리 설치

!pip install -q langchain langchain-openai langchain_community tiktoken chromadb

 

인증키 설정

import os
os.environ["OPENAI_API_KEY"] = "your-openai-api-key-here"

 

RAG 파이프라인 개요

Load Data - Text Split - Indexing - Retrieval(검색) - Generation(생성)

 

Step 1 : Load Data

# Data Loader - 웹페이지 데이터 가져오기
from langchain_community.document_loaders import WebBaseLoader

# 위키피디아 정책과 지침
url = 'https://ko.wikipedia.org/wiki/%EC%9C%84%ED%82%A4%EB%B0%B1%EA%B3%BC:%EC%A0%95%EC%B1%85%EA%B3%BC_%EC%A7%80%EC%B9%A8'
loader = WebBaseLoader(url)

# 웹페이지 텍스트 -> Documents
docs = loader.load()

print(len(docs))
print(len(docs[0].page_content))
print(docs[0].page_content[5000:6000])

 

Step 2 : Split texts

# Text Split (Documents -> small chunks: Documents)
from langchain.text_splitter import RecursiveCharacterTextSplitter # 쉼표 기준으로 짤라주기 때문에 문장이 매끄럽게 잘린다.

text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200) # RecursiveCharacterTextSplitter 텍스트를 작게 나누어 주는 클래스 함수
splits = text_splitter.split_documents(docs) # split_documents 리스트에 들어있는 documents 객체를 청크 단위로 작게 나누어 준다.

print(len(splits))
print(splits[10])

# page_content 속성
splits[10].page_content

# metadata 속성
splits[10].metadata

 

Step 3 : Indexing

# Indexing (Texts -> Embedding -> Store)
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings

vectorstore = Chroma.from_documents(documents=splits,
                                    embedding=OpenAIEmbeddings())

docs = vectorstore.similarity_search("격하 과정에 대해서 설명해주세요.") # 격하 과정과 가장 연관이 있는 청크를 가져와서 보여준다.
print(len(docs))
print(docs[0].page_content)

 

Step 4: Retrieval ~ Generation

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

# Prompt
template = '''Answer the question based only on the following context:
{context}

Question: {question}
'''

prompt = ChatPromptTemplate.from_template(template)

# LLM
model = ChatOpenAI(model='gpt-3.5-turbo-0125', temperature=0) # temperature=0 답변의 일관성을 위해서 낮추는 경우도 있다.

# Rretriever
retriever = vectorstore.as_retriever() # 임베딩 된 벡터들 사이에서 검색해오도록 설정

# Combine Documents
def format_docs(docs):
    return '\n\n'.join(doc.page_content for doc in docs)

# RAG Chain 연결
rag_chain = (
    {'context': retriever | format_docs, 'question': RunnablePassthrough()} # RunnablePassthrough()} 사용자의 입력이 question에 전달되도록 구현
    | prompt
    | model
    | StrOutputParser()
)

# Chain 실행
rag_chain.invoke("격하 과정에 대해서 설명해주세요.")

 

 

큰 가다는 잡았는데,, 이제 시작이네요 파이팅


참고 자료

 

 

https://bcho.tistory.com/1420

 

Langchain을 이용한 LLM 애플리케이션 개발 #13 - Chain을 이용한 워크 플로우 구현 #1

Chain을 이용한, LLM 워크 플로우 구현 #1 조대협 (http://bcho.tistory.com) LLM 기반 애플리케이션을 개발할때, 한번의 LLM 호출로 결과를 낼 수 도 있지만, 복잡한 LLM 애플리케이션의 경우, LLM의 출력을 다

bcho.tistory.com

 


출처

 

https://wikidocs.net/book/14473

 

랭체인(LangChain) 입문부터 응용까지

**랭체인(LangChain) 입문부터 응용까지** 이책은 거대언어모델(LLM)을 활용하여 애플리케이션과 파이프라인을 신속하게 구축할 때 주로 활용되는 랭체인(LangCha…

wikidocs.net

 

 

 

 

728x90
반응형

관련글 더보기