Eat Study Love

먹고 공부하고 사랑하라

Data Science/Research

SQL2NL 모델 실험진행

eatplaylove 2025. 7. 15. 11:27

https://eglife.tistory.com/359

 

SQL2NL 모델 개선방안에 대한 검토 - Prompt & RAG

https://eglife.tistory.com/358 연구주제 고찰(2)MCP를 많이 파봤지만, 일단 연구주제는 SQL2NL이 위주였으면 한다는 교수님의 바람이 있었다. 그래야 연구실에서 활발히 진행중인 NL2SQL 연구와 엮어서 또

eglife.tistory.com

지난 시간에, 실험 기획은 다 짰다.

이제 본격적으로 실험을 슉슉슉 진행해보자.

 

실험준비


1. SQL 간 Strucutre Similarity 계산 Equation 확립

2. Data Set 준비

 

실험절차


1. Data Set 준비

-> BIRD / Spider

 

2. Data-Set Vector Embedding

-> 문제발생😥 : 나는 SQLGlot을 이용해 SQL을 Structure Parse 된 형태로 나누고 이것을 추후 Similiarity계산에 이용하려고 했는데 일단 'Vector DB'라는 것이 Data를 임베딩 형태로 저장하는건데, 이건 그냥 구분 없는 숫자의 형태로 저장된다. 그러면 Vector DB에서 'Parse 되었다.'라는 것이 유의미 할까? 아니면 아싸리 Vector DB 임베딩하는 것 자체를 건드려야 할 것으로 보인다. 이를테면, 내가 이 전 글에서 적었듯이 SQL Syntax의 사용이 유사하고 Table & Column 명이 비슷한 것들끼리 근처에 Cluster 형성하도록 Vector 임베딩을 하는 것!

이렇게 하면, RAG를 이용해 Vector DB Search를 할 때는 그냥 Jacard / Cosine Similiary만 계산해서 TOP-K 뽑아내도 유의미할 것으로 예상한다.

 

3. LLM 준비?

-> LLama 3.2 기준 Parameter 적은 거 / 많은 거 실험 해볼 예정 10b, 70b 정도?

 

4. Vector DB( 2번 Embedding ) RAG + TOP-K Prompt + Schema Linking(Default) 실험진행

 a) Naive LLM

 b) Naive LLM + 일반 RAG(Gold SQL를 Text기반 Jaccard or Cosine 유사도로 계산)

 c) Naive LLM + Stucture Based RAG(ours)


문제점을 해결하자 : 2. Data-Set Vector Embedding

SQLGlot Library를 보면, SQL간 Semantic 유사도를 측정하는 diff.py라는 파일이 있다.

diff(SQLGlot_diff_function).py
0.02MB

https://github.com/tobymao/sqlglot/blob/main/posts/sql_diff.md

 

sqlglot/posts/sql_diff.md at main · tobymao/sqlglot

Python SQL Parser and Transpiler. Contribute to tobymao/sqlglot development by creating an account on GitHub.

github.com

 

이 코드는 Change Distiller 알고리즘을 바탕으로, SQL의 AST(Abstract Syntax Tree)구조를 비교해서 유사도를 계산하는 것이다.

 

알고리즘의 개요는 아래와 같다.

ChangeDistiller는 두 SQL을 트리 구조로 파싱한 후, 다음 단계로 유사도를 계산합니다:
  1. Leaf Matching: 리프 노드들을 먼저 매칭
  2. Inner Node Matching: 리프 매칭 결과를 바탕으로 내부 노드 매칭
  3. Edit Script Generation: 변환에 필요한 편집 작업 도출

여기서, SQL의 Grammer적인 유사도인 Syntax 말고 Semantic 유사도를 계산하기 위한 Matrix로 Dice Coefficient를 사용한다.

def _dice_coefficient(self, source: exp.Expression, target: exp.Expression) -> float:
    source_histo = self._bigram_histo(source)
    target_histo = self._bigram_histo(target)
    
    # 2 * overlap / (total_grams)
    return 2 * overlap_len / total_grams

이 함수는 SQL 표현식을 문자열(TEXT)로 변환한 후 bigram(2글자 단위)으로 분해해서 유사도를 계산한다.

ex) Student와 Study는 'st','tu','ud' 가 같기에 유사도가 높다고 판단할 수 있다.

 

Syntax유사도는, _is_same_type 이라는 함수를 통해서 SQL의 구문타입(SELECT, JOIN [ JOIN의 종류까지 비교], Where 등)을 비교 한다.

def _is_same_type(source: exp.Expression, target: exp.Expression) -> bool:
    if type(source) is type(target):
        if isinstance(source, exp.Join):
            return source.args.get("side") == target.args.get("side")
        return True
    return False

 

결론은,

 

이 Strcuture based 함수를 필두로 데이터들을 Vector DB에 임베딩시켜야 한다.

내가 원하는 '기준'으로 Data들끼리 유사한 정도를 Vector DB에 표현하기 위해선, 'Embedding 함수'를 건드려야 한다.

 

일단, 갖고 있는 Gold SQL<->NL Pair data에 대해서 Vector DB 임베딩을 먼저 시도하자. (Structure Based Diff 계산)

그게 되면, Gold SQL<->NL Pair를 그냥 Text based로도 Vector DB 임베딩을 시도하자.

 

그리고 Data Set을 준비해서 실험을 돌리자.

일단, Data Set은 같은 BIRD 같은 DB에서 RAG에 1000ea, 실험은 RAG에 없는 100ea로 진행하면 될듯.


 

근데, VectorDB Embedding하다보니까 드는 의문,

 

굳이 VectorDB, RAG가 필요한가,,? 물론 미래에 Data 개수가 늘어나는 시대 or 대기업에선 필요할 수 있다.

 

하지만, 실험에선 그냥 DB에 DataSet(Gold SQL & NL)만 나열해놓고, input SQL과 Structure based score 높은 TOP-K 추출하면 어찌어찌 실험은 돌아갈 거 같다.

 

GPT를 통해 만든 코드기준으론, N개의 Gold Data가 있을 때 N개를 N개끼리 모두 유사도 비교한 N*N Matrix를 만들고, New input SQL에 대해서 이 N개와 유사도를 계산한 뒤에 Top-K를 추출했다.

 

이러면, 새로 Top-K 추출할 때마다 계산을 N번 해야해서 시간이점이 없다. 즉, Vector Embedding을 하는 의미가 없다.

(하지만, 일단 여기까지 기준으로 실험이라도 진행해보려고 한다. 과연 SQL Structure based evaluation이 Few-shot 대표 SQL 추출에 유의미한지 판단용)

 

FutreWork 느낌의 Vector Embedding 결론,

 

지금 VectorDB 전문가 소견에 따르면, VectorDB 업계는 결국 Vector EmbeddingVector Search 두 가지 영역으로 나눌 수 있다. 전자는 거의 NEW LLM을 만드는 듯한 난이도를 자랑한다. Hugging Face 등에서 BERT 등으로 임베딩 알고리즘을 만드는데.. 이게 생각보다 Deep한 영역이다. Vector Search는 HNSW, IVF 등 다양한 알고리즘이 있는데 Vector 임베딩과 관계 없이 사용할 수 있는 스킬이고, 시중에 Open된 알고리즘이라 취사선택하기 편하다.

 

Embedding을 굳이 굳이 하려면 내 나름의 기준으로 SQL을 Vector Embedding을 해야한다. 근데 사실 이건 검증해야할 요소가 너무 많긴 하다.

 

방법은, 수많은 Gold SQL 中 200ea 처럼 대표적인 Data 일부만을 Standard로 삼아서 모든 Gold Data를 200차원 임베딩을 시켜 VectorDB에 저장하고, Generate이 필요한 Input SQL도 200차원의 Vector로 Embedding시키고 Cosine 계산(Vector끼리 유사도 계산 by HNSW, IVF 등등..)을 통해 TOP-K를 추출한다.

 

또는, 내 나름의 SQL 구분 Feature를 정한다. 예를 들면, 아래와 같이 고정차원의 Embedding 기법을 사용하는 것이다.

# 예시: 8차원 벡터
[has_group_by, has_having, has_limit, num_joins, has_between, has_like, num_conditions, num_agg_funcs]

SELECT SUM(salary) FROM emp WHERE age BETWEEN 30 AND 40 GROUP BY dept_id
-> 벡터변환
[1, 0, 0, 0, 1, 0, 1, 1]  → float vector

 

이런식으로 하면 Gold SQL이 몇 개든 관계 없이 항상 동일방식으로 Vector 생성이 가능하다.

이러면, VectorDB + RAG를 이용하는 장점인 Search 속도를 향상시킬 수 있다.

(예를 들어, Gold SQL이 100M ea 있어도 모든 Data들을 5차원으로 Embedding하니까 계산시간이 적고 무엇보다!!! 새로운 Input SQL을 Embedding할 때 100M번의 계산이 필요한 게 아니라 5차원 계산만 하면 된다. 그리고 Vector Data끼리 Top-K를 추출하는 Cosine계산은 계산속도가 빠르다..!)


결론, To-do list

VectorDB + Embedding은 일단 놔두고 Gold SQL 1.5K에다가 Iterative하게 유사도검색을 돌려 TOP-K를 Few-shot으로 뽑는 것이 SQL2NL Task 정확도 향상에 효과가 있는지 Check해야겠다. 비교군은 아래와 같다.

 

1. Naive Llama

2. Llama + 일반 Text끼리 유사도 비교 TOP-K

3. Llama + SQLGlot Structure based calculation 기반 유사도 비교 TOP-K

 

Schema Linking은 기본으로 깔고, 희망사항은 1->2->3 번순으로 정확도가 올라가는 것이다.

SQL2NL 정확도 지표는 BERT Recall / BART Score에서 BLEU , METEOR  사용!

 

반드시, VectorDB + RAG에 대한 설명을 Future Work에 넣을 것..!