각 코드베이스별로 임베딩 공간을 선형 변환(linear transformation)

TMT

Cursor의 코드베이스 인덱싱 시스템에서 **"임베딩 공간을 선형 변환(linear transformation)하여 저장한다"**는 것은 코드베이스마다 독립적인 벡터 공간을 만들고, 임베딩을 변환하여 저장하는 방법을 사용한다는 의미입니다.


✅ 1. 선형 변환(Linear Transformation)이란?

**선형 변환(Linear Transformation)**은 벡터 공간의 점을 특정한 방식으로 변환하는 연산을 의미합니다.
수학적으로, 벡터 ( x )에 대해 선형 변환을 수행하는 식은 다음과 같습니다.

$$x = Ax + b$$

여기서:

  • $A$ : 변환 행렬 (Matrix) → 특정한 선형 변환을 수행하는 행렬
  • $b$ : 변환 벡터 (Bias) → 추가적인 이동을 적용하는 벡터
  • $x$ : 원래의 벡터 (임베딩)
  • $x'$ : 변환된 벡터 (새로운 벡터 공간에서 표현됨)

즉, 원래의 벡터 임베딩을 코드베이스마다 다르게 변환하여 저장하는 방법입니다.


✅ 2. 왜 코드베이스마다 임베딩 공간을 변환할까?

각 코드베이스는 도메인, 네이밍 컨벤션, 스타일, 라이브러리 사용 방식이 다릅니다.
따라서, 모든 코드베이스를 동일한 벡터 공간에서 비교하면 비효율적일 수 있습니다.

🚀 선형 변환을 사용하는 이유

1️⃣ 코드베이스 간의 독립성 유지

  • 각 코드베이스는 독립적인 의미 체계를 가지므로, 벡터 공간을 개별적으로 조정하여 최적화할 필요가 있습니다.

2️⃣ 벡터 임베딩을 표준화(Standardization)

  • 코드 스타일이 다른 프로젝트 간 비교가 어려우므로, 벡터를 변환하여 일관된 공간에서 학습할 수 있습니다.

3️⃣ 기존의 임베딩 모델을 유지하면서 코드베이스별 최적화

  • 임베딩 모델을 새로 학습할 필요 없이, 선형 변환 행렬 $A$ 를 학습하여 적용하면 됩니다.

4️⃣ 팀 간 협업에서 일관된 임베딩 생성

  • 같은 코드베이스를 여러 사람이 공유하는 경우, 같은 방식으로 변환된 임베딩을 사용하면 일관된 검색 결과 제공이 가능합니다.

✅ 3. 코드베이스별 선형 변환 적용 방식

📌 (1) 코드 조각을 임베딩으로 변환

각 코드 조각을 벡터로 변환하는 기본적인 과정입니다.

const { OpenAI } = require("openai");

const openai = new OpenAI({ apiKey: "your-api-key" });

async function getEmbedding(text) {
  const response = await openai.embeddings.create({
    model: "text-embedding-ada-002",
    input: text,
  });
  return response.data[0].embedding; // 500차원 벡터
}

📌 (2) 코드베이스별 선형 변환 적용

각 코드베이스에 대해 변환 행렬 $A$ 와 이동 벡터 $b$ 를 적용합니다.

function applyLinearTransformation(embedding, A, b) {
  return embedding.map((value, i) => A[i] * value + b[i]);
}

// 예제: 변환 행렬 A와 벡터 b
const A = Array(500).fill(1.05); // 예제: 1.05배 스케일 조정
const b = Array(500).fill(-0.02); // 예제: -0.02만큼 이동

// 원본 임베딩
const originalEmbedding = await getEmbedding("function add(a, b) { return a + b; }");

// 선형 변환 적용
const transformedEmbedding = applyLinearTransformation(originalEmbedding, A, b);

이렇게 하면 코드베이스마다 고유한 변환을 적용하여 벡터 공간을 조정할 수 있음


📌 (3) 변환된 임베딩을 DynamoDB 및 벡터 DB에 저장

const { ChromaClient } = require("chromadb");

async function storeTransformedEmbedding(filePath, transformedEmbedding) {
  const client = new ChromaClient();
  const collection = await client.getOrCreateCollection({ name: "code-index" });

  await collection.add({
    ids: [filePath],
    embeddings: [transformedEmbedding],
    metadatas: [{ filePath }]
  });
}

// 저장 실행
storeTransformedEmbedding("src/utils/math.ts", transformedEmbedding);

✅ 4. 선형 변환을 적용한 벡터 검색

1️⃣ 사용자 입력을 벡터화한 후, 코드베이스별 변환 행렬 $A$와 $b$를 적용
2️⃣ 변환된 벡터를 사용하여 벡터 DB에서 검색 수행

async function searchInCodebase(query, A, b) {
  const client = new ChromaClient();
  const collection = await client.getCollection({ name: "code-index" });

  // 쿼리 변환
  const queryEmbedding = await getEmbedding(query);
  const transformedQuery = applyLinearTransformation(queryEmbedding, A, b);

  // 벡터 DB 검색
  const results = await collection.query({
    queryEmbeddings: [transformedQuery],
    nResults: 3,
  });

  return results.documents;
}

// 실행 예제
searchInCodebase("add 함수 찾기", A, b).then(console.log);

각 코드베이스에 맞게 변환된 벡터를 사용하여 검색 정확도를 향상시킴


✅ 5. 결론

💡 "임베딩 공간을 선형 변환하여 저장한다"는 의미는?

  • 각 코드베이스별로 벡터 공간을 다르게 변환하여 저장하는 방식
  • 변환 행렬 $A$와 이동 벡터 $b$를 적용하여 임베딩을 최적화
  • 이렇게 하면 코드베이스마다 독립적인 벡터 공간에서 검색할 수 있어 정확도가 높아짐

이 방식은 검색 성능을 개선하고, 코드베이스의 특성을 반영한 맞춤형 검색을 가능하게 합니다! 🚀

Edit this page