LLM (Large Language Model) 을 사용하는데 있어 

 - Knowledge Cutoff: 모델 학습 이후에 생성된 데이터에 대해서는 학습이 안되어 답변을 못함

 - No access to private data: 회사 내부 기밀정보, 신상정보, 비공개 정보에 대해서는 접근 불가

 - Hallucinations: 사실에 입각하지 않은 답변을 그럴싸하고 자연스럽게 하는 환각 증상

 - General Purpose: 다방면에 일정 수준 이상의 답변을 할 수 있지만, 특정 영역의 전문성이 떨어짐

등의 한계가 있을 수 있습니다. 

 

이를 해결하는 방법으로 Fine-tuning 방법과 함께 RAG (Retrieval-Augmented Generation) 방법이 많이 사용됩니다. 

 

이번 포스팅에서는 

 

1. RAG (Retrieval-Augmented Generation) 이란 무엇인가? 

2. RAG (Retrieval-Augmented Generation) 방법의 절차는 어떻게 되나? 

3. LangChain 으로 RAG 구현해보기

 

에 대해서 소개하겠습니다. 

 

 

 

1. RAG (Retrieval-Augmented Generation) 이란 무엇인가? 

 

RAG (Retrieval-Augmented Generation)은 자연 언어 처리 분야에서 언어 모델의 기능을 향상시키기 위해 검색 시스템과 결합하는 방법입니다. 이 접근법은 광범위한 외부 정보 소스에 의해 정보를 제공받아 응답이나 콘텐츠를 생성하는 데 특히 유용합니다. 

RAG(Retrieval-Augmented Generation)에 대한 개요는 다음과 같습니다. 

1. 검색 및 생성 결합 (Combining Retrieval and Generation): RAG 모델은 문서 검색 시스템과 seq2seq 언어 모델을 통합합니다. 검색 시스템은 입력 쿼리에 기반하여 대규모 코퍼스(위키피디아나 사용자 정의 데이터베이스와 같은)에서 관련 문서나 정보를 먼저 가져옵니다. 

2. 정보 증강 (Information Augmentation): 검색된 문서는 언어 모델의 입력을 증강하는 데 사용되며, 이는 모델이 초기 훈련 중에 '학습'하지 못한 추가적인 맥락과 정보(additional context and information)를 제공합니다. 이를 통해 모델은 더 정확하고 맥락에 적합한 응답을 생성할 수 있게 됩니다. 

3. 응용 분야 (Applications): RAG는 모델이 훈련 데이터에 포함되지 않은 구체적이고 사실적인 답변을 제공해야 하는(model needs to provide specific, factual answers) 질문 응답 시스템에서 특히 유용합니다. 또한, 정확도를 위해 외부 데이터 소스가 필요한 상세하거나 기술적인 응답이 필요한 애플리케이션에서도 사용됩니다. 

4. 사용 예시: RAG의 일반적인 응용은 복잡한 질문에 대해 독립적인 언어 모델보다 정확하게 답변할 수 있는 관련 정보를 수집하는 AI 챗봇과 가상 비서입니다. 

5. RAG 방법의 장점 (Advantages of RAG)

- 더 풍부한 응답 (Richer Responses): 광범위한 정보에 접근함으로써 RAG는 더 상세하고 정확한 응답을 제공할 수 있습니다.
- 동적 지식 (Dynamic Knowledge): 모델이 최신 정보에 접근할 수 있게 해주며, 특히 최근 이벤트나 특정 데이터에 관한 질문에 유용합니다. 

6. 기술 구현 (Technical Implementation): RAG 시스템을 구현하는 것은 일반적으로 Elasticsearch나 밀집 벡터 검색 시스템(Dense Vector Search System)과 같은 검색 모델과 GPT나 BART와 같은 seq2seq 언어 모델을 결합하는 것을 포함합니다. 


RAG는 AI와 NLP 분야에서 중요한 발전을 나타내며, 광범위한 외부 정보에 기반한 보다 동적이고 최신의 내부 정보에 입각한 정확한 언어 생성을 가능하게 합니다.  

 

 

 

2. RAG (Retrieval-Augmented Generation) 방법의 절차는 어떻게 되나? 

 

검색-증강 생성(Retrieval-Augmented Generation, RAG) 방법은 정보 검색과 언어 생성의 힘을 결합한 자연 언어 처리에서의 정교한 접근법입니다. RAG 작동 방식을 단계별로 설명하면 다음과 같습니다:

 

RAG(Retrieval-Augmented Generation): Load - Split - Embed - Store
RAG(Retrieval-Augmented Generation): Question - Retrieve - Prompt - LLM - Answer

* 출처: https://python.langchain.com/docs/use_cases/question_answering/

 

 


1. 문서를 텍스트 임베딩으로 변환하여 Vector DB에 저장하기 

- 문서를 읽어와서(Load) 분할(Split)하고 파싱(Parsing)하기
- Dense Vector 형태의 Sentence Embedding 변환하기
- 나중에 Retrieval 단계에서 빠르게 검색해서 사용할 수 있도록 Indexing하여 Vector DB에 저장(Store)하기 


2. 입력 수신 (Input Reception)

프로세스는 입력 쿼리 또는 프롬프트를 받는 것으로 시작됩니다. 이 쿼리가 RAG 프로세스를 활성화합니다.


3. 문서 검색 (Document Retrieval)

- 검색 시스템 활성화 (Retrieval System Activation): 첫 번째 주요 단계는 검색 시스템을 활성화하는 것입니다. 이 시스템은 대규모 문서 코퍼스나 데이터베이스(위키피디아나 전문화된 데이터셋과 같은)를 검색하도록 설계되었습니다. 
- 쿼리 처리 (Retrieval System Activation): 입력 쿼리는 검색 시스템이 이해할 수 있는 검색 쿼리를 형성하기 위해 처리됩니다. 
문서 가져오기 (Document Fetching): 검색 시스템은 처리된 쿼리를 기반으로 코퍼스를 검색하고 관련 문서나 정보 스니펫을 검색합니다. 


4. 정보 증강 (Information Augmentation)

맥락 통합 (Context Integration): 검색된 문서는 입력 쿼리를 증강하는 데 사용됩니다. 이 단계는 원래 입력과 문서에서 추출된 관련 정보를 결합하는 것을 포함합니다.
증강된 입력 형성 (Context Integration): 증강된 입력이 형성되며, 이제 원래 쿼리와 검색된 문서의 추가 맥락을 모두 포함하게 됩니다.


5. 언어 모델이 답변 생성 (Language Model Generation)

증강된 입력 공급 (Feeding Augmented Input): 이 증강된 입력은 일반적으로 GPT나 BART와 같은 시퀀스-투-시퀀스 모델인 언어 모델에 공급됩니다.
응답 생성 (Response Generation): 언어 모델은 증강된 입력을 처리하고 응답을 생성합니다. 이 응답은 모델의 사전 훈련된 지식뿐만 아니라 검색 단계에서 가져온 외부 정보에 의해 정보를 제공받습니다.


6. 출력 생성 (Output Production)

정제 및 형식화 (Refinement and Formatting): 생성된 응답은 필요에 따라 애플리케이션의 요구 사항에 맞게 정제되거나 형식화될 수 있습니다.
아웃풋 전달 (Output Delivery): 최종 응답은 RAG 프로세스의 출력으로 전달됩니다. 이 출력은 일반적으로 독립적인 언어 모델에 의해 생성된 응답보다 더 정보에 근거하고 정확하며 맥락적으로 관련성이 높습니다.


7. 피드백 루프 (선택적)

일부 구현에서는 검색된 문서의 효과성과 최종 출력의 품질을 평가하는 피드백 메커니즘이 있을 수 있습니다. 이 피드백은 프로세스의 미래 반복을 개선하는 데 사용될 수 있습니다.


RAG 방법은 광범위한 정적 지식베이스와 현대 언어 모델의 동적 생성 능력 사이의 격차를 효과적으로 연결하여 맥락적으로 풍부하고 매우 관련성이 높은 답변을 생성합니다. 

 

 

 

3. LangChain 으로 RAG 구현해보기

 

흐름은 다음과 같습니다. 

RAG Pipeline

* 출처: https://python.langchain.com/docs/expression_language/get_started#basic-example-prompt-model-output-parser



1. 첫 번째 단계에서는 두 개의 항목이 있는 RunnableParallel 객체를 생성합니다. 첫 번째 항목인 context는 검색기에 의해 가져온 문서 결과를 포함할 것입니다. 두 번째 항목인 question은 사용자의 원래 질문을 담을 것입니다. 질문을 전달하기 위해 RunnablePassthrough를 사용하여 이 항목을 복사합니다. 

2. 위 단계에서의 Dictionary를 프롬프트 구성요소에 공급합니다. 그런 다음 사용자 입력인 question과 검색된 문서인 context를 사용하여 프롬프트를 구성하고 PromptValue를 출력합니다. 

3. 모델 구성요소는 생성된 프롬프트를 가져와 OpenAI LLM 모델(ChatModel)에 평가를 위해 전달합니다. 모델에서 생성된 출력은 ChatMessage 객체입니다. 

4. 마지막으로, output_parser 구성요소는 ChatMessage를 가져와 이를 Python 문자열로 변환(StrOutputParser)하며, 이는 chain.invoke() 메소드에서 반환됩니다. 

 

 

 

LangChain 코드를 하나씩 살펴보겠습니다. 

먼저, 필요한 모듈로서 langchain, openai, docarray, tiktoken 이 미리 설치가 안되었다면, 먼저 터미널에서 pip install 을 사용해서 모듈을 설치해야 합니다. (Jupyter Notebook에서 사용 중이라면 셀 안에서 '!' 를 앞에 붙임)

 

-- Prerequisite: install modules of langchain, openai, docarray, tiktoken
! pip install -q langchain openai docarray tiktoken

 

 

DocArrayInMomorySerch 클래스를 사용해서 In-memory Vector DB에 OpenAI의 Embeddings model을 사용해서 임베딩 변환한 문장들을 저장합니다. 

 

그리고 retriever 를 설정 (retriever = vectorstore.as_retriever()) 합니다. 

retriever 는 나중에 chaining 해서 쓸 수도 있고, 아니면 retriever.invoke() 해서 바로 검색(Retrieval)하는데 사용할 수도 있습니다. 검색을 할 때는 Vector DB에 저장된 임베딩과 User query로 들어온 문서의 임베딩 간에 코사인 유사도 (cosine similarity)를 계산해서 유사도 내림차순 기준으로 검색 결과를 반환합니다. 

 

## import modules required
from langchain.chat_models import ChatOpenAI
from langchain.embeddings import OpenAIEmbeddings
from langchain.prompts import ChatPromptTemplate
from langchain.vectorstores import DocArrayInMemorySearch
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableParallel, RunnablePassthrough


## Sentence Embedding, Vector Store
vectorstore = DocArrayInMemorySearch.from_texts(
    ["There are 4 seasons in Korea", 
     "Harry Potter is an outstanding wizard",
     "The dog likes to walk"], 
    embedding=OpenAIEmbeddings(openai_api_key=OPENAI_API_KEY), # set with yours
)


## Setup the Retriever using an In-memory store
## , which can retrieve documents based on a query
retriever = vectorstore.as_retriever()

## This is a runnable component that can be chained together with other component as below, 
## , but you can also try to run it separately.
retriever.invoke("Who is Harry Potter?")
# [Document(page_content='Harry Potter is an outstanding wizard'),
#  Document(page_content='The dog likes to walk'),
#  Document(page_content='There are 4 seasons in Korea')]

 

 

ChatPromptTemplate.from_template() 를 사용해서 Prompt Template 를 생성해줍니다. 아래의 template 를 보면 알 수 있는 것처럼 LLM 모델에게 "오직 주어진 context 에 기반해서 질문에 답변을 해라" 라고 지시를 합니다. 

 

template = """Answer the question based only on the following context: {context}  Question: {question}""" 

 

그리고 {context} 에는 문서 검색(Retrieval)의 결과를 넣어주고, {question}에는 사용자 질문을 넣어줍니다. 그러면 위에서 정의한 retriever 가 문서의 임베딩된 문장들과 사용자 질문 임베딩 간의 코사인 유사도를 계산해서 가장 유사한 k 개의 문장을 {context}로 넣어주게 됩니다. 

 

## Chat Prompt Template
# : the prompt template below takes in context and question as values 
# : to be substituted in the prompt.
template = """Answer the question based only on the following context:
{context}

Question: {question}
"""

prompt = ChatPromptTemplate.from_template(template)

## ChatModel
model = ChatOpenAI(openai_api_key=OPENAI_API_KEY) # set with yours

## Output Parser
output_parser = StrOutputParser()

 

 

RunnableParallel 메소드는 (a) "context"로서 retriever와 (b) "question"으로서 RunnablePassthrough() 를 병렬로 처리할 수 있게 해줍니다. (위의 RAG pipeline 그림 참조하세요. 두 갈래로 나뉘어서 동시 처리됩니다.)

 

'|' 를 사용해서 setpu_and_retrieval (사용자 질문을 받아서 저장된 문서 중에서 가장 관련있는 텍스트를 검색해오기) + Prompt (오직 {context}를 참조해서만 {question}에 답변하라는 지시) + model (OpenAI의 ChatModel LLM을 사용해서 답변) + output_parser (ChatModel이 생성한 ChatMessage를 string으로 파싱) 연결(chaining) 합니다. 

 

chain.invoke() 로 위에서 생성한 chian을 실행해줍니다. 

 

## Before building the prompt template, 
## we want to retrieve relevant documents to the search and include them as part of the context.
setup_and_retrieval = RunnableParallel(
    {"context": retriever, # the retriever for document search
     "question": RunnablePassthrough() # to pass the user's question
     }
)

## Chaining setup_and_retrieval + Prompt + ChatModel + OutputParser
chain = setup_and_retrieval | prompt | model | output_parser

## Run a chain pipeline
chain.invoke("Who is Harry Potter?")
# Harry Potter is an outstanding wizard.

 

 

[ Reference ]
1. RangChain Tutorial - RAG Search Example
https://python.langchain.com/docs/expression_language/get_started#basic-example-prompt-model-output-parser

2. LangChain Tutorial - Retrieval-Augmented Generation (RAG)
https://python.langchain.com/docs/use_cases/question_answering/

 

3. transformer를 이용한 토큰화(Tokenization), 단어 임베딩(Word Embedding), 텍스트 임베딩 (Text Embedding)
: https://rfriend.tistory.com/m/807

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!

 

728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서 LangChain 은 대규모 언어 모델을 사용해서 애플리케이션을 개발, 프로덕트화, 배포하는 것을 도와주는 프레임워크라고 소개했습니다. 그리고 LangChain Expression Language (LCEL) 는 LangChain의 기본적인 컴포넌트들을 chaining 기법을 써서 다단계 작업들의 연결 체인을 만들어 복잡한 기능을 구현할 수 있도록 해준다고 했습니다. 

 

이번 포스팅에서는 LangChain Expression Language (LCEL) 의 가장 간단한 형태의 chaining 예시를 들어보겠습니다. 

 

LCEL Chaining 기본 형태: Prompt + Model + Output Parser

 

이때 Model 로서 ChatModel과 LLM 모델을 사용할 때 input 과 output 이 조금 다르기 때문에, Model에 따라서 나누어서 소개하겠습니다. 

 

(1) ChatModel 사용 시 Pipeline: Input --> {Dict} --> PromptTemplate --> PromptValue --> ChatModel --> ChatMessage --> StrOutputParser --> String --> Result

 

(2) LLM 사용 시 Pipeline: Input String --> LLM --> Output String

 

LangChain: chaining a prompt, a model, and output parser

 

 

 

(1) ChatModel 을 사용한 Chaining 예: Prompt + ChatModel + Output Parser

: Input --> {Dict} --> PromptTemplate --> PromptValue --> ChatModel --> ChatMessage --> StrOutputParser --> String --> Result

 

만약 실습 환경에 langchain, openai 모듈이 설치가 안되어 있다면 termianl 이나 Jupyter Notebook (pip install 앞에 '!' 를 붙여서 사용) 에서 langchain, openai 모듈을 먼저 설치하기 바랍니다. 

 

! pip install -q langchain
! pip install openai

 

 

(1) 필요한 modules 을 불러옵니다. 

(2) OpenAI 의 API Key 를 설정해줍니다. 

(3) Prompt Template, Chat Model, Output Parser 의 인스턴스를 생성합니다. 

(4) '|' 를 사용해서 chain = promt | model | output_parser 를 연결(Chaining)해줍니다 

(5) chain.invoke() 를 사용해서 chain pipeline 을 실행시켜줍니다. 이때 invoke({"topic": "bird"}) 처럼 Dictionary 형태로 PromptTemplate 에 들어갈 Input을 넣어주면 됩니다. 

 

## (1) Importe modules
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser

## (2) set with your openai api key
openai_api_key="sk-xxxxxx..."

## (3) Create instances of prompt, ChatModel, adn output parser
prompt = ChatPromptTemplate.from_template("tell me a short joke about {topic}")
model = ChatOpenAI(openai_api_key=openai_api_key)
output_parser = StrOutputParser()

## (4) Chaining a PromptTemplate + ChatModel + Output Parser using '|'
chain = prompt | model | output_parser

## (5) Run the whole pipeline of chain
chain.invoke({"topic": "bird"})

# \n\nWhy was the bird kicked out of the comedy club? Because it kept telling fowl jokes!

 

 

위에서 생성한 ChatPromptTemplate 의 인풋, 아웃풋을 단계별로 하나씩 살펴보겠습니다. 

 

    - ChatPromptValue: prompt.invoke()

    - Messages: prompt_value.to_messages()

    - HumanMessage: prompt_value.to_string()

 

ChatModel 은 인풋으로 PromptValue 를 받는 반면에, LLM 은 a string을 인풋으로 받는 차이점이 있습니다. (LLM도 PromptValue를 인풋으로 넣어줘도 작동합니다)

 

## ChatPromptValue
prompt_value = prompt.invoke({"topic": "bird"})

prompt_value
# ChatPromptValue(messages=[HumanMessage(content='tell me a short joke about bird')])

prompt_value.to_messages()
# Human: tell me a short joke about bird

prompt_value.to_string()
# Human: tell me a short joke about bird

 

 

다음으로, ChatModel의 아웃풋인 AIMessage 를 살펴보겠습니다. 

 

## ChatModel AIMessage
message = model.invoke(prompt_value)

message
# AIMessage(content="Why don't birds wear shoes? \n\nBecause they have talon-t!")

 

 

 

(2) LLM Model 을 사용 예

: Input String --> LLM --> Output String

 

LLM 모델로는 OpenAI의 OpenAI(model="gpt-3.5-turbo-instruct") 모델을 사용해서 예를 들어보겠습니다. LLM 모델의 인풋으로 string 을 받고, 아웃풋으로는 생성된 string 을 반환합니다. (위의 ChatModel 대비 인풋과 아웃풋이 모두 string 으로 상대적으로 간단합니다.)

 

# prompt + llm model + output parser
from langchain.llms import OpenAI

# Set your OpenAI API Key
openai_api_key="sk-xxxxx..."

# Create an instance of LLM model
llm = OpenAI(model="gpt-3.5-turbo-instruct", openai_api_key=openai_api_key)

# Run LLM model, put a string as an input, and outputs a string
llm.invoke("tell me a short joke about bird")
# \n\nWhy did the chicken go to the seance? To get in touch with its inner chick!

 

 

 

[Reference]

* LangChain Expression Language
https://python.langchain.com/docs/expression_language/get_started#basic-example-prompt-model-output-parser

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

LLM (Large Language Model) 을 활용한 애플리케이션 개발에 많이 활용되는 프레임워크로 LangChain과 Semantic Kernel 이 있습니다. 

 

이번 포스팅에서는 

 

1. LangChain의 주요 컴포넌트 및 특징

2. Semantic Kernel의 주요 컴포넌트 및 특징

3. LangChain과 Semantic Kernel 의 관계

 

에 대해서 소개하겠습니다. 

 

 


1. LangChain의 주요 컴포넌트 및 특징

 

LangChain은 언어 모델을 기반으로 한 애플리케이션을 개발하기 위한 프레임워크입니다. 특히 복잡한 추론, 다단계 작업 처리, 그리고 외부 데이터 소스와의 통합에 중점을 둡니다. 이 프레임워크를 통해 다음과 같은 애플리케이션을 구현할 수 있습니다.  

문맥 인식 (Are context-aware): 언어 모델을 문맥의 출처(프롬프트 지시사항, 소수 샷 예시, 반응을 근거로 하는 내용 등)에 연결합니다. 

 

추론 (Reason): 언어 모델이 제공된 문맥을 바탕으로 어떻게 답변할지, 어떤 행동을 취할지 등에 대해 추론하도록 합니다. 

 


이 프레임워크는 여러 부분으로 구성되어 있습니다. 


(1) LangChain 라이브러리 (LangChain Libraries): Python과 JavaScript 라이브러리입니다. 다양한 컴포넌트의 인터페이스와 통합, 이러한 컴포넌트를 체인과 에이전트로 결합하는 기본 런타임, 그리고 체인과 에이전트의 즉각적인 구현을 포함합니다.  


LangChain은 다음 모듈에 대한 표준적이고 확장 가능한 인터페이스 및 통합을 제공합니다.  

 
  - 모델 I/O (Models I/O): 언어 모델과의 인터페이스

  - 검색 (Retrieval): 애플리케이션별 데이터와의 인터페이스

  - 에이전트 (Agents): 고수준 지시에 따라 모델이 사용할 도구를 선택하도록 함

 


(2) LangChain 템플릿 (LangChain Templates): 다양한 작업에 쉽게 배포할 수 있는 참조 아키텍처 모음입니다.  


(3) LangServe: LangChain 체인을 REST API로 배포하기 위한 라이브러리입니다.  


(4) LangSmith: 어떤 LLM 프레임워크에서 구축된 체인을 디버깅, 테스트, 평가 및 모니터링할 수 있게 해주는 개발자 플랫폼으로, LangChain과 원활하게 통합됩니다.  

 

[ LangChain Stack ]

LangChain Stack


* 출처: https://python.langchain.com/docs/get_started/introduction



이러한 제품들은 애플리케이션 수명 주기 전체를 간소화합니다. 


개발 (Develop): LangChain/LangChain.js에서 애플리케이션을 작성할 수 있습니다. 템플릿(Templates)을 참조하여 신속하게 시작할 수 있습니다.  


제품화 (Productionize): LangSmith를 사용하여 체인을 검사, 테스트 및 모니터링하면 지속적으로 개선하고 자신있게 배포할 수 있습니다.  


배포 (Deploy): LangServe를 사용하여 어떤 체인도 API로 전환합니다.  

 

 

LangChain을 활용한 애플리케이션 수명주기 간소화

 




LangChain의 주요 구성요소는 다음과 같습니다.  

 

(1) 언어 모델 (Language Models): LangChain의 핵심으로, 텍스트 생성에 필요한 기능을 제공합니다. GPT-3와 같은 다양한 언어 모델과 호환됩니다. 모델 I/O 모듈은 LLM과의 상호 작용을 다룹니다.  


(2) 체이닝 (Chaining): 여러 언어 모델 출력을 연결하여 일관되고 확장된 응답이나 해결책을 형성하는 과정입니다. 이를 통해 모델은 단일 단계에서 할 수 있는 것보다 더 복잡한 작업을 수행할 수 있습니다. 


(3) 도구 (Tools): LangChain은 웹 브라우저, 데이터베이스, 기타 API와 같은 다양한 도구를 통합하여 언어 모델이 정보를 수집하거나 작업을 수행할 수 있게 합니다. 


(4) 메모리 (Memory): 대화나 작업의 이전 부분에서 얻은 정보를 기억할 수 있는 메모리 시스템을 포함합니다. 이 메모리는 단기(단일 대화 동안) 또는 장기(대화 간 지속)로 사용될 수 있습니다. 


(5) 제어 시스템 (Control Systems): 모델이 다양한 컴포넌트를 어떻게 사용할지 결정하는 방식을 관리합니다. 예를 들어, 제어 시스템은 정보를 찾기 위해 언제 브라우저 도구를 사용할지 또는 모델의 내부 지식에 의존할지 결정할 수 있습니다. 


(6) 사용자 인터페이스 (User Interfaces): LangChain은 다양한 사용자 인터페이스와 통합될 수 있어, 챗봇, 가상 보조원 또는 더 전문화된 도구와 같은 다양한 애플리케이션에서 사용될 수 있습니다. 


(7) 애플리케이션 로직 (Application Logic): LangChain 모델이 특정 애플리케이션에서 어떻게 동작할지 정의하는 특정 로직과 규칙을 포함합니다. 이것이 LangChain을 다양한 사용 사례에 맞게 사용자 정의하는 부분입니다. 


(8) 콜백 (Callback): LangChain은 개발자에게 LLM 애플리케이션의 다양한 단계에 연결할 수 있는 콜백 시스템을 제공합니다. 이는 로깅, 모니터링, 스트리밍 및 기타 작업에 유용합니다. 파이프라인 내에서 특정 상황이 발생할 때 호출되는 사용자 지정 콜백 핸들러를 작성할 수 있게 해줍니다.  


LangChain의 모듈식 아키텍처는 개발자들이 언어 모델을 혁신적인 방식으로 활용하는 다양한 애플리케이션을 구축할 수 있는 유연성과 맞춤 설정 기능을 제공합니다. 

 


2. Semantic Kernel의 주요 컴포넌트 및 특징


Semantic Kernel은 언어 모델의 의미론적 이해와 텍스트 처리 기능을 향상시키기 위해 설계된 프레임워크입니다. 이는 언어 모델이 보다 정교하고 깊이 있는 의미론적 분석을 수행할 수 있도록 하는 데 중점을 두고 있습니다.  


Semantic Kernel 의 플러그인(Plugins)과 커넥터(connectors) 를 활용하면 AI 개발을 확장할 수 있습니다.  

Semantic Kernel은 플러그인(Plugins)을 통해 기존 코드를 AI 에이전트에 쉽게 추가할 수 있도록 설계되었습니다. 플러그인을 사용하면, 기존 앱과 서비스를 호출하여 에이전트가 실제 세계와 상호작용할 수 있는 능력을 부여할 수 있습니다. 이런 방식으로, 플러그인은 AI 앱의 "팔과 손"과 같습니다. 

또한, Semantic Kernel의 인터페이스는 어떤 AI 서비스와도 유연하게 통합할 수 있게 해줍니다. 이는 기억과 AI 모델을 쉽게 추가할 수 있는 일련의 커넥터(Connectors)들을 통해 이루어집니다. 이런 방식으로 Semantic Kernel은 앱에 시뮬레이션된 "뇌"를 추가할 수 있으며, 새롭고 더 나은 AI 모델이 나올 때마다 쉽게 교체할 수 있습니다. 

Semantic Kernel이 커넥터와 플러그인으로 제공하는 확장성 덕분에, 특정 AI 모델 제공업체에 구속되지 않고 거의 모든 기존 코드를 조율할 수 있습니다. 예를 들어, OpenAI의 ChatGPT용으로 다수의 플러그인을 구축했다면, Semantic Kernel을 사용하여 Azure나 Hugging Face와 같은 다른 제공업체의 모델과 함께 조율할 수 있습니다. 

Semantic Kernel은 어떤 제공업체의 AI 플러그인도 조율할 수 있습니다. 

개발자로서, Semantic Kernel의 다양한 구성요소를 별도로 사용할 수 있습니다. 예를 들어, OpenAI와 Azure OpenAI 서비스에 대한 추상화만 필요한 경우, 수제 프롬프트를 실행하기 위해 SDK만 사용할 수 있지만, Semantic Kernel의 진정한 힘은 이러한 구성요소를 함께 결합할 때 나타납니다. 


Semantic Kernel makes AI development extensible: Connectors, Plugins




Semantic Kernel의 주요 컴포넌트 및 특징은 다음과 같습니다. 

(1) 의미론적 분석 (Semantic Analysis): 이 컴포넌트는 텍스트의 의미를 보다 정확하게 파악하고 분석하는 데 도움을 줍니다. 이는 언어 모델이 텍스트의 뉘앙스와 복잡한 의미 구조를 더 잘 이해하고 반영할 수 있도록 합니다. 

(2) 문맥 관리 (Context Management): SemanticKernel은 긴 대화나 문서에서 문맥을 더 효과적으로 관리하는 기능을 제공합니다. 이를 통해 모델은 대화의 전체 흐름을 더 잘 이해하고, 이전에 나온 정보를 적절히 활용할 수 있습니다. 

(3) 향상된 언어 생성 (Enhanced Language Generation): 의미론적 분석을 통해 얻은 통찰력을 바탕으로, SemanticKernel은 언어 모델이 보다 정교하고 자연스러운 텍스트를 생성할 수 있도록 지원합니다. 이는 모델이 더 복잡하고 세부적인 표현을 생성하는 데 유용합니다. 

(4) 긴 문서 처리 (Long Document Handling): 긴 텍스트나 문서를 처리할 때, SemanticKernel은 중요한 정보를 추출하고 의미론적으로 관련된 내용을 더 잘 연결하는 데 도움을 줍니다. 

SemanticKernel의 핵심 목표는 언어 모델의 기본적인 언어 이해 능력을 향상시켜, 더 깊이 있는 의미론적 이해와 정교한 언어 생성을 가능하게 하는 것입니다. 이 프레임워크는 특히 의미론적으로 복잡한 작업, 긴 문서 분석, 또는 자연스러운 언어 생성이 필요한 응용 프로그램에서 유용하게 사용될 수 있습니다. 

 

 

 

3. LangChain과 Semantic Kernel 의 목적, 구성요소 및 관계


LangChain과 Semantic Kernel은 언어 모델의 기능을 향상시키기 위해 설계된 프레임워크지만, 각각 다른 목적을 가지고 있으며 다른 방식으로 작동합니다.

 

(1) LangChain


목적: LangChain은 언어 모델 응용 프로그램을 구축하기 위한 프레임워크로, 특히 멀티스텝 추론과 외부 도구와의 통합에 중점을 둡니다. 이는 언어 모델이 다양한 데이터 소스, 도구와 상호작용하거나 확장된 추론 작업을 수행하는 복잡한 응용 프로그램을 만드는 데 사용됩니다.


구성요소: 이 프레임워크는 언어 모델 출력의 연결, 브라우저나 데이터베이스와 같은 도구와의 통합, 메모리 처리 및 특정 응용 프로그램 로직을 포함합니다.


초점: LangChain의 주요 초점은 언어 모델이 복잡한 멀티스텝 작업을 수행하고 외부 데이터 소스와 상호작용할 수 있도록 함으로써 응용 프로그램 구축 프로세스를 향상시키는 데 있습니다.

 

 

(2) Semantic Kernel


목적: SemanticKernel은 언어 모델이 텍스트를 이해하고 생성하는 능력을 향상시키기 위해 설계된 프레임워크입니다. 이는 언어 모델의 의미론적 이해와 추론 능력을 향상시키는 데 중점을 둡니다.


구성요소: SemanticKernel은 더 나은 문맥 관리, 의미론적 이해 및 긴 대화나 문서 처리에 대한 효과적인 관리와 같은 기술 및 도구를 포함합니다.


초점: SemanticKernel의 주요 목표는 모델이 깊은 의미론적 이해와 뉘앙스 있는 언어 생성을 요구하는 작업에서 더 효과적이도록 언어 모델의 기본 언어 이해 및 처리 능력을 향상시키는 것입니다.

 


(3) LongChain과 Semantic Kernel의 보완적 관계


보완적 성격: LangChain과 Semantic Kernel은 서로 보완적입니다. LangChain은 외부 도구와 멀티스텝 추론을 활용하여 복잡한 작업을 수행할 수 있는 응용 프로그램을 구축하는 데 관한 것이며, 반면 Semantic Kernel은 언어 모델의 의미론적 처리 능력을 향상시키는 데 중점을 둡니다.


통합 가능성: 실제 응용 프로그램에서 Semantic Kernel을 사용하여 LangChain 프레임워크 내의 모델의 언어 이해 및 생성 능력을 향상시킬 수 있습니다. 이 통합은 LangChain의 고급 응용 프로그램 구축 기능과 Semantic Kernel의 향상된 의미론적 처리 능력을 결합합니다.

 


이 두 프레임워크는 언어 모델 기능을 향상시키는 다른 레이어를 나타냅니다. LangChain은 응용 프로그램 및 워크플로우 영역에서, Semantic Kernel은 핵심 언어 처리 및 이해 영역에서 작동합니다. 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요! 

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 PyTorch에서 모델을 저장하고 다시 로딩하는 3가지 방법을 소개하겠습니다. 

 

(1) 모델 가중치(model weights) 만 저장하고, 다시 로딩하기

(2) 모델 전체 (model structure & weights) 를 저장하고, 다시 로딩하기

(3) General Checkpoint 를 저장하고, 다시 로딩하기

 

위 3가지 방법 중에서 하고자 하는 과업의 상황에 맞게 선택해서 사용하면 되겠습니다. 

 

 

PyTorch : Saving and Loading the model

 

 

(1) 모델 가중치(model weights) 만 저장하고, 다시 로딩하기

 

PyTorch 의 internal state dictionary (state_dict)에 있는 학습된 파라미터의 가중치(weights)를 torch.save(model.state_dict(), 'model_weights.pth') 메소드를 사용해서 저장할 수 있습니다. 

 

import torch
import torchvision.models as models


## Saving and Loading model weights
# : PyTorch models store the learned parameters in an internal state dictionary
# : called 'state_dict'
# : These can be persisted via the 'torch.save' method.

model = models.vgg16(weights='IMAGENET1K_V1')
torch.save(model.state_dict(), 'model_weights.pth')

 

 

 

저장된 파라미터별 가중치를 다시 로딩하서 사용하기 위해서는

먼저, 가중치를 저장했을 때와 동일한 모델의 인스턴스를 생성해 놓고,

==> 그 다음에 load_state_dict() 메소드를 사용해서 가중치를 로딩합니다. 

(비유하자면, 철근으로 집의 골격을 먼저 세워 놓고, ==> 그 다음에 시멘트를 붇기)

 

# to load model weights, we need to create an instance of the same model first
# and then load the parameters using 'load_state_dict()' method.

model = models.vgg16() # create untrained model
model.load_state_dict(torch.load('model_weights.pth'))
model.eval() # to set the dropout and batch normalization layers to evaluation mode

 

 

 

(2) 모델 전체 (model structure & weights) 를 저장하고, 다시 로딩하기

 

다음 방법으로는 모델의 구조(model structure, shape)과 데이터로 부터 학습된 파라미터별 가중치(model weights)를 model.save() 메소드를 사용해서 한꺼번에 저장하는 방법입니다. 

 

## Saving the model with shape and weights
torch.save(model, 'model.pth')

 

 

모델의 구조와 가중치가 한꺼번에 저장이 되었기 때문에, 로딩할 때도 torch.load() 메소드로 한꺼번에 모델 구조와 가중치를 가져와서 사용하면 됩니다.

(* (1)번과는 달리, 모델의 구조에 해당하는 인스턴스 생성 절차 없음)

 

# loading the model with shape and weights
model = torch.load('model.pth')

 

 

 

(3) General Checkpoint 를 저장하고, 다시 로딩하기

 

여러번의 Epoch를 반복하면서 모델을 훈련하다가, 중간에 그때까지 학습된 모델과 기타 모델 학습 관련된 정보를 직렬화된 딕셔너리(serialized dictionary)로 저장하고, 다시 불러와서 사용할 수도 있습니다. 

 

예를 들어보자면, 먼저 아래처럼 먼저 신경망 클래스를 정의하고 초기화해서 인스턴스를 생성하겠습니다. 

 

import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F


# Define and initialize the neural network
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(kernel_size=2, stride=2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16*5*5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16*5*5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)

        return x

net = Net()
print(net)
# Net(
#   (conv1): Conv2d(3, 6, kernel_size=(5, 5), stride=(1, 1))
#   (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
#   (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
#   (fc1): Linear(in_features=400, out_features=120, bias=True)
#   (fc2): Linear(in_features=120, out_features=84, bias=True)
#   (fc3): Linear(in_features=84, out_features=10, bias=True)
# )

 

 

Optimizer 를 초기화하고, 모델 학습과 관련된 추가 정보로 EPOCH, PATH, LOSS 등도 정의를 해준 후에 torch.save() 메소드에 Dictionary 형태로 저장하고자 하는 항목들을 key: value 에 맞춰서 하나씩 정의해주어서 저장을 합니다. 

 

# initialize the optimizer
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

# save the general checkpoint
# : collect all relevant information and build your dictionary.
# additional information
EPOCH = 5
PATH = "model.pt"
LOSS = 0.4

torch.save({
    'epoch': EPOCH,
    'model_state_dict': net.state_dict(),
    'optimizer_state_dict': optimizer.state_dict(),
    'loss': LOSS,
}, PATH)

 

 

위에서 처럼 serialized dictionary 형태로 저장된 General Checkpoint 를 다시 로딩해서 사용할 때는, 

먼저 저장했을 때와 동일한 model, optimizer 의 인스턴스를 초기화해서 생성해 놓고, 

--> torch.load(PATH) 메소드를 사용해서 checkpoint 를 로딩하고 

--> load_state_dict() 메소드를 사용해서 로딩한 checkpoint 로부터 저장되어있던 model과 optimizer를 가져와서 사용합니다. 

 

모델 평가나 예측(inference) 용도로 사용할 때는 반드시 model.eval() 를 사용해서 'evaluation model'로 설정해주어야 합니다. 그렇지 않으면 예측을 할 때마다 dropout 과 batch normalization이 바뀌어서 예측값이 바뀌게 됩니다. 

 

만약 모델 훈련을 계속 이어서 하고자 한다면, model.train() 을 사용해서 'training model'로 설정해서 데이터로 부터 계속 Epoch을 반복하면서 학습을 통해 파라미터의 가중치를 업데이트 해주면 됩니다. 

 

## Load the general checkpoint
# : first initialize the model and optimier, then load the dictionary locally.
model = Net()
optimizer =  optim.SGD(model.parameters(), lr=0.001, momentum=0.9) # initialization first

checkpoint = torch.load(PATH)
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
epoch = checkpoint['epoch']
loss = checkpoint['loss']

model.eval() #  to set dropout and batch normalization layers to evaluation mode before running inference.

 

 

 

[Reference]

(1) PyTorch tutorial - Save and Load the Model

: https://pytorch.org/tutorials/beginner/basics/saveloadrun_tutorial.html

(2) PyTorch tutorial - Saving and Loading a General Checkpoint in PyTorch

: https://pytorch.org/tutorials/beginner/basics/saveloadrun_tutorial.html

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

PyTorch 는 이미지, 텍스트, 오디오에 대한 데이터셋과 DataLoader 를 제공합니다. 

 

(1) torch.utils.data.Dataset: train, test 샘플 데이터와 label 을 저장해 놓은 데이터셋

(2) torch.utils.data.DataLoader: 샘플 데이터에 쉽게 접근 가능하도록 해주는 iterable 을 제공. 

 

 

PyTorch에서 Dataset을 로딩할 때 매개변수는 다음과 같습니다. 

 

- root : train/test 데이터가 저장될 경로
- train : train/test 데이터셋 여부 설정. True 이면 training set, False 이면 test set
- download=True : root 에서 데이터가 사용가능하지 않으면 인터넷에서 데이터를 다운로드함
- transform : Feature 를 변환하는 함수 지정
- target_transform : Label 을 변환하는 함수 지정

 

 

이번 포스팅에서는 torchvision.datasets 에서 FashionMNIST 이미지 데이터셋을 로딩하고 변환 (transformation)하는 예를 들어보겠습니다. 

 

 

1. FashionMNIST 데이터셋 가져와서 변환하기

 

torchvision.datasets.FashionMNIST() 메소드를 사용해서 train, test 데이터셋을 다운로드하여 가져옵니다.

 

이때, 모델 훈련에 적합한 형태가 되도록 Feature와 Label 을 변환할 필요가 생길 수 있는데요, PyTorch에서는 Transforms 를 사용하여 변환을 합니다. Dataset 을 가져올 때 사용자 정의 함수를 정의해서 transfrom, target_transform 매개변수에 넣어주여 Feature와 Lable 을 모델 훈련에 맞게 변환해줍니다. 

 

- Feature 변환: 이미지를 (224, 224) 크기로 조정하고, PyTorch tensor로 변환

- Label 변환: integer list 로 되어있는 것을 one-hot encoding 변환

 

 

transform, target_transform 매개변수 자리에는 Lambda 를 사용해서 사용자 정의 함수를 바로 넣어줘도 됩니다. 

 

## Loading and Transforming Image Dataset
import torch
from torchvision import datasets
import torchvision.transforms as transforms

# Define transformations for the image
image_transforms = transforms.Compose([
    transforms.Resize((224, 224)),  # Resize the image to 224x224 pixels
    transforms.ToTensor(),          # Convert the image to a PyTorch tensor
])

# Define transformations for the target (e.g., converting to a one-hot encoded tensor)
def target_transform(target):
    return torch.eye(10)[target]  # there are 10 classes
    
    
# Load the dataset with transformations
training_data = datasets.FashionMNIST(
    root='data',
    train=True, # training set
    download=True, 
    transform=image_transforms,         # (1) specify how the input data should be preprocessed
    target_transform=target_transform # (2) specifies how the labels should be converted
)

trest_data = datasets.FashionMNIST(
    root='data',
    train=False, # test set
    download=True, 
    transform=image_transforms,         # (1) specify how the input data should be preprocessed
    target_transform=target_transform # (2) specifies how the labels should be converted
)


print(f"Image tensor:\n {training_data[0][0]}")
print("-----------" * 5)
print(f"Label: \n {training_data[0][1]}")

# Image tensor:
#  tensor([[[0., 0., 0.,  ..., 0., 0., 0.],
#          [0., 0., 0.,  ..., 0., 0., 0.],
#          [0., 0., 0.,  ..., 0., 0., 0.],
#          ...,
#          [0., 0., 0.,  ..., 0., 0., 0.],
#          [0., 0., 0.,  ..., 0., 0., 0.],
#          [0., 0., 0.,  ..., 0., 0., 0.]]])
# -------------------------------------------------------
# Label: 
#  tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 1.])

 

 

 

FashionMNIST 데이터셋에서 무작위로 이미지를 9개 가져와서 시각화해보면 아래와 같습니다. 

 

## Iterating and Visualizing the Dataset
import matplotlib.pyplot as plt

labels_map = {
    0: "T-Shirt",
    1: "Trouser",
    2: "Pullover",
    3: "Dress",
    4: "Coat",
    5: "Sandal",
    6: "Shirt",
    7: "Sneaker",
    8: "Bag",
    9: "Ankle Boot",
}

figure = plt.figure(figsize=(8, 8))
cols, rows = 3, 3

for i in range(1, cols * rows + 1):
    sample_idx = torch.randint(len(training_data), size=(1,)).item()
    img, label = training_data[sample_idx]
    figure.add_subplot(rows, cols, i)
    plt.title(labels_map[torch.argmax(label).item()])
    plt.axis("off")
    plt.imshow(img.squeeze(), cmap="gray")
plt.show()

 

FashionMNIST

 

 

 

 

2. DataLoader 를 사용해서 Iterate 하기

 

모델 학습을 할 때 보통 mini-batch 만큼의 데이터셋을 가져와서 iteration을 돌면서 학습을 진행합니다. 이때 batch_size 만큼 mini-batch 만큼 데이터셋 가져오기, shuffle=True 를 설정해서 무작위로 데이터 가져오기 설정을 할 수 있습니다.  

 

## Preparing your data for training with DataLoaders
from torch.utils.data import DataLoader

train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True)
test_dataloader = DataLoader(test_data, batch_size=64, shuffle=True)


# Display image and label.
train_features, train_labels = next(iter(train_dataloader))
print(f"Feature batch shape: {train_features.size()}")
print(f"Labels batch shape: {train_labels.size()}")
img = train_features[0].squeeze()
label = train_labels[0]
plt.imshow(img, cmap="gray")
plt.show()
print(f"Label: {label}")

# Feature batch shape: torch.Size([64, 1, 28, 28])
# Labels batch shape: torch.Size([64])
# Label: 9

 

 

[Reference]

(1) PyTorch Datasets & DataLoaders
: https://pytorch.org/tutorials/beginner/basics/data_tutorial.html

 

Datasets & DataLoaders — PyTorch Tutorials 2.2.0+cu121 documentation

Note Click here to download the full example code Learn the Basics || Quickstart || Tensors || Datasets & DataLoaders || Transforms || Build Model || Autograd || Optimization || Save & Load Model Datasets & DataLoaders Code for processing data samples can

pytorch.org

 

(2) PyTorch Transforms
: https://pytorch.org/tutorials/beginner/basics/transforms_tutorial.html

 

Transforms — PyTorch Tutorials 2.2.0+cu121 documentation

Note Click here to download the full example code Learn the Basics || Quickstart || Tensors || Datasets & DataLoaders || Transforms || Build Model || Autograd || Optimization || Save & Load Model Transforms Data does not always come in its final processed

pytorch.org

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요! :-)

 

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 PyTorch 를 사용해서 두 개 Tensor 에 대해 

 

(1) 원소 간 곱 (Element-wise Product)

(2) 행렬 곱 (Matrix Multiplication) 

 

하는 방법을 비교해서 소개하겠습니다. 

 

 

PyTorch: 원소 간 곱 (element-wise product) vs. 행렬 곱 (matrix multiplication)

 

 

먼저, 간단한 예제로 torch.size([2, 2]) 형태를 가지는 x, y 두 개의 Tensor 객체를 만들어보겠습니다. 

그리고 shape, dtype (data type), 저장되어 있는 device 를 확인해보겠습니다. 

 

import torch

x = torch.tensor([[1, 2], [3, 4]])
y = torch.tensor([[0, 10], [20, 30]])

print(f"Tensor x: \n {x}")
print(f"Shape of tensor x: {x.shape}")
print(f"Datatype of tensor x: {x.dtype}")
print(f"Device tensor x is stored: {x.device}")
print("----" * 15)
print(f"Tensor y: \n {y}")
print(f"Shape of tensor y: {y.shape}")
print(f"Datatype of tensor y: {y.dtype}")
print(f"Device tensor y is stored: {y.device}")

# Tensor x: 
#  tensor([[1, 2],
#          [3, 4]])
# Shape of tensor x: torch.Size([2, 2])
# Datatype of tensor x: torch.int64
# Device tensor x is stored: cpu
# ------------------------------------------------------------
# Tensor y: 
#  tensor([[ 0, 10],
#          [20, 30]])
# Shape of tensor y: torch.Size([2, 2])
# Datatype of tensor y: torch.int64
# Device tensor y is stored: cpu

 

 

 

(1) 원소 간 곱 (Element-wise Product)

 

원소 간 곱은 Tensor 내 같은 위치에 있는 원소 간 산술 곱을 해줍니다. 

 

# (1) Element-wise product
# *, mul() is for element-wise multiplication, 
# where each element in the resulting tensor is the product 
# of the corresponding elements in the input tensors.

x * y
# tensor([[  0,  20],
#            [ 60, 120]])


x.mul(y)
# tensor([[  0,  20],
#            [ 60, 120]])

 

 

 

 

(2) 행렬 곱 (Matrix Multiplication)

 

행렬 곱은 선형대수 (Linear Algebra) 의 규칙을 따라서 행렬 곱을 해줍니다. (위의 예제 풀이 그림 참조) 

 

# (2) Matrix Multiplication
# @, matmul() is for matrix or batched matrix multiplication, 
# following the rules of linear algebra. 

x @ y
# tensor([[ 40,  70],
#            [ 80, 150]])


x.matmul(y)
# tensor([[ 40,  70],
#            [ 80, 150]])

 

 

 

행렬 곱을 할 때 두 Tensor 간 shape 에 대해서 조심해야 할 것이 있습니다. 

만약 첫번 째 행렬의 열의 개수와 두번 째 행렬의 행의 개수가 서로 일치하지 않은면 RuntimeError 가 발생합니다. 

 

z = torch.tensor([[5, 6], [7, 8], [9, 10]])
print(f"Shape of tensor z: {z.shape}")
# Shape of tensor z: torch.Size([3, 2])

# RuntimeError: mat1 and mat2 shapes cannot be multiplied (2x2 and 3x2)
# due to a matrix multiplication between two matrices with shapes that are incompatible 
x.matmul(z) # RuntimeError

# ---------------------------------------------------------------------------
# RuntimeError                              Traceback (most recent call last)
# <ipython-input-42-0a2708938308> in <cell line: 2>()
#       1 # RuntimeError: mat1 and mat2 shapes cannot be multiplied (2x2 and 3x2)
# ----> 2 x.matmul(z)

# RuntimeError: mat1 and mat2 shapes cannot be multiplied (2x2 and 3x2)

 

 

 

따라서, 두 Tensor 간 행렬 곱을 할 때는 첫번 째 행렬의 열과 두번 째 행렬의 행의 개수를 일치시켜야 합니다. 

 

행렬 A 가 (m x n), 행렬 B 가 (n x p) 의 형태를 가지고 있다면 행렬 A와 행렬 B 간의 행렬 곱 (matrix multiplication of A and B) 은 (m x p) 형태의 행렬 곱 결과를 반환합니다. 

 

# In matrix multiplication, 
# the number of columns in the first matrix 
# must equal the number of rows in the second matrix. 
# In other words, if the shape of the first matrix is (m x n), 
# the shape of the second matrix must be (n x p) in order to multiply them, 
# resulting in a new matrix of shape (m x p).

A = torch.randn(2, 3)
B = torch.randn(3, 2)
C = torch.matmul(A, B)  # This will result in a matrix of shape 2x2

print(f"Shape of tensor C: {C.shape}")
# Shape of tensor C: torch.Size([2, 2])

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요. :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 심층 신경망을 훈련하는 과정에서 발생하는 문제 중의 하나로 기울기 소실 문제 (Vanishing Gradient Problem) 에 대해서 다루어보겠습니다. 

 

1. 기울기 소실 문제은 무엇인가? 

   (What is the Vanishing Gradient Problem?)

2. 기울기 소실 문제에 기여하는 주요 요인은 무엇인가? 

   (What are the key factors contributing to the vanishing gradient problem?)

3. 기울기 소실 문제를 완화할 수 있는 방법은 무엇인가? 

   (How to mitigate the vanishing gradient problem?)

 

 

 

1. 기울기 소실 문제은 무엇인가? 
(What is the Vanishing Gradient Problem?)

 

기울기 소실 문제 (Vanishing Gradient Problem)은 특히 많은 층을 갖는 심층 신경망을 훈련하는 과정에서 발생하는 어려움입니다. 이는 훈련 과정 중에 네트워크를 통해 그래디언트가 역전파될 때 손실 함수에 대한 가중치와 편향의 그래디언트가 극도로 작아지는 문제를 나타냅니다. 그래디언트가 거의 0에 가까워지면 네트워크의 가중치가 효과적으로 업데이트되지 않을 수 있으며, 네트워크는 데이터에서 학습하는 데 어려움을 겪을 수 있습니다. 


기울기 소실 문제 (Vanishing Gradient Problem)는 특히 순환 신경망 (RNN)이나 심층 피드포워드 신경망(Deep Feedforward Neural Network)과 같은 심층 아키텍처에서 두드러지며, 오류의 역전파는 각 층의 업데이트에서 많은 작은 그래디언트 값을 서로 곱함으로써 기울기 소실로 이어집니다. 이 작은 값들의 곱셈은 기울기 소실을 초래하여 전체적으로 소실된 그래디언트를 초래할 수 있습니다.

 

vanishing gradient problem intuition, 기울기 소실 문제

 

 

 

 

2. 기울기 소실 문제에 기여하는 주요 요인은 무엇인가? 
(What are the key factors contributing to the vanishing gradient problem?)


(1) 활성화 함수 (Activation Functions)


일부 활성화 함수, 특히 시그모이드(Sigmoid) 또는 쌍곡선 탄젠트 (Hyperbolic tangent, tanh)와 같은 함수는 극단적인 입력 값에 대해 포화됩니다. 이러한 영역에서 그래디언트는 거의 0에 가까워져 네트워크가 유용한 그래디언트를 역방향으로 전파하는 것을 어렵게 만듭니다.

 

vanishing gradient problem - sigmoid activation funciton

 

 


(2) 네트워크의 깊이 (Depth of the network)


신경망의 층 수가 증가함에 따라 소실 그래디언트를 만날 확률도 높아집니다. 그래디언트는 여러 층을 통해 역전파되어야 하며, 각 층은 그래디언트가 소실되기에 추가 기회를 제공합니다.

 


(3) 가중치 초기화 (Weight Initialization)


가중치 초기화를 잘못 선택하면 기울기 소실 문제가 악화될 수 있습니다. 가중치가 너무 작게 초기화되면 그래디언트가 층을 통과할 때 소실될 수 있습니다. 

 


(4) 네트워크 아키텍처 (Network Architecture)


순환 신경망(RNN)이나 심층 신경망(Deep Neural Network)과 같이 특정 아키텍처 선택은 소실 그래디언트 문제를 강화할 수 있습니다.

 

 



3. 기울기 소실 문제를 완화할 수 있는 방법은 무엇인가? 
(How to mitigate the vanishing gradient problem?)


(1) 활성화 함수 (Activation Function)


해당 입력 범위에서 포화 문제가 없는 활성화 함수를 사용하십시오. ReLU(Rectified Linear Unit)는 양수 입력 값에 대해 포화되지 않는 일반적으로 사용되는 활성화 함수입니다. 

 

## Use Activation Functions That Do Not Saturate like ReLU, using PyTorch

# define a simple nueral network
class SimpleNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(SimpleNN, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.relu = nn.ReLU() # Rectified Linear Unit Activation Function
        self.fc2 = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        x = self.fc1(x)
        x = self.relu(x)
        x = self.fc2(x)

        return x

# Usage
model = SimpleNN()

 



(2) 배치 정규화 (Batch Normalization)


배치 정규화는 미니 배치의 평균을 빼고 표준 편차로 나누어 신경망 층의 입력을 정규화함으로써 소실 그래디언트 문제를 완화합니다. 다음은 배치 정규화가 소실 그래디언트 문제에 대처하는 데 도움이 되는 방법입니다.

 


정규화 (Normailzation): 배치 정규화는 미니 배치의 평균을 빼고 표준 편차로 나누어 층의 입력을 정규화합니다. 이로써 다음 층의 입력이 대략적으로 0 주변에 집중되고 표준 편차가 약 1이 되도록 보장됩니다.


활성화 함수 안정화(Stablizing Activation Functions): 시그모이드 또는 tanh와 같은 특정 활성화 함수는 입력이 너무 크거나 작을 때 포화됩니다. 배치 정규화는 입력을 정규화하여 입력이 안정된 범위에 유지되도록 돕습니다. 이로써 기울기 소실 문제의 일반적인 원인인 활성화 함수의 포화를 방지합니다.


내부 공변량 변화 감소 (Reducing Internal Covariate Shift): 배치 정규화는 훈련 중에 층의 입력 분포가 변경되는 내부 공변량 변화를 감소시킵니다. 입력을 정규화함으로써 배치 정규화는 입력 분포의 변화를 완화시켜 최적화 과정을 안정화시킵니다. 


더 높은 학습률 허용 (Allowing Higher Learning Rates): 배치 정규화는 더 높은 학습률을 사용할 수 있게 합니다. 높은 학습률은 최적화 알고리즘이 빠르게 수렴하는 데 도움을 줄 수 있습니다. 배치 정규화 없이 높은 학습률을 사용하면 소실 그래디언트 문제로 인해 발산하거나 수렴이 느려질 수 있습니다. 


가중치 초기화에 대한 독립성 (Independence from Weight Initialization): 배치 정규화는 가중치 초기화 선택에 대한 민감성을 줄입니다. 배치 정규화가 없으면 나쁜 가중치 초기화가 소실 그래디언트 문제를 악화시킬 수 있습니다. 배치 정규화는 학습 동적을 초기화로부터 분리하여 훈련을 더 견고하게 만듭니다. 


이러한 문제들을 해결함으로써 배치 정규화는 특히 기울기 소실 문제가 두드러지는 깊은 아키텍처에서 효과적으로 학습하도록 도와줍니다. 

 

## Batch Normalization using PyTorch

## Batch Normalization
import torch
import torch.nn as nn

class SimpleNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(YourModel, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.bn1 = nn.BatchNorm1d(hidden_size) # Batch Normalization
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        x = self.fc1(x)
        x = self.bn1(x) # Batch Normalization
        x = self.relu(x)
        x= self.fc2(x)

        return x

# Usage
model = SimpleNN()

 

 


(3) 가중치 초기화 (Weight Initialization)


신호 전파를 촉진하는 방식으로 초기 가중치를 설정하는 적절한 가중치 초기화 기술을 사용하십시오. 예를 들면, Xavier/ Glorot 초기화가 있습니다. 

 

## Xavier Initialization using PyTorch

import torch.nn.init as init

class YourModel(nn.Module):
    def __init__(self):
        super(YourModel, self).__init__()
        self.fc1 = nn.Linear(in_features, out_features)
        # Initialize weights using Xavier/Glorot initialization
        init.xavier_uniform_(self.fc1.weight)

# Usage
model = YourModel()

 


(4) 스킵 연결 (Skip Connections)


심층 신경망에서 스킵 연결(Skip Connections) 또는 잔차 연결(Residual Connections)은 정보를 층 간에 직접 흐르게 해 소실 그래디언트를 완화하는 데 도움이 됩니다. ResNet과 같은 아키텍처에서 사용됩니다. 

 


기울기 소실 문제를 해결하는 것은 심층 신경망을 효과적으로 훈련시키기 위해 중요하며, 아키텍처 선택 및 다양한 기술의 조합이 그 영향을 완화하는 데 도움이 될 수 있습니다. 

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요.  :-) 

 

728x90
반응형
Posted by Rfriend
,

인공신경망이 인풋 데이터와 정답(label) 데이터를 가지고 학습을 한다고 했을 때, 신경망의 내부에서 무슨 일이 일어나고 있는지, 기계가 데이터를 가지고 학습을 하는 원리가 무엇인지 궁금하실거예요. 이번 포스팅은 바로 이 마법에 대한 소개 글입니다. 

 

"오차역전파(Backpropagation)"은 "오차를 역방향으로 전파하는 방법 (backward propagation of errors)" 의 줄임말로서, 인공 신경망을 훈련시키기 위한 지도 학습 알고리즘으로 자주 사용됩니다. 이는 신경망이 오류에서 학습하고 시간이 지남에 따라 성능을 향상시킬 수 있도록 하는 훈련 과정에서 중요한 구성 요소입니다. 


다음은 오차역전파 알고리즘이 학습하는 단계입니다. 

 

1. 초기화 (Initialization)

2. 순방향 전파 (Forward Pass)

3. 오류 계산 (Calculate Error) 

4. 역방향 전파 (Backward Pass, Backpropagation) 

5. 경사 하강법 (Gradient Descent)

 

 

[ 손실함수와 경사하강법에 의한 최적화를 통한 모델 훈련 ]

model training using loss function and optimization by gradient descent

 

 

 

1. 초기화 (Initialization)

 

신경망에서 초기화(Initialization)는 훈련이 시작되기 전에 네트워크의 가중치(Weights)와 편향(Biases)을 설정하는 과정을 나타냅니다. 올바른 초기화는 신경망의 성공적인 훈련에 중요하며 모델의 수렴 및 성능에 큰 영향을 미칠 수 있습니다.

신경망을 만들 때 그 가중치 및 편향을 일정한 값으로 초기화합니다. 이러한 초기 값의 선택은 네트워크가 얼마나 빨리 학습하며 올바른 해법으로 수렴하는지에 영향을 줄 수 있습니다. 부적절한 초기화는 수렴이 느리게 되거나 그라디언트가 소멸하거나 폭발하는 등의 문제를 초래할 수 있습니다.

가중치 초기화에는 여러 기법이 있으며, 일반적으로 사용되는 몇 가지 방법에는 다음이 포함됩니다.


(1) 랜덤 초기화(Random Initialization): 가중치에 무작위 값 할당하기. 이는 흔한 접근법으로, 가우시안(정규) 분포 또는 균일 분포를 사용하여 무작위 값 생성하는 방법을 사용할 수 있습니다. 


(2) 자비에/글로럿 초기화(Xavier/Glorot Initialization): 이 방법은 시그모이드 및 쌍곡선 탄젠트(tanh) 활성화 함수와 잘 작동하도록 설계되었습니다. 가중치는 평균이 0이고 분산이 (1/ 입력 뉴런 수) 인 가우시안 분포에서 값들을 추출하여 초기화됩니다. 

 

(3) He 초기화(He Initialization): "ReLU 초기화"로도 알려져 있으며, ReLU 활성화 함수를 사용하는 네트워크에 적합합니다. 가중치는 평균이 0이고 분산이 (2/ 입력 뉴런 수) 인 가우시안 분포에서 값들을 추출하여 초기화됩니다. 


적절한 초기화 방법을 선택하는 것은 사용된 활성화 함수에 따라 다르며, 신경망을 효과적으로 훈련시키기 위한 중요한 측면입니다. 적절한 초기화 기술을 사용하면 훈련 중 발생하는 일반적인 문제를 완화하고 더 안정적이고 빠른 수렴에 기여할 수 있습니다. 

 

 

 

2. 순방향 전파 (Forward Pass)

 

신경망에서의 순전파(forward pass)는 입력 데이터를 네트워크를 통과시켜 출력을 생성하는 과정을 나타냅니다. 순전파 중에는 입력 데이터가 신경망의 각 층을 통과하고, 각각의 층에서 변환을 거쳐 최종 출력을 생성합니다. 


순전파의 단계를 설명하면 다음과 같습니다. 


(1) 입력 층(Input Layer): 데이터 입력, 즉 데이터 세트의 특징들이 신경망의 입력 층에 공급됩니다. 


(2) 가중치와 편향(Weights and Biases): 인접한 층 사이의 각 뉴런 간에는 가중치가 할당됩니다. 이러한 가중치는 연결의 강도를 나타냅니다. 또한 각 뉴런은 편향(bias) 항을 갖습니다. 입력 데이터는 가중치로 곱해지고 편향이 결과에 더해집니다. 


(3) 활성화 함수(Activation Function): 입력과 편향의 가중 합은 활성화 함수를 통과합니다. 이는 신경망에 비선형성을 도입하여 데이터의 복잡한 관계를 학습할 수 있도록 합니다. 일반적인 활성화 함수로는 ReLU(렐루), 시그모이드, tanh 등이 있습니다. 


(4) 출력(Output): 활성화 함수의 결과는 해당 층의 출력이 되고, 이는 네트워크의 다음 층에 대한 입력으로 전달됩니다. 


(5) 과정 반복(Repeating the process): 2~4단계는 각각의 이전 층에 대해 반복되어 네트워크의 모든 층을 통과합니다. 최종 출력은 일반적으로 출력 층(Output Layer)에서 생성됩니다. 


순전파는 예측 단계에서 정보가 네트워크를 통과하는 방식입니다. 네트워크는 학습된 가중치와 편향을 사용하여 입력 데이터를 의미 있는 예측으로 변환합니다. 순전파의 출력은 실제 대상 값과 비교되며, 이 오차는 역전파(backpropagation) 단계에서 가중치와 편향을 업데이트하는 데 사용되어 네트워크가 시간이 지남에 따라 학습하고 성능을 향상시킬 수 있도록 합니다. 

 

Feedforward Neural Network

 

 

3. 오류 계산 (Calculate Error) 

 

신경망의 출력은 손실 함수(loss function)를 사용하여 실제 목표 값(실제 값)과 비교됩니다. 이는 예측 값과 실제 출력 간의 차이를 측정합니다. 이 손실 함수를 최소화하는 것이 목표로, 이는 네트워크의 예측이 가능한 한 실제 값에 가까워지도록 합니다. 

 

다양한 유형의 손실 함수가 있으며, 사용할 적절한 손실 함수의 선택은 해결하려는 문제의 성격에 따라 다릅니다. 일반적인 손실 함수에는 다음이 포함됩니다. 


(1) 평균 제곱 오차 (Mean Squared Error, MSE): 회귀 문제(Regression problem)에 적합하며, 예측된 값과 실제 값 간의 평균 제곱 차이를 계산합니다. 


(2) 이진 교차 엔트로피 손실 (Binary Cross-Entropy Loss): 이진 분류 문제(Binary classification problem)에 사용되며, 예측된 확률 분포와 실제 분포 간의 불일치를 측정합니다. 


(3) 다중 클래스 교차 엔트로피 손실 (Categorical Cross-Entropy Loss): 다중 클래스 분류 문제(Multi-class classification problem)에 적합하며, 각 클래스에 대한 예측된 확률 분포와 실제 분포 간의 불일치를 측정합니다. 


(4) 힌지 손실 (Hinge Loss): 서포트 벡터 머신(Support Vector Machine, SVM)에서 흔히 사용되며, 일부 분류 작업에서도 사용됩니다.

 

손실 함수의 선택은 학습 과정에 영향을 미치며, 주어진 작업의 특수한 요구사항과 특성과 일치하는 손실 함수를 선택하는 것이 중요합니다. 최적화 알고리즘은 훈련 중에 계산된 손실을 기반으로 모델 매개변수를 조정하여 시간이 지남에 따라 모델의 성능을 향상시키려고 노력합니다. 

 

 

4. 역방향 전파 (Backward Pass, Backpropagation)

 

역전파(Backward Pass, Backpropagation)는 신경망을 훈련시키는 중요한 단계입니다. 입력 데이터가 네트워크를 통과하여 예측을 생성하는 순전파 이후에는 역전파를 사용하여 계산된 손실에 기초해 모델의 매개변수(가중치와 편향)를 업데이트합니다. 이 과정의 목표는 예측된 출력과 실제 목표 값 사이의 차이를 최소화하는 것입니다.


아래는 역전파 오류 전파 과정의 주요 단계입니다.  


(1) 출력에 대한 손실의 그래디언트 계산 (Compute Gradient of the Loss with Respect to the Output) : 손실 함수의 그래디언트를 모델의 출력에 대해 계산합니다. 이 단계에서는 출력이 조정될 경우 손실이 얼마나 변하는지를 결정합니다. 

 

(2) 네트워크를 통한 그래디언트 역전파 (Compute Gradient of the Loss with Respect to the Output) : 그래디언트를 네트워크의 층을 통해 역방향으로 전파합니다. 이는 미적분의 연쇄 법칙을 적용하여 각 층의 가중치와 편향에 대한 손실의 그래디언트를 계산하는 것을 포함합니다. 

 

(3) 가중치와 편향 업데이트 (Update Weights and Biases) : 계산된 그래디언트를 사용하여 네트워크의 가중치와 편향을 업데이트합니다. 최적화 알고리즘인 확률적 경사 하강법(SGD) 또는 그 변형 중 하나는 손실을 줄이는 방향으로 매개변수를 조정합니다. 

 

(4) 다중 반복에 대해 반복 (Repeat for Multiple Iterations (Epochs)) : 단계 1-3은 여러 번의 반복 또는 에폭에 대해 반복되며 각 반복에서는 훈련 샘플의 배치가 처리됩니다. 이 반복적인 프로세스를 통해 네트워크는 매개변수를 조정하여 시간이 지남에 따라 성능을 향상시킬 수 있습니다. 

 

 

역전파의 수학적 세부 사항은 편미분(partial derivatives)과 연쇄 법칙(the chain rule)을 포함합니다. 구체적으로 그래디언트는 오류를 층을 통해 역방향으로 전파함으로써 계산되며, 가중치와 편향은 그래디언트의 반대 방향으로 업데이트됩니다.

 

 

[ The Chain Rule]

The Chain Rule

 

 

[ example: Partial Derivative of w1 with respect to E1 ]

Partial Derivative of w1 with respect to Error

 


역전파는 신경망을 훈련시키는 데 기본 개념으로, 네트워크가 실수에서 학습하고 매개변수를 계속해서 조정할 수 있게 합니다. 이 반복적인 과정을 통해 모델은 새로운, 보지 않은 데이터에 대한 예측을 더 잘 수행하게 됩니다.

 

 

5. 경사 하강법 (Gradient Descent)

 

계산된 도함수는 손실을 줄이는 방향으로 가중치와 편향을 업데이트하는 데 사용됩니다.
이를 위해 일반적으로 경사 하강 최적화 알고리즘(Optimization by Gradient-Descent)이 사용되며, 학습률에 의해 스케일링된 기울기의 일부를 빼서 가중치와 편향을 조절합니다. 

경사하강법 ( Gradient Descent )



- Eta (η)는 학습율(the learning rate)로서, 학습율은 기계 학습 및 신경망 훈련에서 사용되는 하이퍼파라미터 중 하나로, 각 훈련 단계에서 모델의 가중치를 조정하는 정도를 나타냅니다. 즉, 학습율은 가중치 업데이트의 크기(step size)를 결정하는 매개변수입니다.


높은 학습율은 각 단계에서 더 큰 가중치 업데이트를 의미하며, 이는 모델이 빠르게 학습할 수 있게 할 수 있습니다. 그러나 너무 높은 학습율은 수렴을 방해하고 발산(explosion)할 수 있기 때문에 조심스럽게 선택되어야 합니다.


낮은 학습율은 더 안정적인 학습을 제공하지만, 수렴하는 데 더 많은 시간이 걸릴 수 있습니다. 또한, 지역 최솟값(local optima)에서 빠져나오기 어려울 수 있습니다.


적절한 학습율을 선택하는 것은 모델의 성능과 수렴 속도에 큰 영향을 미칩니다. 학습율은 하이퍼파라미터 튜닝 과정에서 조정되며, 실험을 통해 최적의 값을 찾는 것이 일반적입니다. 일반적으로는 0.1, 0.01, 0.001과 같은 값을 시도하며 Cross-validation을 통해 모델의 성능을 평가하여 최적의 학습율을 결정합니다.

 

 

다음은 오차역전파법을 설명하기 위해 가상으로 만든, 각 레이어 별로 input size = 3, hidden size 1 = 3, hidden size 2 = 3, output size = 1 의 모델 아키텍처를 가지는 아주 간단한 PyTorch 예시가 되겠습니다.  MSE 를 손실함수로 하고, Adam optimizer를 사용하였습니다. 

 

import torch
import torch.nn as nn
import torch.nn.functional as F

# Define the neural network architecture with hyperparameters
class SimpleNet(nn.Module):
    def __init__(self, input_size, hidden_size_1, hidden_size_2, output_size):
        super(SimpleNet, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size_1)
        self.bn1 = nn.BatchNorm1d(hidden_size_1)
        self.fc2 = nn.Linear(hidden_size_1, hidden_size_2)
        self.bn2 = nn.BatchNorm1d(hidden_size_2)
        self.fc3 = nn.Linear(hidden_size_2, output_size)

    def forward(self, x):
        x = F.relu(self.bn1(self.fc1(x)))
        x = F.relu(self.bn2(self.fc2(x)))
        x = self.fc3(x)

        return x

# Set Hyperparameters
input_size = 3
hidden_size_1 = 6
hidden_size_2 = 3
output_size = 1
epochs = 100
learning_rate = 0.001

# Initiate the network
model = SimpleNet(input_size, hidden_size_1, hidden_size_2, output_size)

print(model)
# SimpleNet(
#   (fc1): Linear(in_features=3, out_features=6, bias=True)
#   (bn1): BatchNorm1d(6, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
#   (fc2): Linear(in_features=6, out_features=3, bias=True)
#   (bn2): BatchNorm1d(3, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
#   (fc3): Linear(in_features=3, out_features=1, bias=True)
# )

 

 

GPU 를 사용할 수 있으면 Device를 "cuda"로, CPU를 사용할 수 있으면 Device를 "cpu"로 설정해줍니다. 아래 예에서는 Tesla V100 16G GPU를 사용하므로 "cuda"로 Device를 설정해주었습니다. 

 

# Check for CUDA availability
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

print(device)
# cuda


#### when you run at Jupyter Notebook, add '!' at the beginning.
! nvidia-smi -L
# GPU 0: Tesla V100-SXM2-16GB (UUID: GPU-d445249d-b63e-5e62-abd1-7646bb409870)

 

 

 

Loss 함수는 MSE (Mean Squared Error) 로 설정하였고, Optimizer는 Adam 으로 설정해주었습니다. 

모델 훈련에 사용할 데이터셋을 난수를 발생시켜서 생성하고, 위에서 지정한 Device 에 모델의 파라미터와 버퍼, 그리고 데이터를 이동시켰습니다 (modeo.to(device), torch.randn((100, input_size)).to(device)). 

 

# Move the model's parameters and buffers to the specified device.
model.to(device)

# Set Loss and Optimizer
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)


# Generate dummy input and target data, move the data to the specified device
inputs = torch.randn((100, input_size)).to(device)
targets = torch.randn((100, output_size)).to(device)

 

 

 

Epochs = 100 만큼 for loop 순환문을 사용해서 반복하면서 모델을 훈련 시킵니다. Forward pass, Calculate loss, Backward pass, Update weights (Optimize) 의 과정을 반복합니다. 

 

# Training phase
model.train() # training mode
for epoch in range(epochs):
    # Forward pass
    output = model(inputs)
    loss = criterion(output, targets) # calculate loss

    # Backward pass and optimize
    optimizer.zero_grad() # clear previous gradients
    loss.backward() # compute gradients, backward pass
    optimizer.step() # update weights

    # Print training statistics
    if (epoch + 1) % 10 == 0:
        print(f'Epoch [{epoch+1}/{epochs}], Loss: {loss.item():.4f}')

# Epoch [10/100], Loss: 0.9778
# Epoch [20/100], Loss: 0.9476
# Epoch [30/100], Loss: 0.9217
# Epoch [40/100], Loss: 0.9000
# Epoch [50/100], Loss: 0.8826
# Epoch [60/100], Loss: 0.8674
# Epoch [70/100], Loss: 0.8534
# Epoch [80/100], Loss: 0.8423
# Epoch [90/100], Loss: 0.8329
# Epoch [100/100], Loss: 0.8229

 

 

위에서 훈련한 모델에 가상으로 만든 1개의 샘플 데이터를 인풋으로 넣어서 예측을 해보겠습니다. 배치가 아니가 단 1개의 샘플 데이터만 사용하기 때문에 model.eval() 을 해주었습니다. (만약 model.eval() 을 추가해주지 않으면, ValueError: Expected more than 1 value per channel when training, got input size torch.Size([1, 2]) 가 발생합니다.)

 

# Example of using the trained model
test_input = torch.randn((1, input_size)).to(device)

model.eval() # evaluation mode
with torch.no_grad():
    predicted_output = model(test_input)

print("Test Input:", test_input)
print("Predicted Output:", predicted_output.item())

# Test Input: tensor([[ 0.3862,  0.1250, -0.3322]], device='cuda:0')
# Predicted Output: -0.00717635452747345

 

 

위에서 학습이 완료된 인공신경망 모델의 레이어 이름과 파라미터별 가중치, 편향에 접근해서 확인해보도록 하겠습니다. 

 

## (1) Access to the names and parameters in the trained model
for name, param in model.named_parameters():
    print(name)
    print(param)
    print("-----" * 15)

# fc1.weight
# Parameter containing:
# tensor([[-0.0542, -0.5121,  0.3793],
#         [-0.5354, -0.1862,  0.4207],
#         [ 0.3339, -0.0813, -0.2598],
#         [ 0.5527, -0.3505, -0.4357],
#         [-0.0351, -0.0992,  0.1252],
#         [-0.2110,  0.0713, -0.2545]], device='cuda:0', requires_grad=True)
# ---------------------------------------------------------------------------
# fc1.bias
# Parameter containing:
# tensor([ 0.5726,  0.1125,  0.4138, -0.1896,  0.1353, -0.0372], device='cuda:0',
#        requires_grad=True)
# ---------------------------------------------------------------------------
# bn1.weight
# Parameter containing:
# tensor([0.9044, 0.9735, 1.0557, 0.9440, 1.0983, 1.0414], device='cuda:0',
#        requires_grad=True)
# ---------------------------------------------------------------------------
# bn1.bias
# Parameter containing:
# tensor([-0.1062,  0.0177,  0.0283, -0.0527,  0.0059, -0.0117], device='cuda:0',
#        requires_grad=True)
# ---------------------------------------------------------------------------
# fc2.weight
# Parameter containing:
# tensor([[-0.3057,  0.0730,  0.2711,  0.0770,  0.0497,  0.4484],
#         [-0.3078,  0.0153,  0.1769,  0.2855, -0.4853,  0.2110],
#         [-0.3342, -0.2749,  0.3440, -0.2844,  0.0763, -0.1959]],
#        device='cuda:0', requires_grad=True)
# ---------------------------------------------------------------------------
# fc2.bias
# Parameter containing:
# tensor([-0.0536, -0.3275,  0.0371], device='cuda:0', requires_grad=True)
# ---------------------------------------------------------------------------
# bn2.weight
# Parameter containing:
# tensor([1.0843, 0.9524, 0.9426], device='cuda:0', requires_grad=True)
# ---------------------------------------------------------------------------
# bn2.bias
# Parameter containing:
# tensor([-0.0564,  0.0576, -0.0848], device='cuda:0', requires_grad=True)
# ---------------------------------------------------------------------------
# fc3.weight
# Parameter containing:
# tensor([[-0.4607,  0.4394, -0.1971]], device='cuda:0', requires_grad=True)
# ---------------------------------------------------------------------------
# fc3.bias
# Parameter containing:
# tensor([-0.0125], device='cuda:0', requires_grad=True)
# ---------------------------------------------------------------------------

 

 

 

## (2) Access to the weights and biases in the trained model
print("[ Model FC1 Weights ]")
print(model.fc1.weight.cpu().detach().numpy())
print("-----" * 10)
print("[ Model FC2 Weights ]")
print(model.fc2.weight.cpu().detach().numpy())

# [ Model FC1 Weights ]
# [[-0.05423066 -0.51214963  0.37927148]
#  [-0.53538287 -0.18618222  0.42071185]
#  [ 0.33391565 -0.08127454 -0.2597795 ]
#  [ 0.55268896 -0.3505293  -0.4356556 ]
#  [-0.03507916 -0.09922788  0.1251672 ]
#  [-0.21096292  0.07126608 -0.25446963]]
# --------------------------------------------------
# [ Model FC2 Weights ]
# [[-0.30573702  0.07304495  0.27107015  0.07700069  0.04968859  0.44840544]
#  [-0.3078182   0.01533379  0.17685111  0.28549835 -0.485255    0.21100621]
#  [-0.33416617 -0.27492833  0.3440289  -0.28436708  0.07630474 -0.19592032]]

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요.  :-)

 

728x90
반응형
Posted by Rfriend
,

기계학습에서 Regularization 은 Penalization, Constraing method, Shrinkage methods 라고도 하는데요, "규제하다", "벌점을 주다", "제약을 가하다", "축소시키다" 등의 억압을 가하는 의미를 가지고 있습니다. 


보통 Regularization을 "정규화"라고 번역하는 경우가 많던데요, Normalization 도 "정규화"라고 번역을 하곤 해서 개념이 서로 혼동되기 쉬운 면이 있어서요, 이번 포스팅에서는 그냥 Regularization 을 그대로 사용하겠습니다. 굳이 번역하라면 "규제화"나 "벌점화", "(가중치) 축소화" 등으로 번역할 수 있을 거 같습니다.

 

이번 포스팅에서는 Regularization 이 무엇이며 왜 사용하는지에 대해 알아보겠습니다. 또한 Regularization 기술 및 그 사용 방법에 대해 소개할 것입니다. 


1. 기계 학습에서의 Regularization 은 무엇이고 왜 사용하는가? 
2. L1 Regularization (LASSO Regression)
3. L2 Regularization (Ridge Regression

4. Cross-validation을 이용한 Tuning Parameter Gamma 최적값 찾기
5. Python을 이용한 LASSO 예제 (w/ Hyper-parameter tuning)

 

 

 

1. 기계 학습에서의 Regularization 은 무엇이고 왜 사용하는가? 

 

Regularization 은 기계 학습에서 과적합(Overfitting)을 방지하기 위한 기술 중 하나로, 모델이 훈련 데이터에 지나치게 적합화되는 것을 막기 위해 사용됩니다. 과적합은 모델이 훈련 데이터의 잡음과 이상치까지 학습하여 새로운, 보지 못한 데이터에 대해 일반화하기 어렵게 만드는 현상입니다. 이러한 상황에서 모델은 훈련 데이터를 외우기보다는 기본적인 패턴을 학습하는 것이 중요합니다.

 

Regularization은 모델이 학습하는 파라미터의 계수 (coefficient estimages) 값에 제약을 가해서 '0'으로 가깝게 축소를 하거나 또는 정확하게 '0'으로 축소를 시킴으로써 계수 추정치의 분산을 줄이고(reducing the variance of the coefficients estimates) 모델의 복잡성을 제어하여 과적합을 피하게 해주는 효과가 있습니다. (대신에 Bias 는 늘어나는 비용을 지불하게 됩니다. 세상에 공짜가 없어요..>.<;). 

 

Regularization = Loss Function + Penalty

 

선형회귀모형을 예로 들어서 설명하면, Regularization 은 Penalty 항을 무엇을 사용하느냐에 따라

(1) L1 Regularization(LASSO), (2) L2 Regularization(Ridge), (3) ElasticNet 으로 구분할 수 있습니다. 

 

Regularization Methods

 

 

Tuning parameter Lambda(λ)는 모델에 적용되는 규제의 전반적인 강도를 제어하는 규제 강도 하이퍼파라미터입니다. 람다(λ) 값이 높아지면 더 강한 패널티가 적용되어 더 희소한 모델과 작은 계수 값이 생성됩니다. 

 

Elastic Net에 있는 Mix ratio 알파(α) (책에 따라 Alpha 대신 Gamma( γ )가 혼용되서 사용됨. Scikit-Learn 모듈에는 알파(α)  로 표기) 는 Elastic Net에서 L1 및 L2 패널티 항의 조합을 결정하는 하이퍼파라미터입니다. 


  - 알파(α)가 0이면 Elastic Net은 Ridge 회귀 (L2 Regularization)와 동일합니다.
  - 알파(α)가 1이면 Elastic Net은 Lasso 회귀 (L1 Regularization)와 동일합니다.
  - 알파(α)가 0과 1 사이의 값을 가지면 Elastic Net은 L1 및 L2 Regularization을 결합합니다.

 

요약하면, Elastic Net은 알파(α) 매개변수를 통해 L1 및 L2 Regularization을 조합하고, Regularization의 전반적인 강도는 람다(λ) 매개변수로 제어됩니다.

 

 


2. L1 Regularization (LASSO Regression*)

 

LASSO 회귀 또는 L1 Regularization은 모델의 목적 함수에 Penalty 항을 통합하는 선형 회귀 방법입니다. LASSO 회귀의 목표는 과적합을 방지하고 모델이 사용하는 특징의 일부만 사용하도록 유도하여 실질적으로 변수 선택(Feature Selection)을 수행하는 것입니다.

표준 선형 회귀 목적 함수는 예측 값과 실제 값 간의 제곱 차이의 합을 최소화하는 것입니다. 그러나 LASSO 회귀에서는 이 목적 함수에 회귀계수의 절대값의 합이 페널티 항으로 추가되어 수정된 목적 함수는 다음과 같습니다. 즉, 기존의 RSS(Residual Sum of Squares) 에 L1 penalty term(회귀계수의 절대값의 합) 이 더해진 손실함수를 최소로 하는 회계계수 Beta 추정치를 찾는 것입니다. 람다(λ)는 규제하는 강도를 조절하는 매개변수입니다. 

 

LASSO objective function

 

 

이 목적함수 식을 선형계획법(Linear Planning) 의 제약조건(constraints, subject to)을 가지는 식으로 변경해서 다시 써보면 아래의 왼쪽 수식과 같습니다. 

 

LASSO regression, L1 penalty

 

 

* Robert Tibshirani, “Regression Shrinkage and Selection via the Lasso” (1996), Journal of the Royal Statistical Society

 

 

페널티 항은 모델에서 일부 특징 가중치를 정확히 0로 축소(shrinks to exactly zero)함으로써 모델 내에서 희소성(sparsity)을 촉진합니다. 이는 모델이 가장 관련성이 높은 특징의 하위 집합을 선택하도록 하고 다른 특징(feature selection)을 무시하도록 만듭니다. 

 

위의 오른쪽 그림은 튜닝 파라미터 람다를 점점 크게 했을 때 변수별 회귀계수의 값이 어느 순간 정확히 0으로 축소되는 것을 알 수 있습니다. 

LASSO 회귀는 특히 많은 특징이 존재하며 일부가 무관하거나 중복될 수 있는 상황에서 유용합니다. 희소성을 촉진함으로써 LASSO는 특징 선택(Feature Selection)을 도와주며 해석 가능하고 잠재적으로 간단한 모델을 만듭니다. 

요약하면, LASSO 회귀는 L1 Regularization을 사용한 선형 회귀 기술로, 과적합을 방지하고 모델의 특징 가중치에 희소성을 촉진하여 더 나은 일반화 성능을 달성합니다.

 


3. L2 Regularization (Ridge Regression)

 

Ridge 회귀 또는 L2 Regularization은 모델의 목적 함수에 패널티 항을 통합하는 선형 회귀 기술입니다. Ridge 회귀의 주요 목적은 선형 회귀 비용 함수의 제곱 계수 합에 페널티 항을 추가함으로써 과적합(Overfitting)을 방지하는 것입니다.

표준 선형 회귀 목적 함수는 예측 값과 실제 값 간의 제곱 차이의 합을 최소화하는 것입니다. 릿지 회귀에서는 이 목적 함수에 페널티 항이 추가되어 수정된 목적은 다음과 같습니다. 

 

Ridge objective function

 

 

위의 목적함수식을 선형계획법의 제약조건을 가지는 식으로 변경하면 아래와 같습니다. 

 

Ridge regression

 

 

페널티 항은 모델에게 계수를 제로에 가깝게 축소하도록 유도하지만 거의 0으로 만들지는 않습니다.(not exactly zero). 이는 모델이 너무 복잡해지고 훈련 데이터에 과적합되는 것을 방지하는 데 도움이 됩니다.

 

릿지 회귀는 특징 간에 다중공선성이 있는 경우에 특히 유용하며, 이는 일부 특징이 높은 상관관계를 가질 때 발생합니다. 이러한 경우 릿지 회귀의 정규화 항은 계수를 안정화하고 큰 가중치 값을 피하는 데 도움이 됩니다. 

요약하면, 릿지 회귀는 L2 Regularization을 사용한 선형 회귀 기술로, 제곱 계수의 합에 페널티 항을 추가함으로써 오버피팅을 방지합니다. 이는 훈련 데이터를 잘 맞추는 동시에 모델을 간단하게 유지하고 더 일반적으로 만들기 위한 균형을 제공합니다. 

 

 

 

4. Cross-validation을 이용한 Tuning Parameter Lambda, Mix ratio Gamma 최적값 찾기

 

Regularization의 강도를 조절하는 튜닝 파라미터 람다(λ)와 Elastic Net 에서 L1, L2 Regularization의 비율을 조절하는 알파(α)는 데이터로 부터 학습될 수 없고 분석가가 지정해줘야 하는 하이퍼-파라미터(Hyper-parameter) 입니다. 

 

분석 툴에서 제공하는 기본 값을 람다(λ), 알파(α)의 값으로 해도 되지만, 최적의 람다(λ)와 알파(α)를 찾기 위해서는 Cross-validation 기법을 사용합니다. 

 

cross-validation to find the optimal tuning parameter lambda

 

교차 검증(Cross-validation)은 예측 모델의 성능을 평가하고 과적합의 위험을 줄이기 위한 통계적 기술로, 데이터 집합을 여러 하위 집합으로 나누고 모델을 이러한 하위 집합 중 일부로 훈련하고 나머지로 성능을 평가하는 과정을 포함합니다. 주요 아이디어는 훈련 및 테스트에 서로 다른 하위 집합을 사용하여 모델의 성능에 대한 더 견고한 추정을 얻는 것입니다.

가장 일반적인 교차 검증 형식은 k-겹 교차 검증(k-fold cross-validation)으로, 데이터 집합을 k개의 동일한 크기의 폴드로 나눕니다. 모델은 k번 훈련되며 각 훈련에서 k-1개의 폴드를 사용하고 나머지 폴드를 테스트에 사용합니다. 이 프로세스는 k번 반복되며 각 반복에서 다른 폴드가 테스트에 사용됩니다. 성능 메트릭(예: 정확도, 평균 제곱 오차)은 k번의 반복을 통해 평균화되어 모델의 성능을 더 신뢰할 수 있는 추정으로 제공됩니다.

교차 검증은 특히 하이퍼파라미터 튜닝에 유용하며, 이는 머신 러닝 모델에 대한 최적의 하이퍼파라미터 세트를 찾는 목표입니다. 단일 훈련-테스트 분할을 의존하는 대신 교차 검증을 사용하면 모델의 성능을 여러 훈련-테스트 분할에서 평가할 수 있습니다. 이는 데이터가 특정 랜덤 분할에 따라 평가되는 것에 대한 편향을 줄이는 데 도움이 됩니다.

다음은 하이퍼파라미터 튜닝을 위해 교차 검증을 사용하는 단계별 프로세스입니다:

(1) 데이터 분할: 데이터 집합을 훈련 및 테스트 세트로 나눕니다. 훈련 세트는 k-겹으로 나뉩니다.
(2) 하이퍼파라미터 선택: 튜닝하려는 하이퍼파라미터를 지정합니다.
(3) 하이퍼파라미터 그리드 생성: 튜닝할 하이퍼파라미터 값의 그리드를 정의합니다. 이는 주로 그리드 서치(grid search) 또는 랜덤 서치(random search) 기법을 사용하여 수행됩니다.
(4) 교차 검증 수행: 각 하이퍼파라미터 조합에 대해 k-겹 교차 검증을 수행합니다. k-1개의 폴드를 사용하여 모델을 훈련하고 나머지 폴드를 테스트합니다. 이러한 프로세스를 k번 반복하여 각 반복에서 다른 폴드를 테스트로 사용합니다.
(5) 최적의 하이퍼파라미터 선택: k 폴드를 통해 얻은 성능 중 가장 좋은 평균 성능을 가진 하이퍼파라미터 세트를 선택합니다.
(6) 최종 모델 훈련: 선택된 하이퍼파라미터를 사용하여 전체 훈련 세트에서 최종 모델을 훈련합니다.

교차 검증을 사용하여 하이퍼파라미터 튜닝을 수행하면 데이터의 여러 하위 집합에서 어떤 하이퍼파라미터가 잘 작동하는지에 대한 보다 신뢰할 수 있는 평가를 얻을 수 있습니다.

 

 


5. Python sklearn을 이용한 LASSO 예제 (w/ Hyperparameter tuning)

 

Python의 Scikit-learn 모듈에 있는 sklearn.linear_model.Lasso 메소드를 사용해서 LASSO 회귀를 적합해보겠습니다. 이때 GridSearchCV 로 규제의 강도를 조절하는 튜닝 파라미터 alpha (위에서는 람다로 표기, 좀 헷갈릴 수 있겠네요..) 의 최적의 값을 찾아보도록 하겠습니다. 

 

# Import necessary libraries
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import Lasso
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.metrics import mean_squared_error

# Generate some example data
np.random.seed(42)
x1 = 2 * np.random.rand(100, 1)   # strong relation with y
x2 = 2 * np.random.rand(100, 1) # no relation with y
X = np.column_stack([x1, x2])

y = 4 + 3 * x1 + 0.1 * np.random.randn(100, 1)

# Split the data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Perform Lasso regression with grid search for alpha
lasso = Lasso()
param_grid = {'alpha': [0.01, 0.1, 0.5, 1.0, 2.0]}  # Values to try for alpha

grid_search = GridSearchCV(lasso, param_grid, cv=5, scoring='neg_mean_squared_error')
grid_search.fit(X_train, y_train)

# Get the best hyperparameters
best_alpha = grid_search.best_params_['alpha']

# Train the model with the best hyperparameters
lasso_reg = Lasso(alpha=best_alpha)
lasso_reg.fit(X_train, y_train)

# Read out attributes
coeffs = lasso_reg.coef_            # coefficients
intercept = lasso_reg.intercept_  # intercept

# Make predictions on the test set
y_pred = lasso_reg.predict(X_test)

# Evaluate the model
mse = mean_squared_error(y_test, y_pred)


print(f'Best alpha: {best_alpha}')
print('Coefficients:', coeffs)
print('Intercept:', intercept)
print(f'Mean Squared Error: {mse}')


# Best alpha: 0.01
# Coefficients: [2.95614483 0.00726315]
# Intercept: [4.04106647]
# Mean Squared Error: 0.0066267863966506645

 

 

Cross-validation 으로 찾은 최적의 best alpha 는 0.01 이네요. 

Alpha = 0.01 일 때 목표변수 y와 강한 상관성이 있는 변수 x1의 회귀계수는 2.956이고, 목표변수 y와는 아무런 상관이 없는 변수 x2 의 회귀계수는 0.0072 로서 거의 0 으로 축소되었습니다. 

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 베타분포 (Beta Distribution)란 무엇이고, 왜 중요한가에 대해 다뤄보겠습니다. 

 

 

1. 베타 분포 (Beta Distribution)는 무엇인가?

 

베타 분포는 구간 [0, 1] 에서 정의된 연속 확률 분포입니다. 일반적으로 Beta(α,β)로 표기되며, 여기서 α와 β는 양의 형태 매개변수입니다. 베타 분포의 확률 밀도 함수(PDF)는 다음과 같습니다:

베타 분포 확률밀도함수



여기서 B(α,β)는 베타 함수로, 확률 밀도 함수 아래의 면적이 1이 되도록 하는 정규화 상수 역할을 합니다.


베타 분포는 형태 매개변수의 값에 따라 다양한 모양을 가지며 다음과 같은 특징을 갖습니다:

  • α=β=1인 경우, 베타 분포는 구간 [0,1]에서 균일 분포입니다.
  • α와 β가 증가함에 따라 분포는 평균 주변에 더 피크한 형태를 가집니다.

 

 


2. 베이지안 통계(Bayesian Statistics)에서 베타분포(Beta Distribution)는 왜 중요한가? 

 


베타 분포는 베이지안 통계에서 여러 이유로 중요합니다:


(1) 켤레 사전 분포 (Conjugate Prior)


베타 분포는 이항, 음이항 이항, 베르누이 및 기하 분포에 대한 켤레 사전 분포입니다(The beta distribution is the conjugate prior for the binomial, negative binomial, Bernoulli, and geometric distributions). 이는 베이지안 분석에서 베타 분포를 사전 분포로 사용하고 관측된 데이터로 업데이트하면 사후 분포도 베타 분포가 되는 특성을 의미합니다. 이러한 특성은 베이지안 추론에서 업데이팅 과정을 단순화합니다.


(2) 사전 신념 표현 (Expressing Prior Beliefs)


베타 분포는 이진 결과(성공/실패, 앞면/뒷면 등)에 대한 성공 확률에 대한 사전 신념을 모델링하는 데 자주 사용됩니다. 형태 매개변수 α와 β(shape parameters α and β)에 적절한 값을 선택함으로써 연구자는 한 방향으로의 강한 신념부터 더 불확실하거나 흩어진 신념까지 다양한 사전 신념을 표현할 수 있습니다.


(3) 베이지안 업데이팅 (Bayesian Updating)


베타 분포는 베이지안 분석에서 사전 지식을 분석에 통합하는 데 도움이 됩니다. 새로운 데이터를 관측한 후에는 사전 분포를 사후 분포로 업데이트하며, 베타 분포의 수학적 속성은 이 업데이트 과정을 분석적으로 다루기 쉽게 만듭니다.

 


(4) 베타-이항 모델 (Beta-Binomial Model)

 

베타 분포는 이항 분포와 밀접한 관련이 있습니다. 베이지안 분석에서 사전으로 사용되어진다면 베르누이 분포와 함께 사용되는 경우가 많으며, 결과적인 모델은 베타-이항 모델 (beta-binomial model)이라고 불립니다. 이 모델은 베이지안 맥락에서 이항 확률 변수의 분포를 모델링하는 데 널리 사용됩니다. 

요약하면 베타 분포는 베이지안 통계에서 사전 신념을 모델링하고 이를 관측 데이터로 업데이트하는 데 유연하고 분석적으로 편리한 방법을 제공합니다. 그 켤레성(conjugate nature)은 베이지안 추론에서 수학적 계산을 간단하게 만듭니다.

 


3. Python을 이용해서 베타 분포의 파라미터별 그래프 그려보기

X 축 값의 구간은 [0, 1] 사이의 실수로 하고, 형태 매개변수 α와 β(shape parameters α and β) 를 변화시켜가면서 베타 분포를 그래프로 그려보겠습니다. 가령, 아래 그래프에서 보는 바와 같이,  α와 β의 값이 각각 1인 경우 확률값이 모두 0으로 동일합니다. (즉, 사전 정보가 없다는 의미입니다) 

α와 β의 값에 따라 매우 다양한 형태의 확률 분포를 표현할 수 있어서 유용합니다. 

 

# Plot the beta distribution for each set of alpha and beta values
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import beta

# Define a range of x values
x = np.linspace(0, 1, 1000)

# Define different alpha and beta values
d = [.5, 1, 2, 4]

fig = plt.figure(figsize=(16, 12))

for i in np.arange(16):
    a = d[i % 4]
    b = d[i // 4]
    ax = fig.add_subplot(4, 4, 1 + i)
    ax.set_title('Beta Distribution(a=' + str(a) + ', b=' + str(b) + ')')
    ax.set_xlabel('X')
    ax.set_ylabel('P(X)')
    ax.grid()
    ax.plot(x, beta(a, b).pdf(x), 'b--')

plt.subplots_adjust(top=.95, bottom=.05, hspace=.3, wspace=.3)
plt.show()

 

베타 분포 (Beta Distribution for different alpha and beta values)

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행보한 데이터 과학자 되세요!  :-)

 

 

728x90
반응형
Posted by Rfriend
,