OpenAI Embedding encoding_format

JunMyung Lee·2024년 4월 1일
0

LLM

목록 보기
4/7

OpenAItext-embedding모델을 통해서 임베딩처리를 하려고 할때 다음과 같은 옵션이 존재한다.

Request body

ParameterRequiredDefaultSummary
input문자열 또는 토큰 배열로 인코딩된 포함할 텍스트를 입력합니다. 단일 요청에 여러 입력을 포함하려면 문자열 배열 또는 토큰 배열 배열을 전달하세요. 입력은 모델의 최대 입력 토큰(text-embedding-ada-002의 경우 8192개 토큰)을 초과할 수 없고, 빈 문자열일 수 없으며, 배열은 2048차원 이하여야 합니다.
model사용할 모델의 ID입니다. 모델 목록 API를 사용하여 사용 가능한 모든 모델을 확인하거나 모델 개요에서 해당 모델에 대한 설명을 볼 수 있습니다.
encoding_formatfloat임베딩을 반환할 형식입니다. float 또는 base64일 수 있습니다.
dimensions결과 출력 임베딩이 가져야 하는 차원 수입니다. text-embedding-3 이상 모델에서만 지원됩니다.
userOpenAI가 남용을 모니터링하고 감지하는 데 도움이 될 수 있는 최종 사용자를 나타내는 고유 식별자입니다.

이때 1536 벡터 기준, response 되어지는 글자수의 차이가 존재한다.

  • Response float: 45661
  • Response base64: 8459

명확한 글자수의 차이로 인하여, base64방식으로 임베딩값을 받고, 코드내에서 Decoding하고 ES에 색인처리를 하려고 했다. 이때 일반적인 base64 decoding으로는 깨진 글자로 나오게 되며 이에 따라 조사한 방식을 공유한다.

Request base64 - https://api.openai.com/v1/embeddings

{
    "input": "변환 문자열",
    "model": "text-embedding-3-small",
    "dimensions": 1536,
    "encoding_format": "base64"
}

Response base64

{
    "object": "list",
    "data": [
        {
            "object": "embedding",
            "index": 0,
            "embedding": "e5cjPb7goby3xAa9es4JurG7KDkfgRa9fiWxv...8SA8nngBPKijM70egyk8OEySvM8n7zxePho9QPxzvPYX5DwflmY82fGavHWOxbsqqjW8K6iiu3kc07srPnw94tQ8vWt4Y7wO0CK9sD7FvO8uibxgzrq7zyfvO2BPxLsGy2o8M0Ghu+1ngrvTNPO6/TP/O+ZiSrt0kNg776+SPGnmLzw/aC26"
        }
    ],
    "model": "text-embedding-3-small",
    "usage": {
        "prompt_tokens": 205,
        "total_tokens": 205
    }
}

Java
원본 코드 주소

String str = response.get("data").get(0).get("embedding");
// Base64 디코드하여 바이트 배열로 변환
byte[] decodedBytes = Base64.getDecoder().decode(str);

// 바이트 배열을 FloatBuffer로 변환
ByteBuffer byteBuffer = ByteBuffer.wrap(decodedBytes);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);

FloatBuffer floatBuffer = byteBuffer.asFloatBuffer();

// FloatBuffer에서 List<Float> 생성
List<Float> floatList = new ArrayList<>();
while (floatBuffer.hasRemaining()) {
	floatList.add(floatBuffer.get());
}

System.out.println(floatList);

여기서 가장 중요한 부분이다.

byteBuffer.order(ByteOrder.LITTLE_ENDIAN);

Java에서 decode를 하고, float일때의 데이터와 비교하면 데이터가 맞질 않는다. 실제 코드와 다른 부분이 뭔지 조사를 하다가 발견하였으며 해당 코드의 설명을 GPT에 문의하면 나오는 답변은 다음과 같다.

byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 코드는 ByteBuffer 객체의 바이트 순서를 리틀 엔디언(little-endian)으로 설정하는 명령입니다. 이 명령은 ByteBuffer가 바이트 배열을 해석할 때 사용하는 바이트 순서를 지정합니다.

엔디언(Endian)은 다중 바이트 값을 메모리에 저장하거나 읽을 때 바이트의 순서를 나타내는 용어입니다. 주로 두 가지 엔디언 방식이 사용됩니다:

* 빅 엔디언(Big-endian): 가장 큰 바이트(상위 바이트)가 가장 낮은 메모리 주소에 저장됩니다. 예를 들어, 32비트 정수 0x12345678을 빅 엔디언으로 메모리에 저장하면 12 34 56 78 순서로 저장됩니다.
* 리틀 엔디언(Little-endian): 가장 작은 바이트(하위 바이트)가 가장 낮은 메모리 주소에 저장됩니다. 동일한 32비트 정수 0x12345678을 리틀 엔디언으로 메모리에 저장하면 78 56 34 12 순서로 저장됩니다.

Java의 ByteBuffer 클래스는 기본적으로 빅 엔디언을 사용합니다. 그러나, 데이터가 리틀 엔디언으로 인코딩된 경우, ByteBuffer의 바이트 순서를 리틀 엔디언으로 변경해야 올바르게 데이터를 해석할 수 있습니다.

byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 코드를 사용하면, ByteBuffer가 리틀 엔디언 방식으로 바이트 배열을 해석하도록 설정됩니다. 이는 다음과 같은 경우에 필요합니다:

* 데이터가 리틀 엔디언 시스템에서 생성되었을 때
* 네트워크를 통해 전송된 데이터가 리틀 엔디언으로 인코딩되었을 때
* 파일이나 다른 데이터 소스가 리틀 엔디언 형식으로 저장되었을 때

이 설정을 적용한 후에는 ByteBuffer를 통해 읽는 모든 다중 바이트 값(예: short, int, float, long, double 등)이 리틀 엔디언으로 해석되어 올바른 값을 반환하게 됩니다.

따라서, byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 코드를 적용하니 문제가 해결되었다면, 이는 원본 데이터가 리틀 엔디언 형식으로 인코딩되어 있었음을 의미합니다.

저장할 때 상위 바이트. 큰 쪽을 먼저 저장하는 것을 빅 엔디안(Big Endian)
저장할 때 하위 바이트. 작은 쪽을 먼저 저장하는 것을 리틀 엔디안(Little Endian)

profile
11년차 검색개발자 입니다. 여러 지식과 함께 실제 서비스를 운영 하면서 발생한 이슈에 대해서 정리하고 공유하고자 합니다.

0개의 댓글

관련 채용 정보