2024년 5월 11일 토요일

GPT, LLM 의 이해와 용어 정리

 GPT 란

GPT는 "Generative Pre-trained Transformer"의 약자입니다. GPT 모델은 기본적으로 자연어 처리를 위한 딥러닝 모델로, 대규모의 텍스트 데이터를 학습하여 문장을 생성하거나 이해하는 데 사용됩니다. 이 모델은 트랜스포머(Transformer)라는 구조를 기반으로 하며, 사전에 학습된(pre-trained) 모델을 기반으로 새로운 작업에 대해 추가적인 학습을 통해 성능을 높일 수 있습니다. GPT 모델은 대화 시스템, 문장 생성, 요약, 번역 등 다양한 자연어 처리 작업에 활용될 수 있습니다.


Transformer란

Transformer는 딥러닝 모델의 하나로, 자연어 처리 및 기타 시퀀스 기반 작업에 사용되는 뛰어난 성능을 보이는 모델 아키텍처입니다. 이 아키텍처는 "Attention is All You Need"라는 논문에서 처음으로 소개되었습니다. https://proceedings.neurips.cc/paper/2017/file/3f5ee243547dee91fbd053c1c4a845aa-Paper.pdf

Transformer 아키텍처는 어텐션 메커니즘(attention mechanism)을 사용하며 이를 통해 문장의 각 단어 간의 상관 관계를 이해하고 처리할 수 있습니다.

Transformer는 인코더(encoder)와 디코더(decoder)라는 두 개의 주요 구성 요소로 구성됩니다. 인코더는 입력 시퀀스를 임베딩하고, 이를 통해 입력 시퀀스의 정보를 추출합니다. 디코더는 인코더의 출력과 디코딩된 출력을 사용하여 출력 시퀀스를 생성합니다. 이러한 구조는 기계 번역, 대화 시스템, 요약 등의 작업에 사용됩니다. Transformer는 효율적인 병렬 처리와 높은 성능을 제공하여 많은 자연어 처리 작업에서 사용되고 있습니다.

Transformer 구조


토큰과 토크나이저

"토큰(Token)"은 텍스트 데이터를 작은 단위로 나누는 과정에서 사용되는 용어입니다. 자연어 처리에서, 토큰은 문장 또는 문서를 단어, 구두점, 숫자 등의 작은 단위로 분할하는 과정에서 생성됩니다.

예를 들어, "나는 학교에 갔다"라는 문장을 토큰화하면 다음과 같이 단어 단위로 분할될 수 있습니다:

"나는", "학교에", "갔다"

토큰은 텍스트를 이해하고 처리하는 데 사용됩니다. 딥러닝 모델에서는 텍스트 데이터를 토큰 단위로 분할하여 입력으로 사용하며, 이를 통해 모델은 단어의 시퀀스로 이루어진 데이터를 처리할 수 있습니다.

이때 토큰을 나누는 도구가 토크나이저가 됩니다.


텍스트 임베딩

LLM(언어 모델)에서도 텍스트 임베딩(Text Embedding)은 중요한 개념입니다. LLM은 텍스트의 의미를 이해하고 생성하기 위해 대규모의 텍스트 데이터를 학습합니다. 이 과정에서, LLM은 단어, 문장 또는 문서를 고차원의 벡터 공간에 매핑하여 텍스트 임베딩을 생성합니다.


Attention

Attention은 query와 key-value 쌍을 output에 매핑하는 것



가령, "나는 오늘 밥을 먹었다"와 "나는 내일 운동을 할 것이다" 라는 두 문장을 비교해 보겠습니다. 이 문장들을 번역하거나, 유사도를 비교하는 작업을 수행한다고 가정해 봅시다.

쿼리(Query): 두 번째 문장에서 "운동"이라는 단어를 고려해 보겠습니다. "운동"이라는 단어가 쿼리(Query)가 됩니다.

키(Key) 및 값(Value): 첫 번째 문장과 두 번째 문장에 있는 모든 단어들이 키(Key)와 값(Value)이 됩니다.

이제 어텐션 메커니즘을 사용하여 "운동"이라는 단어와 다른 단어들 간의 관련성을 측정하고, 이를 기반으로 가중 평균을 계산하여 "운동"과 관련된 정보를 추출할 수 있습니다.

예를 들어, "운동"과 "밥을" 사이의 유사도가 낮을 것이고, "운동"과 "오늘" 혹은 "내일"과의 유사도가 높을 것입니다. 이 정보를 기반으로 "운동"과 관련된 의미를 보다 잘 이해하고, 번역하거나 유사도를 측정하는 데 사용할 수 있습니다.

이런 식으로 어텐션 메커니즘은 주어진 작업에 필요한 정보에 집중하고, 입력 데이터의 다양한 부분 간의 관련성을 이해하는 데 도움이 됩니다. 

위의 예시에서는 text를 들었지만 실제 동작에서는 텍스트 임베딩이 된 수치 형태의 벡터 상태로 동작됩니다.


Self-Attention 

하나의 "단일한" 시퀀스(예: 문장)에서 각기 다른 위치에 대해 어텐션 메커니즘을 적용


BERT

BERT(또는 Bidirectional Encoder Representations from Transformers)는 2018년에 구글에서 발표된 자연어 처리 모델입니다. BERT는 트랜스포머(Transformer) 아키텍처를 기반으로 하며, 대량의 텍스트 데이터를 사전 학습(pre-training)하여 언어 이해의 다양한 작업에 활용할 수 있도록 만들어졌습니다.

  1. 양방향(bidirectional) 모델: BERT는 입력 문장의 좌우 문맥을 모두 고려하여 단어를 이해합니다. 이전의 자연어 처리 모델들은 단어를 왼쪽에서 오른쪽으로 순차적으로 처리했지만, BERT는 양방향으로 문맥을 고려하여 더욱 정확한 표현을 생성합니다.

  2. 사전 학습과 파인 튜닝: BERT는 대규모의 텍스트 데이터를 미리 학습한 후, 특정 작업에 맞게 추가적인 학습 없이도 적용할 수 있습니다. 이를 통해 다양한 자연어 처리 작업에서 뛰어난 성능을 발휘할 수 있습니다. 또한, 특정 작업에 맞게 추가 학습(fine-tuning)을 수행하여 성능을 더욱 향상시킬 수 있습니다.

  3. 다양한 자연어 처리 작업에 적용 가능: BERT는 문장 분류, 질문 응답, 텍스트 유사도 측정, 개체명 인식 등 다양한 자연어 처리 작업에 적용할 수 있습니다. 또한, 다양한 언어에 대한 사전 학습 모델도 제공되어 다국어 처리에도 유용하게 활용될 수 있습니다.

트랜스포머의 인코더 구조를 가져와 양방향으로 진행시킨 Masked Language Model

Masked Language Model 통해서 학습을 합니다.


Masked Language Model 

BERT는 '말을 배우는' Large Model입니다. 특정 Input과 label에 따라 Task를 수행하는 법을 배우는 모델이 아닙니다.

BERT의 공부법은 Masked 단어 학습법이다. 이 학습법은 Label이 주어지지 않습니다.(Unsupervised Learning) 그냥 주어진 문장이 있다면, 토큰 단위별로 가려보면서 앞뒤의 문맥을 보고 그 빈칸 안에 들어갈 것을 추론하는 과정을 통해 배우게 됩니다.


GPT 와 BERT

BERT는 딥러닝의 트랜스포머 기술을 이용한 양방향 인코딩 모델(‘임베딩 모델’ 또는 ‘문서 표현 모델’)이고, GPT는 트랜스포머 기술을 이용해 사전 학습된 ‘생성 모델’입니다. 즉, ‘BERT 모델’과 ‘GPT 모델’에서 모델은 대규모 학습 말뭉치의 내용을 구조화해 그 내용을 가장 잘 표현하는 형태로 구조화한 것이라는 의미고, 언어 모델에서 모델은 언어 처리 방법론을 논리적이고 체계적으로 정립한 것이다.


LLM

"LLM"은 "Large Language Model"의 약어로, 대규모 언어 모델을 가리킵니다. 이는 자연어 처리 분야에서 사용되는 매우 큰 규모의 딥러닝 모델을 의미합니다. LLM은 대량의 텍스트 데이터를 학습하여 텍스트 이해, 생성 및 기타 자연어 처리 작업을 수행할 수 있습니다.

LLM은 최근 몇 년 동안 자연어 처리 분야에서 주목을 받고 있으며, 다양한 응용 분야에 적용되고 있습니다. 대표적인 LLM 중 하나는 OpenAI의 GPT(Generative Pre-trained Transformer) 시리즈입니다. GPT 모델은 트랜스포머(Transformer) 아키텍처를 기반으로 하며, 대규모의 텍스트 데이터를 사전 학습(pre-training)한 후, 다양한 자연어 처리 작업에 적용할 수 있습니다.

LLM은 다음과 같은 작업을 수행할 수 있습니다:

텍스트 생성: 주어진 문맥에서 다음 단어나 문장을 생성합니다.

문장 분류: 텍스트를 여러 범주 중 하나로 분류합니다.

기계 번역: 한 언어로 작성된 문장을 다른 언어로 번역합니다.

요약: 긴 문서를 짧게 요약합니다.

질문 응답: 주어진 질문에 대한 답변을 생성하거나 추출합니다.

LLM은 대량의 텍스트 데이터를 학습하여 언어의 다양한 특성과 구조를 학습하며, 이를 통해 다양한 자연어 처리 작업에 효과적으로 활용할 수 있습니다. 이러한 모델은 실제 응용 분야에서 문제 해결과 혁신을 이끌어내는 데 중요한 역할을 하고 있습니다.


LLM 과 GPT관계?

"LLM"과 "GPT"는 실제로 같은 개념을 가리키는 것이 아니며, GPT는 LLM의 한 종류입니다.

  1. LLM (Large Language Model): LLM은 대규모 언어 모델을 의미합니다. 이는 자연어 처리 분야에서 사용되는 매우 큰 규모의 딥러닝 모델을 의미합니다. LLM은 대량의 텍스트 데이터를 학습하여 텍스트 이해, 생성 및 기타 자연어 처리 작업을 수행할 수 있습니다. 예를 들어, GPT(Generative Pre-trained Transformer) 시리즈는 LLM의 한 종류입니다.

  2. GPT (Generative Pre-trained Transformer): GPT는 OpenAI에서 개발된 대표적인 LLM의 한 종류입니다. GPT 모델은 트랜스포머(Transformer) 아키텍처를 기반으로 하며, 대규모의 텍스트 데이터를 사전 학습(pre-training)한 후, 다양한 자연어 처리 작업에 적용할 수 있습니다. GPT 모델은 문맥을 이해하고 다음 단어를 예측하거나 텍스트를 생성하는 등의 작업을 수행할 수 있습니다.

따라서, GPT는 LLM의 한 종류로 볼 수 있으며, LLM은 자연어 처리 분야에서 다양한 모델을 포괄하는 개념입니다.


프롬프트

프롬프트(prompt)란 모델에 입력되는 시작 문장 또는 문장 조각을 의미합니다. 프롬프트는 모델에게 원하는 작업을 수행하도록 지시하거나 특정 유형의 텍스트 생성을 유도합니다.

프롬프트는 사용자가 원하는 텍스트 생성 작업을 설정하는 데 중요한 역할을 합니다. 예를 들어, "Once upon a time"과 같은 프롬프트는 이야기의 시작을 나타내며, 모델에게 이야기를 생성하도록 요청합니다. 비슷하게, "Translate the following sentence into French"과 같은 프롬프트는 모델에게 주어진 문장을 프랑스어로 번역하도록 지시합니다.

프롬프트는 모델이 텍스트 생성 작업을 시작하는 데 중요한 역할을 합니다. 올바른 프롬프트를 사용하면 모델이 원하는 결과를 생성하는 데 도움이 되며, 모델의 생성된 텍스트의 품질과 일관성에 영향을 미칠 수 있습니다. 따라서 적절한 프롬프트를 선택하는 것이 중요합니다.


폐쇄 환경에서 GPT 사용

LLaMA

LLaMA는 메타의 오픈소스 언어모델입니다.


LM Studio 로 LLaMA 사용 그리고 LangChain

랭체인(LangChain)은 대규모 언어 모델과 애플리케이션의 통합을 간소화하는 SDK입니다.


프롬프트의 중요성

프롬프트 엔지니어링(prompt engineering)은 자연어 처리 모델을 사용하여 특정 작업을 수행할 때, 적절한 프롬프트를 설계하고 조정하는 과정을 의미합니다. 이는 모델이 원하는 결과를 생성하고 원하는 방식으로 작동하도록 유도하는 것을 목표로 합니다.


LLM(GPT)으로 번역기 만들기

번역 SW를 따로 개발할 필요없이 GPT를 구동할 수 있다면 쉽게 번역기를 만들 수 있습니다.

ChatGPT에 아래와 같이 사용하면 되며, 이것을 프롬프트 사용이라고 합니다.

"다음 문장을 한글로 번영해주세요."

"""문장""" 


GPT로 요약하기

GPT로 흔히 볼 수 있는것이 요약하는것입니다.

"아래 문장을 간단하게 요약해주세요."

"""문장"""


토큰의 제약

GPT 모델은 토큰 수에 대한 제약 사항이 있습니다. 이러한 제약 사항은 모델의 입력 최대 길이와 관련이 있으며, 주로 모델의 아키텍처와 하드웨어 자원의 한계에 따라 결정됩니다. 그래서 긴 길이의 문장은 생성할 수 없으며, 대화도 길게 유지하기가 어렵습니다.


Hallucination

GPT와 같은 자연어 처리 모델이 실제로는 존재하지 않는 정보를 생성하는 현상을 가리킵니다. 이는 모델이 훈련될 때 사용된 데이터에는 없는 내용이나 사실을 생성하는 것을 의미합니다. GPT는 알 수 없는 것(훈련이 안된 것)을 알 수 없다라고 말하지 못합니다.


RAG(검색 증강)

RAG(Retrieval-Augmented Generation)는 텍스트 생성 모델인 GPT와 정보 검색 모델을 결합하여 더욱 정확하고 다양한 텍스트 생성을 가능하게 합니다.

검색 증강 생성(RAG)은 프라이빗 또는 독점 데이터 소스의 정보로 텍스트 생성을 보완하는 기술입니다. 대규모 데이터 세트 또는 지식 기반을 검색하도록 설계된 검색 모델에 해당 정보를 가져와 읽을 수 있는 텍스트 응답을 생성하는 대규모 언어 모델(LLM)과 같은 생성 모델을 결합합니다. 검색 증강 생성은 추가 데이터 소스의 컨텍스트를 더하고 훈련을 통해 LLM의 원래 지식 기반을 보완함으로써 검색 경험의 정확도를 개선할 수 있습니다. 따라서 모델을 다시 훈련할 필요 없이 대규모 언어 모델의 출력이 향상됩니다. 추가 정보 소스는 LLM의 훈련에 사용되지 않은 인터넷의 새로운 정보부터 독점 비즈니스 컨텍스트 또는 비즈니스에 속한 기밀 내부 문서에 이르기까지 다양합니다. RAG는 생성형 AI 시스템이 외부 정보 소스를 사용하여 보다 정확한 상황 인식 응답을 생성할 수 있도록 해주기 때문에 질문 답변 및 콘텐츠 생성과 같은 작업에 유용합니다. 일반적으로 시맨틱 검색이나 하이브리드 검색과 같은 검색 방법을 구현하여 사용자 의도에 응답하고 보다 정확한 결과를 제공합니다.

ChatGPT4.0 에 pdf 문서를 첨부하면 pdf 문서에서 사실을 검색해준다던가, ChatGPT4.0에 브라우저를 이용해서 최신 컨텐츠를 검색하는 기능을 말합니다.


FineTuning

"Fine-tuning(미세 조정)"은 기계 학습에서 사전에 훈련된(pre-trained) 모델을 특정한 작업에 맞게 추가적인 훈련을 진행하는 과정을 의미합니다. 이는 이미 학습된 모델의 일부 레이어 또는 파라미터를 고정시킨 채로 새로운 데이터셋에 대해 추가적인 학습을 수행하여 모델을 특정 작업에 맞게 세밀하게 조정하는 것을 말합니다.

예를 들어, 범용 LLM은 다양한 언어 정보를 포함하고 유창한 대화에 참여할 수 있습니다. 그러나 의료 분야에서는 환자의 질문에 효과적으로 응답할 수 있는 애플리케이션을 구축하기 위해 대규모 언어 모델을 새로운 의료 데이터에 노출시켜야 합니다. 


LoRA

언어 모델 영역에서는 특정 데이터에 대해 특정 작업을 수행하도록 기존 언어 모델을 미세 조정하게 됩니다.

전체 미세 조정에는 신경망의 모든 레이어를 최적화하거나 훈련하는 작업이 포함됩니다. 이 접근 방식은 일반적으로 최상의 결과를 얻을 수 있지만, 리소스와 시간이 가장 많이 소요되는 방식이기도 합니다.

LoRA는 사전 학습된 대규모 언어 모델의 가중치 행렬을 구성하는 모든 가중치를 미세 조정하는 대신, 작은 행렬을 미세 조정하는 개선된 미세 조정 방식입니다.


2024년 4월 28일 일요일

Stable-diffusion / SDXL 설치

Stable-diffusion / SDXL 설치


windows 환경에서 설치 방법입니다.


1. stable-diffusion-webui 설치

https://github.com/AUTOMATIC1111/stable-diffusion-webui

최신 버전도 많지만 v1.5.1 tag가 붙은 버전을 설치합니다.

https://github.com/AUTOMATIC1111/stable-diffusion-webui/releases/tag/v1.5.1

stable-diffusion-webui-1.5.1.zip 파일을 다운로드 받아서 적당한 위치에 압축을 해제합니다.



2. python 3.10.6 설치

Stable-diffusion이 구동될려면 python, git 이 설치 되어어야 합니다.

https://www.python.org/downloads/release/python-3106/

버전을 맞춰주시고 path 설정도 해줍니다.

1번에서 해제된 압축 파일을내에 webui-user.bat 파일을 열어

PYTHON 경로를 넣어줍니다. (경로는 사용자마다 다릅니다.)

예) set PYTHON=C:\Users\AppData\Local\Programs\Python\Python310\python.exe

추가로 아래 부분도 변경 시켜주세요

set COMMANDLINE_ARGS=--xformers --no-half-vae 

이 부분을 안해주면 결과물이 이상하게 나옵니다.



3. git 설치

아래에서 맞는 버전을 설치해줍니다.

https://git-scm.com/download/win


4. SDXL 모델 다운로드

huggingface 에서 각각 아래 파일을 다운로드 받습니다.

https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/tree/main

sd_xl_base_1.0.safetensors

https://huggingface.co/stabilityai/stable-diffusion-xl-refiner-1.0/tree/main

sd_xl_refiner_1.0.safetensors

Stable-diffusion 만 설치하는 것이라면 4, 5 단계를 무시하면 됩니다.


5. 다운로드 받은 파일을 models\Stable-diffusion 폴더로 복사

경로는 아래 위치가 됩니다.


6. webui-user.bat 실행

실행된 내용에 있는 주소를 브라우저로 엽니다. 커맨드 라인의 Running on local URL 입니다.

아래 화면에서는 http://127.0.0.1:7860/ 이곳입니다.




stable diffusion checkpoint 에서 어떤 모델을 선택하느냐에 따라 SDXL이 되는 것입니다.

앞에 다운로드 받았던, sd_xl_base_1.0.safetensors, sd_xl_refiner_1.0.safetensors 를 선택한다면  SDXL 이 됩니다.



[에러 발생시]

Version: 1.5.1

Commit hash: <none>

Launching Web UI with arguments:

Traceback (most recent call last):

  File "D:\dev\SDXL\stable-diffusion-webui-1.5.1\launch.py", line 39, in <module>

    main()

  File "D:\dev\SDXL\stable-diffusion-webui-1.5.1\launch.py", line 35, in main

    start()

  File "D:\dev\SDXL\stable-diffusion-webui-1.5.1\modules\launch_utils.py", line 390, in start

    import webui

  File "D:\dev\SDXL\stable-diffusion-webui-1.5.1\webui.py", line 44, in <module>

    import gradio  # noqa: F401

  File "D:\dev\SDXL\stable-diffusion-webui-1.5.1\venv\lib\site-packages\gradio\__init__.py", line 3, in <module>

    import gradio.components as components

  File "D:\dev\SDXL\stable-diffusion-webui-1.5.1\venv\lib\site-packages\gradio\components.py", line 55, in <module>

    from gradio import processing_utils, utils

  File "D:\dev\SDXL\stable-diffusion-webui-1.5.1\venv\lib\site-packages\gradio\utils.py", line 339, in <module>

    class AsyncRequest:

  File "D:\dev\SDXL\stable-diffusion-webui-1.5.1\venv\lib\site-packages\gradio\utils.py", line 358, in AsyncRequest

    client = httpx.AsyncClient()

  File "D:\dev\SDXL\stable-diffusion-webui-1.5.1\venv\lib\site-packages\httpx\_client.py", line 1397, in __init__

    self._transport = self._init_transport(

  File "D:\dev\SDXL\stable-diffusion-webui-1.5.1\venv\lib\site-packages\httpx\_client.py", line 1445, in _init_transport

    return AsyncHTTPTransport(

  File "D:\dev\SDXL\stable-diffusion-webui-1.5.1\venv\lib\site-packages\httpx\_transports\default.py", line 275, in __init__

    self._pool = httpcore.AsyncConnectionPool(

TypeError: AsyncConnectionPool.__init__() got an unexpected keyword argument 'socket_options'


=>pip install httpx==0.24.1

그런데 venv로 동작하고 있어서 venv 환경의 httpx 설치를 바꿔줘야 합니다.

cmd 창을 열고

설치된 폴더 stable-diffusion-webui-1.5.1\venv\Scripts 여기로 이동한뒤 activate.bat 를 실행 시킵니다. 그러면 아래와 같은 화면이 나오고 앞에 (venv) 가 붙습니다.



위와 같이 pip install httpx==0.24.1 명령을 수행합니다.

그 이후 webui-user.bat 를 다시 실행시키면 됩니다.


2024년 4월 12일 금요일

Unity 안드로이드 환경에서의 (adb 없이)효과적인 디버깅 방법

 

가끔씩 PC 환경과는 다르게 안드로이드에서 애플리케이션이 제대로 동작하지 않는 경우가 있습니다. 이때는 로그를 보면서 디버깅을 진행해야 합니다.

아래는 adb가 없을때 삼성 휴대폰에서 정리한 안드로이드 환경에서 Unity 애플리케이션을 디버깅하는 방법입니다.


  1. 문제가 의심되는 위치에 다음과 같은 형태로 로그를 추가합니다.

    Debug.Log("bgmEffect null");
    
  2. 휴대폰용으로 APK 빌드를 하여 문제를 재현합니다.

  3. ADB 프로그램이 있다면, 아래의 과정을 수행하지 않아도 됩니다. 그렇지 않은 경우에는 다음과 같은 절차를 따릅니다.

    *#9900#  화면 (두번째 메뉴 Run dumpstate/logcat, 다섯번째 Copy to sdcard 실행)

    • 휴대폰의 *#9900# 덤프 획득 모드로 진입
    • Run dumpstate/logcat 실행
    • Copy to sdcard 메뉴 실행
  4. 휴대폰을 PC와 연결한 후 log 폴더를 찾아서 dumpstate_모델명_날짜.log 파일을 PC로 복사합니다.

  5. 편집기 프로그램을 열고 "Unity :"로 검색합니다.

  6. 이렇게 로그를 확인하면 call stack이 나옵니다.

  7. 04-12 21:57:16.992 10610 31520 31564 I Unity   : bgmEffect null
    04-12 21:57:16.992 10610 31520 31564 I Unity   : GameManager:FadeOutToScene(String)
    04-12 21:57:16.992 10610 31520 31564 I Unity   : UnityEngine.Events.UnityEvent:Invoke()
    04-12 21:57:16.992 10610 31520 31564 I Unity   : UnityEngine.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1)
    04-12 21:57:16.992 10610 31520 31564 I Unity   : UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchPress(PointerEventData, Boolean, Boolean)
    04-12 21:57:16.992 10610 31520 31564 I Unity   : UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchEvents()
    04-12 21:57:16.992 10610 31520 31564 I Unity   : UnityEngine.EventSystems.StandaloneInputModule:Process()
    


  8. 로그를 활용하여 필요한 경우 더 많은 곳에 로그를 추가하고 위의 절차를 반복하여 문제를 해결합니다.


이와 같은 방법을 통해 안드로이드 환경에서 Unity 애플리케이션의 문제를 신속하게 해결할 수 있습니다.





2024년 4월 10일 수요일

Unity에서 데이터 저장 최적화하기: 중복 저장 방지


Unity에서 데이터 저장 최적화하기: 중복 저장 방지

Unity 게임 개발 중, 플레이어의 진행 상황, 설정, 또는 게임 데이터를 저장하는 것은 필수적입니다. 하지만, 불필요하게 같은 데이터를 반복해서 저장하는 것은 효율적이지 못합니다. 이 글에서는 저장 전에 기존에 저장된 데이터와 내용이 같은지 비교하여, 같다면 저장하지 않는 방법을 소개합니다.


저장 데이터 비교 로직

먼저, 저장할 데이터와 기존에 로드된 데이터를 비교하는 함수 CompSaveData를 사용합니다. 이 함수는 SaveDataStruct 타입의 두 데이터 객체를 매개변수로 받아, 내부적으로 BinaryFormatter를 사용하여 객체를 바이트 배열로 직렬화한 후, 두 바이트 배열이 서로 같은지를 비교합니다.

csharp

private static bool CompSaveData(SaveDataSturct data1, SaveDataSturct data2)
{
    BinaryFormatter formatter1 = new BinaryFormatter();
    BinaryFormatter formatter2 = new BinaryFormatter();
    byte[] bytes1;
    byte[] bytes2;
    using (MemoryStream m = new MemoryStream())
    {
        formatter1.Serialize(m, data1);
        bytes1 = m.ToArray();
    }
    using (MemoryStream m = new MemoryStream())
    {
        formatter2.Serialize(m, data2);
        bytes2 = m.ToArray();
    }
    return bytes1.SequenceEqual(bytes2);
}


데이터 저장 최적화

저장할 데이터가 기존에 저장된 데이터와 다를 때만 데이터를 저장하는 로직을 구현합니다. 이를 위해, 먼저 기존의 데이터를 로드하고, 로드된 데이터가 없거나 (loaddata==null) 로드된 데이터가 있지만 현재 데이터와 다른 경우 (!CompSaveData(tempData, loaddata))에만 새로운 데이터를 저장합니다.

csharp

var loaddata = LoadData();
if ((loaddata==null) || (loaddata != null && !CompSaveData(tempData, loaddata)))
{
    WriteSaveDataToDisk(tempData);
    saveData = tempData;
}


데이터 저장 함수

데이터를 저장하기 위한 WriteSaveDataToDisk 함수는 BinaryFormatter를 사용하여 데이터를 직렬화하고, 지정된 경로에 파일로 저장합니다. 이 과정에서 데이터가 실제로 디스크에 기록되는 경로를 로그로 남겨, 디버깅에 도움을 줍니다.

csharp

private static void WriteSaveDataToDisk(SaveDataSturct data)
{
    BinaryFormatter formatter = new BinaryFormatter();
    string path = Application.persistentDataPath + "/game.savegame";
    FileStream stream = new FileStream(path, FileMode.Create);
    formatter.Serialize(stream, data);
    stream.Close();
    Debug.Log("Data saving path : " + path);
}


 




2024년 4월 7일 일요일

Unity 프로젝트 의존성 관리와 리소스 복사


Unity 프로젝트 의존성 관리와 리소스 복사


Unity 프로젝트를 진행하면서 외부 프로젝트를 가져오거나 Package Manager를 통해 다양한 리소스를 사용하게 되는 경우, 의존성(dependency) 관리에 어려움을 겪을 수 있습니다. 이를 해결하기 위한 몇 가지 방법을 소개합니다.


Select Dependencies 사용하기

  • 기능 설명: 프로젝트 내에서 특정 폴더를 선택한 후, 오른쪽 마우스 클릭 메뉴에서 'Select Dependencies'를 선택하면, 해당 폴더가 의존하고 있는 모든 파일들의 목록을 볼 수 있습니다. 이를 통해 어떤 파일들이 실제로 사용되고 있는지 파악할 수 있습니다.
  • 장점: 사용 중인 의존성 파일들을 한눈에 확인할 수 있으며, 파일을 클릭하면 상세 내용을 볼 수 있습니다.

Export Package 사용하기

  • 기능 설명: 'Export Package' 메뉴를 사용하면, 필요한 리소스만을 선택하여 Tree 형태로 확인할 수 있습니다. 이를 통해 불필요한 파일들을 쉽게 식별하고 삭제할 수 있습니다. 
  • 장점: 폴더 단위로 불필요한 파일들을 편리하게 삭제할 수 있으며, 프로젝트의 의존성을 보다 명확하게 관리할 수 있습니다.

Unity 프로젝트를 관리하면서 의존성 문제를 해결하는 것은 프로젝트의 효율성과 유지보수성을 높이는 데 중요합니다. 'Select Dependencies'와 'Export Package' 기능을 적절히 활용하여, 프로젝트의 의존성을 체계적으로 관리해 보세요. 🙂





2024년 3월 22일 금요일

python xml comment parsing

python xml comment parsing

python에서는 xml 파싱하는 방법은 여러가지 준비가 되어 있습니다.

그런데 간혹 comment 까지 파싱 해야 하는 경우가 있습니다. 

XML은 데이터를 저장하고 전송하는 데 사용되는 마크업 언어입니다. 다음은 XML의 간단한 예제입니다

python에서 아래와 같은 xml을 string에 담았습니다.

xml_str = \
"""<?xml version="1.0" encoding="UTF-8"?>
<!-- hello -->
<note>
<!-- dash1 -->
<to>kim</to><!-- dash2 -->
<from>lee</from>
<heading>alert</heading>
<body>i am a boy</body>
</note>
"""

여기에서 XML 파서는 lxml 을 사용합니다. 이 패키지는 comment까지 완벽하게 처리해냅니다.

기본 xml 처리는 아래와 같은 형태를 사용 가능합니다.

def print_xml(xml_str_):
# XML 파일을 불러옵니다.
root = etree.fromstring(xml_str.encode())

# 루트 엘리먼트의 태그와 속성을 출력합니다.
print(f'Root element: {root.tag}')
for name, value in root.attrib.items():
print(f'Attribute - {name}: {value}')

# 모든 자식 엘리먼트를 순회하며 출력합니다.
for child in root:
print(f'Child element: {child.tag}')
for name, value in child.attrib.items():
print(f'Attribute - {name}: {value}')

결과를 아래와 같이 실행시키면

print_xml(xml_str)

다음과 같은 결과를 얻습니다.

Root element: note
Child element: <cyfunction Comment at 0x00000234BF7132B0>
Child element: to
Child element: <cyfunction Comment at 0x00000234BF7132B0>
Child element: from
Child element: heading
Child element: body

cyfunction 이라고 나오는곳이 comment가 됩니다. 이 부분을 처리하려면 아래와 같은 코드가 필요합니다. tag가 etree.Comment 인지 비교해서 따로 처리하는 방법입니다.

if child.tag == etree.Comment:
print("comment:", child.text)

그러면 처음 예제와 합쳐서 구현해보도록 하겠습니다.

def print_xml_wc(xml_str_):
# XML 파일을 불러옵니다.
root = etree.fromstring(xml_str.encode())

# 루트 엘리먼트의 태그와 속성을 출력합니다.
print(f'Root element: {root.tag}')
for name, value in root.attrib.items():
print(f'Attribute - {name}: {value}')

# 모든 자식 엘리먼트를 순회하며 출력합니다.
for child in root:
print(f'Child element: {child.tag}')
if child.tag == etree.Comment:
print("comment:", child.text)
continue
for name, value in child.attrib.items():
print(f'Attribute - {name}: {value}')

이런 식으로 됩니다.

print_xml_wc(xml_str)

수행한 결과는 아래와 같습니다.

Root element: note
Child element: <cyfunction Comment at 0x00000234BF7132B0>
comment: dash1
Child element: to
Child element: <cyfunction Comment at 0x00000234BF7132B0>
comment: dash2
Child element: from
Child element: heading
Child element: body


xml 데이터의 root node보다 앞쪽에 있는 comment 처리 방법

xml을 자세히 보면 앞쪽 hello 라는 주석을 파싱하지 못하는 부분이 있습니다.

이건 어떻게 해야할까요?

처음에 문서도 보고 한참 헤맸는데요. 의외로 간단합니다.

element의 getprevious() 함수를 사용합니다.

변경된 코드는 아래와 같습니다.

def print_xml_wc_root(xml_str_):
# XML 파일을 불러옵니다.
root = etree.fromstring(xml_str.encode())

if root.getprevious() != None:
if root.getprevious().tag == etree.Comment:
print("comment:", root.getprevious().text)

# 루트 엘리먼트의 태그와 속성을 출력합니다.
print(f'Root element: {root.tag}')
for name, value in root.attrib.items():
print(f'Attribute - {name}: {value}')

# 모든 자식 엘리먼트를 순회하며 출력합니다.
for child in root:
print(f'Child element: {child.tag}')
if child.tag == etree.Comment:
print("comment:", child.text)
continue
for name, value in child.attrib.items():
print(f'Attribute - {name}: {value}')

root node의 앞쪽 tag 가 comment 라면 출력하는 코드를 넣었습니다.

실행한 결과는 아래와 같고

comment:  hello 
Root element: note
Child element: <cyfunction Comment at 0x0000026F045732B0>
comment: dash1
Child element: to
Child element: <cyfunction Comment at 0x0000026F045732B0>
comment: dash2
Child element: from
Child element: heading
Child element: body

전체 소스는 git hub에서 보시기 바랍니다.

sourcecode/python/example/_63_lxml/lxml_comment.py at main · donarts/sourcecode · GitHub