1. 학습 :
LLM은 대규모 텍스트 데이터셋을 이용해 학습함
여기서 중요한 개념은 "패턴 인식"임.
수많은 텍스트에서 단어와 문장의 패턴을 찾아내어, 새로운 문장이나 답변을 생성할 때 그 패턴을 적용함
2. 추론 :
학습된 LLM은 질문이나 입력을 받으면, 그에 따른 추론을 통해 답변을 생성함
이때, 이전의 맥락을 기억하고 활용하면서 답을 만들어냄
3. 미세 조정 :
LLM은 특정 도메인이나 용도에 맞춰 추가 학습 (미세 조정)할 수 있음
예를 들어, 의료나 법률과 같은 특수한 분야에 맞는 데이터를 추가로 학습시키면 해당 분야에 대한 답변의 정확성이 높아짐
1. 랜덤성
2. 조건성
RAG
LLM이 스스로 모든 답을 생성하는 대신, 데이터베이스에서 정보를 검색하고 그것을 기반으로 답변을 생성하는 기법
Vector DB
벡터 데이터베이스는 텍스트를 벡터 형태로 변환하여 유사한 의미를 가진 텍스트를 효율적으로 검색할 수 있게 도와주는 기술임. LLM과 결합하면 더 강력한 검색 및 응답 기능을 구현할 수 있음
LangChain
LangChain은 LLM과 같은 언어 모델을 더욱 효율적으로 활용할 수 있게 도와주는 프레임워크임
LangChain의 목적은 다양한 LLM과 외부 리소스를 결합해 강력한 언어 기반 애플리케이션을 만들 수 있도록 돕는 것임
LLM의 기능을 더욱 확장하고, 데이터 소스, API, 데이터베이스 등을 쉽게 통합할 수 있음
주요 기능으로는
토큰 길이
토큰은 GPT 모델에서 사용하는 단위로, 생성할 수 있는 텍스트의 길이를 조절하는 값임
기본적으로 짧은 응답이 필요하면 낮은 값을, 긴 글을 생성하려면 높은 값을 설정함
User
Assistant
System
-> System 역할을 이용해 Assistant가 특정 역할을 수행할 수 있음
예를 들어, System 지침으로 "Assistant는 프로그래밍 전문가로서 답변합니다"라고 설정하면, 기술적 질문에 전문적인 답변을 하게끔 유도할 수 있음
간단한 프롬프트 엔지니어링 실습
from dotenv import load_dotenv
import os
import openai
# .env 파일 불러오기
load_dotenv()
openai.api_key = os.getenv("OPEN_API_TOKEN")
# system message 배열 설정
system_message = {
"role" : "system",
"content" : "너는 환영 인사를 하는 인공지능이야. 너의 이름은 Sooya야. 농담을 섞어서 응대해줘. 영어로도 말해줘",
"content" : "도서관은 공학관 옆에 위치하고 있고 동상은 꼭대기 언덕에 위치하고 있어."
}
# 모든 메시지들 담는 배열 messages
# 초기에 user 맞춤화된 정보를 제공하고 싶다면 초기 설정에 user 값을 넣어줄 수 있겠음
messages = [system_message,
{"role": "user", "content" : "직업은 대학생"}
]
while True:
# 기본 프롬프트 설정해서 응답 API 받아와 completion 생성
completion = openai.ChatCompletion.create(
model="gpt-4o",
messages=messages
)
# 기본 환영인사를 만드는 GPT
# GPT의 응답 생성
reply = completion.choices[0].message.content
messages.append({"role": "assistant", "content" : reply})
# GPT의 응딥 print
print("AI SOOYA : " + reply)
# GPT에게 물어볼 입력값을 받음
user_input = input("무엇을 물어보고 싶으신가요 >>> ")
if user_input == "exit":
print("즐거운 대화였습니다! 감사합니다!")
break
# GPT messages에 추가함
messages.append({"role" : "user", "content" : user_input})
성능을 높일 수 있는 간단한 방법
(기본 원칙)
실습 생략
Act As 류의 프롬프팅 기법 배우기
CoT 적용해보기
예시로 GPT-4o 에 아래 문제를 넣어봤는데 CoT를 부여하지 않아도 잘 푸는 것을 확인할 수 있었다.
생각보다 성능이 많이 좋구나
자동차가 3대 있어, 오토바이는 12대 있어. 이중 바퀴가 모두 터진 장비는 총 3대야. 그런데 터진 바퀴가 총 12개면 자동차와 오토바이는 각각 몇 대 고장났을까?
대화형 프롬프팅 기법
형식 지정 기법 (GPT에게 지정된 형식으로 알아듣기 쉽게 하기 위해 사용)
Markdown 기본 문법
"#" 헤더, 제목과 세션을 나누는 데 사용 (역할, 지침 등등...을 나눠서 부여할 수도 있겠음)
"-" 리스트, 항목을 나열할 수 있음
"|", "--"를 사용해 표를 만들 수 있음
JSON 활용하기
데이터 처리나 시스템과의 연동이 필요한 경우 JSON 형식을 많이 사용
symbol은 크게 영향을 주지 않았다고 함
민감 정보 필터링
입력된 데이터를 처리하기 전에 민감한 정보를 자동으로 걸러내는 필터링 시스템을 구축함
암호화
데이터는 저장 및 전송 중에 암호화되어야 함
특히 SSL/TLS와 같은 안전한 전송 프로토콜을 사용해야 함
데이터 저장 최소화
필요 이상으로 데이터를 저장하지 말고, 필요한 경우에도 데이터 보존 주기를 설정해 자동 삭제하도록 함
접근 통제
LLM을 사용할 수 있는 사람의 권한을 제한하고, 모델이 민감한 데이터에 접근하지 않도록 제한해야 함
비슷한 문자라면 비슷한 숫자 벡터를 부열하자는 개념이 인베딩 모델과 벡터 DB의 개념
라이브러리와 프레임워크의 차이:
특정 패키지가 사용자를 조정하느나
사용자가 조정해서 그 패키지를 사용할 수 있냐
Vector DB는 데이터를 벡터 형식으로 저장하고, 그 벡터들을 효율적으로 검색할 수 있는 데이터베이스임
일반적인 데이터베이스는 정확한 일치를 바탕으로 데이터를 검색하지만, Vector DB는 유사한 벡터 간의 검색을 지원함
벡터(임베딩)의 역할
텍스트나 이미지 등의 비정형 데이터를 벡터화(임베딩)해서 저장함
이 벡터는 데이터의 의미나 특징을 수치로 표현한 것이며, 이를 바탕으로 유사도를 계산해 관련성이 높은 항목을 찾음
예를 들어, "강아지"라는 텍스트는 벡터로 변환되며, 비슷한 의미를 가진 "반려견"도 벡터화되어 유사도가 높은 항목으로 검색될 수 있음
Faiss
Faiss는 Facebook AI Research에서 개발한 벡터 검색 엔진으로, Vector DB를 구현할 때 자주 사용됨
대규모 벡터를 효율적으로 검색하고, 유사도를 계산하는 데 탁월한 성능을 발휘함
특히 빠른 속도와 확장성이 필요한 애플리케이션에서 많이 쓰임
Word2Vec은 특정 단어가 주변 단어와 함께 등장할 확률을 예측하는 방식으로 학습됨
단어 주변의 맥락을 파악하고 비슷한 단어들의 가까운 벡터 위치를 찾을 수 있게 됨
GloVe는 전체 말뭉치에서 단어 상의 공분산을 학습함
의미상 유사하다면 가까운 벡터로 표현되도록 학습이 됨
각 단어가 다른 단어와 어떻게 관계되는지를 학습하게 됨
실습
from sentence_transformers import SentenceTransformer
import numpy as np
from dotenv import load_dotenv
import os
# .env 파일 불러오기
load_dotenv()
api_key = os.getenv("API_TOKEN")
model = SentenceTransformer('intfloat/multilingual-e5-large',token=api_key)
sentences = [
"참새는 짹짹하고 웁니다.",
"LangChain과 Faiss를 활용한 예시입니다.",
"자연어 처리를 위한 임베딩 모델 사용법을 배워봅시다.",
"유사한 문장을 검색하는 방법을 살펴보겠습니다.",
"강좌를 수강하시는 수강생 여러분 감사합니다!"
]
# 문장이 문맥과 의미 분석을 위해 차원이 부여되게 됨
# 문장을 숫자의 나열로 바꿔주는 게 임베딩임
embeddings = model.encode(sentences)
print(embeddings.shape) # (5, 1024) : 5개의 문장이 1024 차원의 벡터로 변환됨
텍스트 처리는 데이터의 품질을 높이고 모델의 성능을 향상시키기 위한 필수 작업임
자연어는 매우 복잡하고 다양하기 때문에, LLM이 텍스트를 정확하게 이해하고 처리하기 위해서는 데이터가 구조화되고 정제될 필요가 있음
목표
노이즈 제거 : 텍스트 내 불필요한 정보나 오류를 제거해 정확한 분석을 할 수 있도록 함
일관성 확보 : 문장의 구조나 형태를 일관되게 유지하여 모델이 더 쉽게 패턴을 학습하게 도움
효율적인 처리 : 불필요한 단어를 제거하고 중요한 정보만 남겨, 모델이 더 빠르게 계산할 수 있도록 함
- 토큰화
토큰화는 텍스트를 단어 또는 서브워드 단위로 나누는 작업임
이 과정은 텍스트를 숫자로 변환하기 전의 가장 중요한 단계에 해당함
단어 단위 토큰화 : 텍스트를 단어 단위로 나누는 기본 방법임
서브워드 토큰화 : 단어를 더 작은 단위로 분리해 새로운 단어를 처리할 수 있도록 함
BPE나 WordPiece같은 방법이 있음
ex. "나는 오늘 책을 읽었다." -> ["나는", "오늘", "책을", "읽었다"]
- 정규화
정규화는 텍스트를 표준화된 형식으로 변환하는 작업임
텍스트에 포함된 대소문자, 특수문자 등을 일관되게 변환하여, 모델이 불필요한 변동에 혼란을 겪지 않도록 함
소문자 변환 : 대문자와 소문자를 통일하여 같은 단어로 인식하게 함
ex. "OpenAI" -> "openai"
불필요한 기호 제거 : 분석에 필요 없는 특수 문자나 기호를 제거
불용어 제거 : 자주 등장하지만 정보가 없는 단어 (ex : 그리고, 이, 는)
형태소 분석 : 교착어에서는 형태소 분석이 필수적임
어간 추출과 표제어 추출 : 텍스트에서 동사나 형용사의 변형을 기본 형태로 돌리는 작업을 말함 (이를 통해 동일한 단어를 일관되게 처리할 수 있음)
문장 분리 및 길이 조절 : 텍스트가 너무 길거나 복잡할 경우, 적절하게 나누거나 길이를 조정해야 함
문장 : "고양이가 야옹했다"
벡터 표현 : [1, 1, 1, 0, 0] (각 단어의 빈도수)
그런데 관사랑 중요하게 사용되는 고유 명사와는 어떻게 구별하는 것일까??
-> 관사같은 경우에는 거의 모든 문서에 등장을 한다. 때문에 IDF에 주는 영향이 훨씬 많으며 이는 TF보다 IDF에 결과값이 더 민감하게 반응하기 때문이라고 결론을 내릴 수 있음
TF-IDF 구조는 둘을 곱해서 가중치를 계산하게 되는데
단어 빈도(TF): 문서 내에서 단어가 얼마나 자주 등장하는지를 반영합니다. 많이 등장할수록 TF가 높아집니다.
역문서 빈도(IDF): 여러 문서에서 등장하는 단어일수록 IDF 값이 낮아지도록 설계되어 있습니다. IDF는 일반적으로 다음과 같은 식으로 계산됩니다:
IDF = logN/df
N은 전체 문서의 수, df는 해당 단어가 등장한 문서의 수이다.
관사같은 경우에는 전체 문서에서 등장하기 때문에 df가 커지게 되고 그에따라 IDF가 작아지게 됨
BUT 고유 명사같은 경우에는 df가 낮기 때문에 역문서 빈도가 낮아지게 됨
이렇게 IDF가 낮아지면서 관사나 전치사와 같이 자주 등장하는 단어들은 TF-IDF 값도 자동으로 낮아져 가중치가 작아지게 되는 것입니다. 이 방법 덕분에 고유 명사나 주제와 관련된 중요한 단어들에 더 높은 가중치를 주고, 불필요한 단어들의 영향을 줄일 수 있습니다.
TF-IDF임베딩 처리를 한다면 Attention 알고리즘을 적용하는 것이 무의미해지는 것이 아닐까??
TF-IDF는 단순하게 문서에서의 중요도민을 생각하지만 Attention알고리즘의 경우에는 문맥까지 생각하기 때문에 구체적인 사항은 다르다고 할 수 있음
CBow : 주변단어로 중심단어를 예측함
SkipGram : 중심단어로 주변단어를 예측함
단어 간의 의미적 유사성을 반영하는 임베딩 기법 (단어를 고차원 벡터로 변환하여, 단어 간의 관계를 학습)
번외
GPT API가 유료이다 보니 토큰 수에 주의를 해야겠다고 생각되어서 billing을 참고했는데
물론 4가 성능이 나아진 것이 맞긴 하지만 billing에서 꽤나 차이를 보이고 있다.
그래도 4를 사용하는 것이 맞을까?!

hugging face 이용 방법
CLI 설치
pip install huggingface_hub
CLI 로그인
huggingface-cli login
LEFT
LangChain