1. 모델 선택
먼저 임베딩 모델을 선택해야 한다. 계속 써오던 multilingual-e5 모델을 다시 쓸까 했는데, 마침 새로운 모델중에서 한국어 수치도 좋은 것들이 많아서 새로나온 모델을 선택해봤다.
아래는 최근에 만들어진 고려대학교의 모델이다. 오!! 괜찬은 성능이라 판단해서 해당 모델로 선택했다.
https://github.com/nlpai-lab/KURE
nlpai-lab/KURE-v1 | 0.52640 | 0.60551 | 0.60551 | 0.55784 |
dragonkue/BGE-m3-ko | 0.52361 | 0.60394 | 0.60394 | 0.55535 |
BAAI/bge-m3 | 0.51778 | 0.59846 | 0.59846 | 0.54998 |
Snowflake/snowflake-arctic-embed-l-v2.0 | 0.51246 | 0.59384 | 0.59384 | 0.54489 |
nlpai-lab/KoE5 | 0.50157 | 0.57790 | 0.57790 | 0.53178 |
intfloat/multilingual-e5-large | 0.50052 | 0.57727 | 0.57727 | 0.53122 |
Model Average Recall Average Precision Average NDCG Average F1
물론 최대한 작은 모델로 선택했다. (서버로 올려야 하는데, 모델 크기가 크면 분명히 메모리 부족으로 서버 에러가 날게 분명했다.)
2. LLM 테스트
구글 colab에서 일단 돌아가는지 테스트 해봤다. 다음의 패키지를 설치하고
!pip install transformers torch
허깅페이스의 모델을 바로 돌려보았다.
from transformers import AutoTokenizer, AutoModel
import torch
# KURE-v1 모델 로드
model_name = "nlpai-lab/KURE-v1"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name)
# 샘플 텍스트 임베딩
text = "안녕하세요, 챗봇 개발 중입니다."
inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True, max_length=128)
with torch.no_grad():
outputs = model(**inputs)
embedding = outputs.last_hidden_state.mean(dim=1).squeeze().numpy() # 평균 pooling
print("벡터 크기:", len(embedding))
print("임베딩 벡터:", embedding)
잘돌아간다!! 벡터 크기는 1024면서 괜찬은 속도로 되는거 같다. (사실 느려도 서버 비용떄문에 어쩔수가 없다..)
tokenizer_config.json: 100% 1.20k/1.20k [00:00<00:00, 35.0kB/s]
tokenizer.json: 100% 17.1M/17.1M [00:00<00:00, 31.6MB/s]
special_tokens_map.json: 100% 964/964 [00:00<00:00, 8.90kB/s]
config.json: 100% 807/807 [00:00<00:00, 24.2kB/s]
model.safetensors: 100% 2.27G/2.27G [00:53<00:00, 35.1MB/s]
벡터 크기: 1024
임베딩 벡터: [-0.17941819 0.25729018 -0.73506755 ... 0.38095683 0.25799245
0.5750115 ]
3. ec2에 적용하기
이제 서버에 올려볼까?
서버는 일단 ec2 프리티어에 올려봤다. 메모리 512메가짜리..ㅎㅎ
1(1코어)
|
512 vCPU메모리(MiB)
|
위에서 실행한 코드를 그대로 실행시켜 보면
[10/24/2023-20:33:12] [TRT] [I] [MemUsageStats] Peak memory usage during Engine building and serialization: CPU: 103590 MiB
[10/24/2023-20:33:12] [TRT-LLM] [I] Config saved to KURE-v1/config.json.
[10/24/2023-20:33:12] [TRT-LLM] [I] Serializing engine to KURE-v1
[10/24/2023-20:33:55] [TRT-LLM] [I] Engine serialized. Total time: 00:00:42
[10/24/2023-20:34:05] [TRT-LLM] [I] Loading weights from Meta KURE-v1 checkpoints ...
Killed
윽... 역시나 서버 용량이 부족하다. 모델 파일만 2.27G인데, model load 하다가 뻗어 버린다.
ec2 인스턴스를 높혀가면서 실행해봤더니, 대략 실행하는데만 3G정도가 필요하다. (t2.medium에서 실행된다..하지만 돈이?)
LLM 실행시 적은 메모리를 위해선, 모델을 경량화를 위해선 양자화를 하면 된다. 양자화의 자세한 내용은 여기 포스팅을 보면 됩니다. https://uiandwe.tistory.com/1446
3. 양자화
먼저 onnx 양자화를 위해서 관련 패키지를 설치한다.
!pip install optimum onnx onnxruntime transformers
모델 양자화를 위해선 두단계가 필요하다.
1. 사용하는 모델을 onnx으로 모델을 변경해야 한다.
2. onnx로 된 모델을 양자화를 진행한다.
모델을 다음 명령어를 통해서 onnx로 변환이 가능하다.
!optimum-cli export onnx --model nlpai-lab/KURE-v1 onnx_model_kure_v1/ --task feature-extraction
변경 완료!
modules.json: 100% 349/349 [00:00<00:00, 1.66MB/s]
config_sentence_transformers.json: 100% 220/220 [00:00<00:00, 1.38MB/s]
README.md: 100% 16.9k/16.9k [00:00<00:00, 53.5MB/s]
sentence_bert_config.json: 100% 54.0/54.0 [00:00<00:00, 302kB/s]
config.json: 100% 807/807 [00:00<00:00, 5.36MB/s]
model.safetensors: 100% 2.27G/2.27G [00:54<00:00, 41.9MB/s]
tokenizer_config.json: 100% 1.20k/1.20k [00:00<00:00, 7.74MB/s]
tokenizer.json: 100% 17.1M/17.1M [00:00<00:00, 41.9MB/s]
special_tokens_map.json: 100% 964/964 [00:00<00:00, 6.49MB/s]
1_Pooling/config.json: 100% 297/297 [00:00<00:00, 1.99MB/s]
-[x] values not close enough, max diff: 5.7220458984375e-05 (atol: 1e-05)
The ONNX export succeeded with the warning: The maximum absolute difference between the output of the reference model and the ONNX exported model is not within the set tolerance 1e-05:
- token_embeddings: max diff = 5.7220458984375e-05.
The exported model was saved at: onnx_model_kure_v1
다음 코드로 양자화가 진행된다. (몇분 걸린다.)
참고로 해당 코드는 colab에서 진행되지 않는다.(메모리가 부족하다.....)
나의 경우 ec2에서 t3.xlage 에서 진행했다.
from onnxruntime.quantization import quantize_dynamic, QuantType
output = "./gdrive/MyDrive/onnx_model_kure_v1/model.onnx" # onnx 모델 위치
converter = "onnx_model/kure-v1-uint8.onnx" # 저장할 모델 위치
quantize_dynamic(output, converter, weight_type=QuantType.QUInt8)
완료되면 kure-v1-unit8.onnx 파일이 생성된다. (2.27G -> 500M로 대략 25%로 줄어든 모델이 생성된다. )
이제 변환된 파일을 다시 서버에서 실행하면 된다.
from transformers import AutoTokenizer
import numpy as np
import onnxruntime as ort
# ONNX 모델 로드
onnx_model_path = "/content/kure_v1-uint8.onnx"
tokenizer = AutoTokenizer.from_pretrained("nlpai-lab/KURE-v1")
ort_session = ort.InferenceSession(onnx_model_path, providers=['CPUExecutionProvider']) # Specify CPUExecutionProvider for CPU inference
# 샘플 텍스트
text = "안녕하세요, 챗봇 개발 중입니다."
inputs = tokenizer(text, return_tensors="np", padding=True, truncation=True, max_length=128) # Use 'np' for NumPy arrays
input_names = [input.name for input in ort_session.get_inputs()]
ort_inputs = {
input_names[0]: inputs['input_ids'],
input_names[1]: inputs['attention_mask']
}
outputs = ort_session.run(None, ort_inputs)
embedding = outputs[0]
embedding = np.mean(embedding, axis=1)
print("벡터 크기:", embedding.shape)
print("임베딩 벡터:", embedding)
사용된 메모리를 확인해 보면 1.5G정도 사용되서 t2.small 에서도 돌아가는 모델을 만들수 있다. (프리티어에서는 안되네...흠..)
1부 끝!