각 코드베이스별로 임베딩 공간을 선형 변환(linear transformation)
TMTCursor의 코드베이스 인덱싱 시스템에서 **"임베딩 공간을 선형 변환(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$를 적용하여 임베딩을 최적화
- 이렇게 하면 코드베이스마다 독립적인 벡터 공간에서 검색할 수 있어 정확도가 높아짐
이 방식은 검색 성능을 개선하고, 코드베이스의 특성을 반영한 맞춤형 검색을 가능하게 합니다! 🚀