반응형

🤔 1. IDF는 왜 필요한가? (제안 동기)

IDF를 이해하려면, 먼저 이전에 배운 **TF(Term Frequency, 단어 빈도)**의 한계를 알아야 합니다.

  • TF의 한계: 불용어 문제
    • TF는 단순히 '하나의 문서 안에서' 특정 단어가 얼마나 자주 등장하는지를 나타냅니다.
    • 이 때문에 'the', 'a', 'is'나 한국어의 '은', '는', '이', '가'와 같은 **불용어(Stopword)**들은 거의 모든 문서에서 높은 TF 값을 갖게 됩니다.
    • 하지만 이런 불용어들은 문서의 핵심 주제를 파악하는 데 전혀 도움이 되지 않습니다. 오히려 중요한 단어들의 가치를 희석시킬 뿐이죠.
  • 필요성: 단순히 한 문서 내에서의 빈도수만 볼 것이 아니라, 전체 문서들 사이에서 이 단어가 얼마나 특별하고 희귀한 정보인지를 측정할 척도가 필요하게 되었습니다. 이것이 바로 IDF가 등장한 배경입니다.

📖 2. IDF란 무엇인가? (정의)

**IDF(Inverse Document Frequency, 역문서 빈도)**는 특정 단어가 전체 문서 집합(코퍼스)에서 얼마나 공통적으로 사용되는지를 나타내는 값의 역수입니다. 즉, 단어의 **'희귀성' 또는 '중요도'**를 나타내는 지표입니다.

  • 핵심 원리:
    • 많은 문서에서 등장하는 단어일수록 IDF 값은 낮아집니다. (예: 'the', '조사')
    • 적은 수의 특정 문서에서만 등장하는 단어일수록 IDF 값은 높아집니다. (예: '트랜스포머', '어텐션' 같은 전문 용어)
  • 공식:
    • : 단어 (term)
    • : 전체 문서의 수
    • : 단어 t가 등장한 문서의 수 (Document Frequency)
  • 해석:
    • IDF 값이 높다 → 그 단어는 소수의 문서에만 등장하는 희귀하고 중요한 키워드일 확률이 높다.
    • IDF 값이 낮다 → 그 단어는 여러 문서에 공통적으로 등장하는 일반적이고 중요도가 낮은 단어일 확률이 높다.

⚖️ 3. Log는 왜 사용하는가? (Log 보정의 필요성, ★시험 핵심★)

IDF 공식에 log가 붙어있는 것은 매우 중요한 이유가 있습니다. 값의 스케일을 완화하여 균형을 맞추기 위함입니다.

슬라이드의 예시를 통해 log가 있을 때와 없을 때의 차이를 극명하게 비교해 보겠습니다. (전체 문서 수 이라고 가정)

log가 없는 경우:

단어 등장 문서 수 (df) IDF 값
word1 1 1,000,000
word2 100 10,000
word3 1,000 1,000
word4 10,000 100
word5 100,000 10
word6 1,000,000 1
Sheets로 내보내기
  • 문제점: 가장 희귀한 단어(word1)의 IDF 값이 1,000,000으로, 가장 흔한 단어(word6)의 IDF 값 1과 비교했을 때 차이가 너무 극심합니다. 이렇게 되면 나중에 TF와 IDF를 곱할 때, IDF 값이 전체 점수를 좌지우지하게 되어 TF의 의미가 거의 사라지게 됩니다. 희귀 단어에 너무 과도한 가중치를 부여하는 것이죠.

log를 적용한 경우:

(※ 편의상 상용로그()로 계산)

단어 등장 문서 수 (df) IDF 값
word1 1
word2 100
word3 1,000
word4 10,000
word5 100,000
word6 1,000,000
Sheets로 내보내기
  • 결과: 가장 희귀한 단어의 IDF는 6, 가장 흔한 단어는 0이 되었습니다. 값의 차이가 훨씬 부드럽고 완만해졌죠.
  • 핵심 역할: 이처럼 로그 함수는 값이 폭발적으로 커지는 것을 막아주는 '댐퍼(damper)' 역할을 합니다. 이를 통해 IDF 값이 지나치게 커지는 것을 방지하고, 단어의 중요도를 더 합리적인 스케일로 조정해 줍니다.

🎓 시험 대비 최종 요약

  • IDF의 목적: TF만 사용했을 때 발생하는 불용어 문제를 해결하고, 단어의 **전체적인 중요도(희귀성)**를 측정하기 위함.
  • IDF의 의미: 값이 높을수록 특정 주제를 대표하는 중요한 키워드, 값이 낮을수록 변별력 없는 일반적인 단어.
  • Log를 사용하는 이유 (★매우 중요★): IDF 값의 스케일을 줄여(완화하여), 희귀 단어에 부여되는 과도한 가중치를 막고, 전체 점수를 더 균형 있게 만들기 위함.

💡 1. TF-IDF란 무엇이고 왜 사용하는가?

**TF-IDF(Term Frequency-Inverse Document Frequency)**는 단어의 중요도를 평가하는 통계적 가중치입니다. 이름 그대로 두 가지 값을 곱해서 계산합니다.

TF-IDF = TF × IDF

  • TF (Term Frequency, 단어 빈도): 특정 문서 내에서 단어가 얼마나 자주 등장하는지 (문서 내 중요도)
  • IDF (Inverse Document Frequency, 역문서 빈도): 전체 문서들 중에서 해당 단어가 얼마나 희귀한지 (전체적인 희귀성)

핵심 아이디어 🧠 단순히 등장 횟수만 세는 것을 넘어, "모든 문서에 자주 나오는 일반적인 단어(예: a, the, 은, 는)는 중요도를 낮추고, 특정 문서에만 집중적으로 등장하는 희귀한 단어는 중요도를 높이자!" 는 것입니다. 이를 통해 각 문서를 가장 잘 대표하는 핵심 키워드, 즉 **특징점(feature)**을 추출할 수 있습니다.


🔢 2. 예시로 완벽 이해하는 TF-IDF 계산 과정

슬라이드에 나온 4개의 문서를 예시로 처음부터 끝까지 계산해 보겠습니다.

  • 문서1: 먹고 싶은 사과
  • 문서2: 먹고 싶은 바나나
  • 문서3: 길고 노란 바나나 바나나
  • 문서4: 저는 과일이 좋아요

1단계: TF (단어 빈도) 계산하기

각 문서에서 단어의 등장 횟수를 해당 문서의 전체 단어 수로 나누어 정규화합니다.

  • 문서1 (총 3단어): '먹고', '싶은', '사과'는 각각 1번씩 등장 → TF는 모두 1/3
  • 문서3 (총 4단어): '길고', '노란'은 1번, '바나나'는 2번 등장 → '길고', '노란'의 TF는 1/4, '바나나'의 TF는 2/4

2단계: IDF (역문서 빈도) 계산하기

전체 문서 수(4개)를 기준으로 각 단어의 희귀성을 측정합니다.

  • 희귀한 단어 (1개 문서에만 등장): '과일이', '길고', '노란', '사과', '저는', '좋아요'
    • IDF =
  • 일반적인 단어 (2개 문서에 등장): '먹고', '바나나', '싶은'
    • IDF =

IDF 값이 높을수록 희귀하고 중요한 단어라는 것을 알 수 있습니다.

3단계: TF와 IDF 곱하기 (최종 TF-IDF 점수)

이제 위에서 구한 TF와 IDF를 단어별로 곱해줍니다.

  • 문서1의 "사과" 점수:
    • TF(1/3) × IDF(1.386) ≈ 0.462
    • (해석: "사과"는 이 문서에서 중요하고(TF), 전체적으로도 희귀해서(IDF) 매우 높은 점수를 받음)
  • 문서2의 "먹고" 점수:
    • TF(1/3) × IDF(0.693) ≈ 0.231
    • (해석: "먹고"는 이 문서에 있지만, 다른 문서에도 등장하는 일반적인 단어라 상대적으로 낮은 점수를 받음)
  • 문서3의 "바나나" 점수:
    • TF(2/4) × IDF(0.693) ≈ 0.347
    • (해석: "바나나"는 이 문서에서 매우 중요하지만(TF가 큼), 다른 문서에도 등장해서(IDF가 낮음) 적당한 점수를 받음)

이 과정을 모든 문서의 모든 단어에 대해 반복하면, 각 문서를 나타내는 고유한 TF-IDF 벡터가 완성됩니다.

 

🎓 시험 대비 최종 요약

  • TF-IDF란? 단어의 **지역적 중요도(TF)**와 **전체적 희귀성(IDF)**을 함께 고려한 가중치.
  • 핵심 목적: 불용어처럼 변별력 없는 단어의 가중치는 낮추고, 특정 문서의 주제를 잘 나타내는 키워드의 가중치는 높이기 위함.
  • 계산 순서: TF 계산 → IDF 계산 → 두 값 곱하기.
  • 최종 활용: 완성된 TF-IDF 벡터는 문서 유사도 계산(코사인 유사도 등), 검색 순위 결정, 텍스트 분류 모델의 입력 특징으로 사용됨.

🤔 4. TF-IDF의 한계점 (★시험 핵심★)

TF-IDF는 강력한 기법이지만, 다음과 같은 명확한 한계점들을 가지고 있으며, 이 한계점을 극복하기 위해 후속 모델들이 제안되었습니다.

한계점 1: 문맥 정보 부재 (Lack of Context)

  • 문제점: TF-IDF는 결국 단어의 빈도수에만 의존하는 Bag-of-Words 기반 모델입니다. 따라서 단어의 순서나 단어 간의 의미적 관계(유사성)를 전혀 고려하지 못합니다.
  • 다의어(Polysemy) 문제: "돈을 찾으러 은행에 갔다"와 "강둑 은행에 앉았다"에서 '은행'은 전혀 다른 의미지만, TF-IDF는 이 둘을 완전히 동일한 단어로 취급하여 같은 벡터 값을 부여합니다.
  • 유의어(Synonym) 문제: '자동차'와 '차'는 의미가 매우 유사하지만, TF-IDF는 이 둘을 전혀 다른 단어로 취급합니다.

한계점 2: TF의 선형적 증가 문제 (Unbounded TF Scaling)

  • 문제점: TF 값은 단어가 반복해서 등장할수록 선형적으로 무한정 증가할 수 있습니다. 예를 들어 한 문서에 '사랑'이라는 단어가 10번 나온 것과 100번 나온 것의 TF 값은 10배 차이가 납니다.
  • 현실: 하지만 실제로 단어의 중요도는 그렇게 선형적으로 증가하지 않습니다. '사랑'이라는 단어가 10번 정도 나오면 이미 그 문서의 핵심 단어임이 분명하며, 100번 나온다고 해서 중요도가 10배가 되는 것은 아닙니다. **중요도는 어느 수준에 이르면 포화(saturate)**됩니다.
  • 해결 방안: TF 값에 로그(log)를 씌우거나, 특정 횟수 이상부터는 가중치가 완만하게 증가하도록 설계해야 합니다. 이 아이디어를 적용한 대표적인 모델이 BM25입니다.

한계점 3: 문서 길이 문제 (Document Length Bias)

  • 문제점: 다른 조건이 같다면, 문서의 길이가 길수록 특정 단어가 더 많이 등장할 확률이 높고, 더 다양한 단어를 포함하게 됩니다. 이 때문에 짧은 문서와 긴 문서의 TF-IDF 값을 직접 비교하는 것은 불공평할 수 있습니다.
  • 해결 방안: 문서의 길이를 일종의 페널티로 사용해 보정해야 합니다. 즉, 문서의 길이를 전체 문서의 평균 길이와 비교하여 점수를 조정하는 방식이 필요합니다. 이 또한 BM25 알고리즘의 핵심 아이디어 중 하나입니다.
반응형
반응형

벡터 유사도'는 텍스트를 벡터로 변환한 후, 그 벡터를 활용하는 핵심적인 개념입니다. 시험에 완벽하게 대비할 수 있도록 슬라이드 내용을 바탕으로 개념부터 원리, 그리고 중요한 측정 방법까지 체계적으로 설명해 드릴게요.


🗺️ 1. 기본 무대: 벡터 공간 (Vector Space)

벡터 유사도를 이해하려면, 먼저 단어들이 존재하는 공간인 **'벡터 공간'**에 대한 개념부터 알아야 합니다.

  • 정의: 우리가 사용하는 모든 단어(토큰)들의 집합, 즉 어휘집(Vocabulary)이 하나의 다차원 공간을 형성한다고 생각할 수 있습니다. 이것이 바로 '벡터 공간'입니다.
  • 단어의 표현: 어휘집에 있는 각각의 단어(토큰)는 이 공간을 떠다니는 하나의 벡터(vector), 즉 방향과 크기를 가진 화살표로 표현됩니다.
  • 핵심 아이디어: 이 공간에서 단어들은 아무렇게나 흩어져 있는 것이 아닙니다. 의미가 비슷한 단어일수록 서로 가까운 곳에 위치하거나 비슷한 방향을 가리키도록 배치됩니다. 예를 들어, 이미지에서 'car'와 'auto'는 'vehicle'이라는 개념 축에서 매우 가까운 방향을 향하고 있습니다. 이것이 바로 자연어를 수학적 표현으로 변환하는 벡터화의 최종 목표입니다.

🎯 2. 유사도 개념: 왜 필요한가?

텍스트를 벡터 공간에 배치했다면, 이제 그들 사이의 관계를 측정해야 합니다.

  • 필요성: "두 단어(또는 문서)가 얼마나 비슷한가?"라는 질문에 답하기 위해, 두 벡터가 얼마나 가까운지를 나타내는 **정량적인 수치(a numerical score)**가 필요합니다. 이것이 바로 **유사도(Similarity)**입니다.
  • 주요 활용 분야:
    • 정보 검색 (Information Retrieval): 사용자가 '빠른 자동차'를 검색했을 때, 검색 엔진은 '빠른 자동차' 벡터와 가장 유사한(가까운) 벡터를 가진 문서들을 찾아서 보여줍니다. '신속한 차량'이라는 문서가 검색 결과에 포함될 수 있는 이유입니다.
    • 추천 시스템 (Recommendation Systems): 여러분이 특정 영화를 좋아했다면, 시스템은 그 영화의 벡터와 유사한 벡터를 가진 다른 영화들을 여러분에게 추천해 줍니다.
    • 비슷한 의미의 텍스트 찾기: 뉴스 기사 군집화(클러스터링)나 표절 탐지 등 비슷한 내용을 가진 문서를 찾는 데 활용됩니다.

📏 3. 유사도 개념: 어떻게 측정하는가? (★시험 핵심★)

벡터 간의 거리를 재는 방법은 여러 가지가 있지만, 자연어 처리에서는 특별히 중요한 원칙이 있습니다.

문제점: 스케일링(Scaling) 문제

단순히 두 벡터 간의 직선거리(유클리드 거리)를 재면 문제가 생길 수 있습니다. 왜냐하면 벡터의 **'크기(길이)'**가 의미를 왜곡시킬 수 있기 때문입니다.

  • 예를 들어, BoW 벡터에서 한 문서에는 '행복'이라는 단어가 1번, 다른 긴 문서에는 10번 등장할 수 있습니다. 이 경우, 두 번째 문서의 '행복' 관련 벡터의 크기(길이)가 훨씬 길어집니다.
  • 하지만 두 문서 모두 '행복'이라는 **같은 개념(의미)**을 이야기하고 있습니다. 단순히 빈도수가 높다고 해서 벡터의 크기가 커지는 이 스케일링 문제는, 두 벡터가 실제로는 같은 의미임에도 불구하고 멀리 떨어져 있는 것처럼 보이게 만듭니다.

해결책: 크기(Magnitude)는 무시하고, 방향(Direction)으로 승부하라!

이 스케일링 문제를 해결하기 위해, 기계학습에서는 벡터의 '크기'가 아닌 '방향'을 기반으로 유사도를 측정합니다.

  1. 벡터 정규화 (Vector Normalization): 모든 벡터의 크기(길이)를 강제로 1로 만들어 버립니다. 이렇게 크기가 1인 벡터를 **단위 벡터(Unit Vector)**라고 합니다.
    • 이렇게 하면 모든 벡터의 끝점이 원점으로부터의 거리가 1인 구(sphere)의 표면에 위치하게 됩니다. 이제 벡터의 길이는 모두 같아졌으므로, 벡터들을 구분하는 유일한 기준은 '방향'만 남게 됩니다.
  2. 방향 비교: 이제 두 벡터가 얼마나 유사한지는 두 벡터가 이루는 사이각으로 판단할 수 있습니다.
    • 방향이 거의 같다 (사이각이 0°에 가깝다) → 유사도가 매우 높다.
    • 방향이 직각을 이룬다 (사이각이 90°) → 관련성이 거의 없다.
    • 방향이 정반대다 (사이각이 180°) → 의미가 정반대다.

이 원리를 이용해 유사도를 측정하는 가장 대표적인 방법이 바로 **코사인 유사도(Cosine Similarity)**입니다.


🎓 시험 대비 최종 요약

  • 벡터 공간: 모든 단어(토큰)들이 벡터 형태로 존재하는 다차원 공간.
  • 벡터 유사도: 이 공간에서 두 벡터가 의미적으로 얼마나 가까운지를 나타내는 수치.
  • 측정 핵심 원리 (★매우 중요★):
    • 단순 빈도수 등으로 인한 **크기(길이)의 왜곡(스케일링 문제)**을 피해야 한다.
    • 따라서 모든 벡터의 길이를 1로 **정규화(Normalization)**한 뒤,
    • 벡터 간의 방향이 얼마나 유사한지를 측정한다.
  • 대표적인 방법: 코사인 유사도 (Cosine Similarity).

벡터 유사도를 측정하는 두 가지 대표적인 방법인 유클리드 거리코사인 유사도는 시험에 비교해서 출제하기 아주 좋은 주제입니다. 두 개념을 완벽하게 이해하고 계산까지 할 수 있도록, 슬라이드 내용을 종합해서 상세히 설명해 드릴게요.


📏 1. 유클리드 거리 (Euclidean Distance): 점과 점 사이의 최단 거리

개념

유클리드 거리는 우리가 학창 시절부터 배워온, 지도 위의 두 점 사이의 직선거리를 구하는 것과 똑같습니다. 벡터 공간에서 두 벡터의 끝 점(화살표의 머리)을 잇는 가장 짧은 거리를 측정하는 방식입니다.

해석 방법

  • 거리 기반(Distance-based): 순수하게 '거리'를 측정합니다.
  • 값이 작을수록 유사: 두 벡터 사이의 거리가 가까울수록(값이 작을수록) 더 유사하다고 판단합니다. 거리가 0이면 두 벡터는 완전히 동일한 위치에 있는 것입니다.

예시 계산

슬라이드의 예시로 직접 계산해 보겠습니다.

  • 문장1 벡터 A: [1, 1, 1, 0, 0]
  • 문장2 벡터 B: [0, 1, 0, 1, 1]

공식:

  1. 각 차원별로 값의 차이를 제곱해서 더합니다.
  2. 모두 더한 값:
  3. 제곱근을 씌웁니다:
    • 결과: 두 벡터의 유클리드 거리는 2입니다.

📐 2. 코사인 유사도 (Cosine Similarity): 벡터 간의 방향 일치도

개념

코사인 유사도는 두 벡터 사이의 **'거리'가 아닌 '각도'**에 주목합니다. 두 벡터가 얼마나 같은 방향을 바라보고 있는지를 측정하여 유사성을 판단하는 방식입니다.

해석 방법 (🚨 시험 핵심!)

  • 각도 기반(Angle-based): 두 벡터가 이루는 각도의 코사인 값을 사용합니다.
  • 값이 클수록 유사: 코사인 값은 클수록(1에 가까울수록) 두 벡터의 방향이 비슷하다는 의미이며, 더 유사하다고 판단합니다.
  • 값의 범위: [-1, 1]
    • 1: 두 벡터의 방향이 완전히 일치 (사이각 0°). 완벽히 유사.
    • 0: 두 벡터가 서로 직각을 이룸 (사이각 90°). 연관성 없음.
    • -1: 두 벡터가 완전히 반대 방향 (사이각 180°). 의미가 정반대.

예시 계산

  • 문장1 벡터 A: [1, 1, 1, 0, 0]
  • 문장2 벡터 B: [0, 1, 0, 1, 1]

공식:

  1. **분자: A와 B의 내적(Dot Product)**을 구합니다.
  2. 분모: 각 벡터의 크기(Magnitude)를 곱합니다.
  3. 분자를 분모로 나눕니다.
    • 결과: 코사인 유사도는 1/3 입니다.

🥊 유클리드 거리 vs. 코사인 유사도: 무엇을 써야 할까?

구분 유클리드 거리 (Euclidean Distance) 코사인 유사도 (Cosine Similarity)
측정 대상 거리 (크기+방향 모두 고려) 방향 (크기는 무시)
"유사하다"는 의미 값이 작을수록 좋다 (가깝다) 값이 클수록 좋다 (방향이 같다)
값의 범위 0 ~ 무한대 -1 ~ 1
자연어 처리(NLP) 잘 사용하지 않음 압도적으로 많이 사용

왜 자연어 처리에서는 코사인 유사도를 선호할까?

바로 '스케일링(scaling) 문제' 때문입니다.

텍스트 분석에서 벡터의 '크기(길이)'는 종종 '문서의 길이'나 '단어의 빈도수'에 비례합니다. 하지만 문서가 길다고 해서 주제가 달라지는 것은 아닙니다.

  • 상황: '스포츠'에 대한 짧은 뉴스 기사(벡터 A)와 긴 칼럼(벡터 B)이 있습니다.
  • 유클리드 거리의 판단: 두 문서의 주제는 '스포츠'로 같지만, 칼럼의 길이가 훨씬 길기 때문에 벡터 B의 크기가 훨씬 큽니다. 따라서 두 벡터 끝 점 사이의 거리는 멀어지게 되어, 유클리드 거리는 두 문서가 유사하지 않다고 판단할 수 있습니다.
  • 코사인 유사도의 판단: 코사인 유사도는 각 벡터의 크기를 1로 정규화(Normalization)하는 효과가 있어, 벡터의 길이는 무시하고 오직 방향만 봅니다. 두 문서 모두 '스포츠'라는 같은 방향을 가리키고 있으므로, 사이각이 매우 작아 코사인 유사도는 매우 유사하다고 판단합니다.

이처럼 문서의 길이라는 크기 요소를 무시하고 순수하게 내용(방향)의 유사성만 보고 싶을 때 코사인 유사도가 훨씬 효과적이기 때문에 자연어 처리 분야에서 표준처럼 사용됩니다.

✨ 추가 유사도 메트릭 (Additional Similarity Metrics)

다양한 메트릭들은 각기 다른 목적과 장점을 가지고 있습니다. 크게 '집합', '거리', '문자열', '확률 분포' 기반으로 나누어 이해하면 쉽습니다.


1. 집합 기반 유사도 (Set-based)

자카드 계수 (Jaccard Coefficient)

  • 개념: 두 텍스트(문서)를 각각 단어의 '집합(Set)'으로 보고, 두 집합이 얼마나 겹치는지를 측정합니다. 단어의 등장 횟수나 순서는 전혀 고려하지 않고, 오직 고유한 단어의 존재 여부만 확인합니다.
  • 공식: (두 집합의 교집합 크기 / 두 집합의 합집합 크기)
  • 해석: 결과는 0과 1 사이의 값을 가지며, 1에 가까울수록 두 문서가 공유하는 고유 단어의 비율이 높다는 의미입니다.
  • 주요 용도: 중복 문서나 유사 문서 판별, 표절 탐지 등.

2. 거리 기반 유사도 (Distance-based)

맨해튼 거리 (Manhattan Distance, L1 Norm)

  • 개념: 벡터 공간에서 한 점에서 다른 점으로 이동할 때, 각 축을 따라서만 이동하는 최단 거리를 측정합니다. 마치 뉴욕 맨해튼의 격자 모양 길을 따라가는 것과 같다고 해서 붙여진 이름입니다. 각 차원의 차이값의 절대값을 모두 더한 값입니다.
  • 공식:
  • 특징: 유클리드 거리가 대각선 최단 거리를 재는 것과 달리, 맨해튼 거리는 각 요소의 차이를 제곱하지 않기 때문에 특정 값이 매우 큰 이상치(outlier)에 덜 민감한 특징이 있습니다.

해밍 거리 (Hamming Distance)

  • 개념: 길이가 같은 두 개의 문자열(또는 벡터)에서, 같은 위치에 있지만 서로 다른 문자의 개수를 세는 것입니다.
  • 예시:
    • "apple"과 "apply"의 해밍 거리는 1입니다 (마지막 'e'와 'y'가 다름).
    • 10110과 10011의 해밍 거리는 2입니다 (3번째, 5번째 비트가 다름).
  • 주요 용도: 통신에서의 에러 검출, 유전 정보 분석 등.

3. 문자열 기반 유사도 (String-based)

편집 거리 / 레벤슈타인 거리 (Edit Distance / Levenshtein Distance)

  • 개념: 하나의 문자열을 다른 문자열로 바꾸기 위해 필요한 삽입(insertion), 삭제(deletion), 치환(substitution) 연산의 최소 횟수를 측정합니다.
  • 예시: "kitten" → "sitting"
    1. kitten → sitten (치환, 1)
    2. sitten → sittin (치환, 2)
    3. sittin → sitting (삽입, 3)
    • → 편집 거리는 3입니다.
  • 주요 용도: 철자 교정("혹시 '이것'을 찾으셨나요?"), DNA 서열 분석 등 문자열 자체의 유사성을 비교할 때 매우 유용합니다.

4. 확률 분포 기반 유사도 (Probability-based)

KL 발산 (KL Divergence)

  • 개념: 두 확률 분포가 얼마나 다른지를 측정합니다. 벡터보다는 문서 내 단어들의 확률 분포, 혹은 토픽 모델링에서 각 토픽의 단어 확률 분포를 비교하는 데 사용됩니다.
  • 특징 (🚨 시험 포인트!): **비대칭적(asymmetric)**입니다. 즉, 와 $D_{KL}(Q|P)$는 다른 값을 가집니다. 따라서 엄밀한 의미의 '거리(distance)'가 아니라 '발산(divergence)'이라고 부릅니다.

상호 정보량 (Mutual Information, MI)

  • 개념: 두 텍스트(또는 단어)가 함께 등장할 때 얻어지는 정보량을 측정합니다. 한 단어가 등장했을 때, 다른 단어가 등장할 불확실성이 얼마나 줄어드는지를 나타냅니다.
  • 예시: '뉴욕'이라는 단어에서 '뉴'가 나타나면, 다음에 '욕'이 나올 확률이 매우 높아집니다. 이 경우 '뉴'와 '욕'의 상호 정보량은 높습니다.
  • 주요 용도: 연어(collocation, 자주 함께 쓰이는 단어쌍) 추출, 특징 선택(feature selection) 등.

💻 파이썬 라이브러리 요약

이러한 유사도 메트릭들은 주요 파이썬 라이브러리에서 쉽게 사용할 수 있습니다.

라이브러리 주요 함수 특징 및 용도
scikit-learn cosine_similarity, euclidean_distances 머신러닝 파이프라인에서 벡터 유사도를 계산하는 표준 라이브러리.
SciPy cosine, euclidean, jaccard 과학 계산용 라이브러리로, 다양한 거리/유사도 함수 제공. (※scipy.cosine은 1-코사인 유사도인 '코사인 거리'를 반환함에 주의!)
NLTK edit_distance 자연어 처리 특화 라이브러리로, 편집 거리와 같은 문자열 기반 유사도 측정에 강점.
PyTorch cosine_similarity, cdist 딥러닝에서 텐서(Tensor) 간의 유사도를 계산할 때 필수적으로 사용.
반응형
반응형

https://hanglestocks.tistory.com/109

 

내가 시험공부하려고 만든 자연어처리-전처리 3탄(벡터화)

1. 벡터화(Vectorization)란 무엇인가? (정의 및 핵심 아이디어)가장 먼저 '벡터화'가 무엇인지 정확히 알아야 합니다.정의벡터화란 우리가 사용하는 자연어(예: 한국어, 영어 문장)를 컴퓨터가 이해

hanglestocks.tistory.com

 

 

🧠 1. 모든 것의 시작: 분포 가설 (Distributional Hypothesis)

카운트 기반 벡터화 방법을 이해하려면, 그 바탕에 깔린 핵심 철학인 **'분포 가설'**을 먼저 알아야 합니다.

정의

분포 가설이란 **"어떤 단어의 의미는 그 단어 주변에 함께 나타나는 단어들(문맥, context)에 의해 결정된다"**는 아이디어입니다. 즉, 끼리끼리 논다는 말처럼, 비슷한 단어는 비슷한 환경에서 등장한다는 뜻이죠.

  • 핵심 아이디어: 비슷한 의미를 가진 단어는 비슷한 문맥에서 자주 등장한다.
  • 예시: "wine"과 "와인"은 다른 언어지만, 두 단어의 주변에는 보통 '마시다', '포도', '레드', '화이트', '치즈' 등 유사한 단어들이 함께 나타납니다. 이를 통해 우리는 'wine'과 '와인'의 의미가 매우 비슷할 것이라고 추정할 수 있습니다.

카운트 기반 접근법의 단순화

여기서 카운트 기반 방법은 한 가지 단순한 가정을 합니다. "여기서 말하는 **문맥(context)**을 그냥 문서(document) 전체라고 보자!"

이 가정에서부터 모든 것이 시작됩니다. 이제 단어의 의미는 '그 단어가 어떤 문서에서 얼마나 자주 등장하는가'를 통해 파악할 수 있다는 아이디어가 탄생합니다.


🔢 2. 구현 방법: 카운트 기반 벡터화 (Count-based Vectorization)

위의 '분포 가설'을 실제 벡터로 구현하는 방법이 바로 '카운트 기반 벡터화'입니다.

정의

카운트 기반 벡터화는 단어의 의미를 그 단어의 출현 빈도(count) 분포를 기반으로 표현하는 방식입니다.

  • 벡터 값의 의미: 벡터를 구성하는 각 숫자 값은 해당 단어(토큰)의 **출현 횟수(빈도)**를 직접적으로 의미합니다.
  • 대표적인 모델: Bag-of-Words (BoW) 모델이 여기에 속합니다. BoW는 문서에 포함된 단어들의 순서는 무시하고, 오직 각 단어가 몇 번 등장했는지만을 세어 벡터로 만듭니다. 이 결과를 표로 나타낸 것을 **문서-단어 행렬(DTM, Document-Term Matrix)**이라고 합니다.

의미와 유사성 추론

이 방식을 사용하면 다음과 같은 추론이 가능해집니다.

  • 텍스트의 의미: 특정 단어들의 출현 분포를 통해 텍스트 전체의 주제나 의미를 유추할 수 있습니다. (예: '농구', '선수', '득점'이라는 단어가 많이 등장하면 스포츠 관련 문서일 확률이 높다.)
  • 텍스트 간 유사성: 두 문서에 등장하는 단어들의 분포가 비슷하다면, 두 문서는 서로 유사하다고 판단할 수 있습니다.

🎯 3. 벡터화의 지향점 (그리고 카운트 기반 방식의 한계)

그렇다면 이 카운트 기반 벡터화는 우리가 궁극적으로 원하는 '이상적인 벡터'를 만들어 냈을까요? '벡터화의 지향점'과 비교하며 평가해 보겠습니다.

벡터화의 이상적인 목표

  1. 벡터 공간의 효율적 사용: 단어(토큰)를 고차원 공간의 벡터로 매핑하여, 의미 관계를 공간상에 표현한다.
  2. 의미 유사도 표현: 비슷한 의미를 가진 단어는 벡터 공간에서 서로 가까운 벡터로 표현되어야 한다. 이를 통해 의미를 수학적으로 계산할 수 있어야 한다.

카운트 기반 벡터화의 평가 (★시험 핵심★)

  • 어떤 것을 해결했나?
    • 단순히 존재 여부만 표시하던 원핫 인코딩과는 달리, '등장 빈도'라는 통계적 정보를 벡터에 담아냈습니다. 이를 통해 문서의 주제를 파악하거나 문서 간의 유사도를 계산하는 데는 어느 정도 성공했습니다.
  • 무엇이 여전히 문제인가? (한계점)
    1. 벡터 공간의 비효율성: 여전히 벡터의 차원은 전체 단어 사전의 크기와 동일하여 고차원이며, 대부분의 값이 0인 **희소 벡터(Sparse vector)**입니다. 이는 차원의 저주(Curse of Dimensionality) 문제를 그대로 가지고 있습니다.
    2. 의미 유사도 표현 실패: 가장 결정적인 한계입니다. 카운트 기반 벡터화는 '단어 자체'의 의미를 표현하지 못합니다. 예를 들어 '고양이'와 '강아지'는 의미가 매우 유사하지만, BoW 벡터 상에서는 이 두 단어 벡터 역시 서로 아무 관계가 없는(직교하는) 벡터일 뿐입니다. 단어 간의 의미적 유사성을 벡터 거리로 계산할 수 없습니다.

🎓 시험 대비 최종 요약

  • 핵심 이론: 분포 가설 ("단어의 의미는 주변 단어로부터 나온다").
  • 구현 방식: 분포 가설의 '문맥'을 '문서'로 단순화하여, 문서 내 **단어의 출현 횟수(count)**를 벡터 값으로 사용하는 것 (대표 예시: Bag-of-Words).
  • 성과와 한계:
    • 성과: 문서의 주제 파악 및 문서 간 유사도 계산이 가능해짐.
    • 한계: 여전히 고차원/희소 벡터 문제를 가지며, 결정적으로 단어 간의 의미적 유사성을 벡터에 담아내지는 못했다.

이러한 한계 때문에, 이후에는 단어의 의미 자체를 저차원의 밀집 벡터(Dense Vector)에 담아내는 **TF-IDF(카운트 기반의 발전형)**나 예측 기반의 Word2Vec 같은 방법들이 등장하게 됩니다. 이 전체적인 흐름을 이해하는 것이 중요합니다!

 

Bag-of-Words (BoW)는 카운트 기반 벡터화의 가장 대표적인 모델입니다. 


👜 1. Bag-of-Words (BoW)란 무엇인가?

개념 (The "Bag" Analogy)

**Bag-of-Words (BoW)**는 이름 그대로 텍스트를 **'단어들의 가방'**으로 취급하는 모델입니다. 문장에서 모든 단어를 꺼내 순서나 문법 구조는 다 무시하고 그냥 하나의 가방 안에 뒤죽박죽 섞어 넣는다고 상상해 보세요.

이 가방에서 우리가 관심을 갖는 것은 딱 하나, "어떤 단어가 몇 번 들어있는가?" 입니다.

  • 핵심 아이디어: 텍스트를 단어(토큰)들의 집합으로 보고, 각 단어가 등장한 빈도(frequency) 정보만을 사용하여 텍스트를 숫자 벡터로 표현합니다.
  • 무시하는 것 (🚨 시험 포인트!): 단어의 순서나 문법을 완전히 무시합니다. 예를 들어, "소년이 소녀를 사랑한다"와 "소녀가 소년을 사랑한다"는 의미가 전혀 다르지만, BoW 모델에게는 완전히 동일한 텍스트로 보입니다. (두 문장 모두 '소년', '소녀', '사랑한다'가 한 번씩 들어있기 때문이죠.)
  • 결과: 텍스트는 최종적으로 각 단어의 빈도를 나타내는 **'토큰 빈도 벡터'**로 표현됩니다.

🛠️ 2. BoW 벡터 생성 과정 (Step-by-Step)

BoW 벡터(및 행렬)는 크게 두 단계로 만들어집니다.

1단계: 어휘집(Vocabulary) 구축 과정

먼저 전체 텍스트 데이터(코퍼스)를 기반으로 기준이 될 **어휘집(단어 사전)**을 만듭니다.

  1. 토큰화(Tokenization): 가지고 있는 모든 텍스트를 문장이나 단어 같은 토큰 단위로 쪼갭니다.
  2. 정제(Cleaning): 불필요한 기호(마침표, 쉼표 등)나 큰 의미가 없는 불용어(stopwords, 예: a, the, 은, 는)를 제거합니다.
  3. 고유 토큰 추출: 모든 텍스트에서 등장한 단어들의 중복을 없애고 고유한 단어들만 남깁니다. 이것이 바로 어휘집이 됩니다.
  4. 인덱스 부여: 어휘집에 있는 각 고유 토큰에 고유한 번호(인덱스)를 부여합니다.
  • 예시:
    • 문장1: "오늘 날씨가 좋아요" → ["오늘", "날씨", "좋다"]
    • 문장2: "내일 날씨를 알려주세요" → ["내일", "날씨", "알려주다"]
    • → 최종 어휘집: {"오늘": 1, "날씨": 2, "좋다": 3, "내일": 4, "알려주다": 5}

2단계: 문서-단어 행렬 (DTM) 생성

어휘집이 완성되면, 각 문장(문서)을 이 어휘집을 기준으로 벡터화하여 **문서-단어 행렬(Document-Term Matrix, DTM)**을 만듭니다.

  • 행(row): 각 문장 또는 문서
  • 열(column): 어휘집에 있는 모든 고유 토큰
  • 값(value): 해당 문서(행)에 해당 토큰(열)이 등장한 횟수 (Term Frequency, TF)
  • 예시 DTM:
토큰 오늘 날씨 좋다 내일 알려주다
문장1 1 1 1 0 0
문장2 0 1 0 1 1
  • 결과 해석:
    • 문장1의 BoW 벡터: [1, 1, 1, 0, 0]
    • 문장2의 BoW 벡터: [0, 1, 0, 1, 1]
    • 이제 각 문장은 어휘집의 차원(이 경우 5차원)을 갖는 숫자 벡터로 성공적으로 변환되었습니다!

👍👎 3. BoW의 장점과 한계 (★시험 핵심★)

장점 (Pros) 단점 (Cons)
✔️ 구현이 간단하고 이해하기 쉽다. ❌ 단어 순서를 무시하여 문맥 정보를 잃는다. (가장 치명적!)
✔️ 문서의 주제 분류에 효과적이다. (예: "선수, 경기, 골"이 많이 나오면 스포츠 문서) ❌ 희소성(Sparsity) 및 고차원 문제: 단어 수가 많아지면 벡터가 매우 길어지고 대부분이 0으로 채워져 비효율적이다. (차원의 저주)
  ❌ 단어의 의미적 유사성을 반영하지 못한다. ('강아지'와 '개'를 완전히 다른 단어로 취급)

🎓 시험 대비 최종 요약

  • BoW란? 단어의 순서는 무시하고, 오직 출현 빈도만으로 텍스트를 숫자 벡터로 만드는 방법.
  • 핵심 결과물: 문서-단어 행렬 (DTM). 여기서 각 행은 하나의 문서를 나타내는 BoW 벡터.
  • 가장 큰 특징이자 한계: 문맥 정보의 손실. "아버지가 방에 들어가신다"와 "아버지 가방에 들어가신다"를 구분하지 못함.
  • 주요 용도: 간단하면서도 효과가 좋아 텍스트 분류, 스팸 메일 필터링, 토픽 모델링 등 넓은 범위의 기초 모델로 사용됨

🔄 1. BoW 모델 프로세스 요약 (Summary Flowchart)

먼저 BoW 모델이 어떻게 동작하는지 전체적인 흐름을 다시 한번 복습해 보겠습니다. 슬라이드의 요약 이미지는 이 과정을 매우 명확하게 보여줍니다.

  1. Corpus (코퍼스): 분석하고자 하는 텍스트 문서들의 모음에서 시작합니다.
  2. Tokenize (토큰화): 각 문서를 단어(토큰)와 같은 작은 단위로 나눕니다.
  3. Count Word Frequencies (단어 빈도수 계산): 전체 문서에서 고유한 단어들로 어휘집(Vocabulary)을 만들고, 각 문서에서 어휘집의 단어들이 몇 번씩 등장하는지 횟수를 셉니다.
  4. Encode the Data (데이터 인코딩): 위에서 계산한 빈도수 정보를 바탕으로, 각 문서를 숫자 벡터로 변환합니다. 이 결과물을 모아놓은 것이 바로 **문서-단어 행렬(DTM)**입니다.

이 네 단계를 거치면, 텍스트는 컴퓨터가 이해하고 계산할 수 있는 숫자 데이터(벡터)로 변환됩니다.


📏 2. BoW의 문제점과 해결책: 텍스트 길이 문제

기본적인 BoW 모델은 한 가지 중요한 약점을 가지고 있습니다. 바로 문서의 길이에 따라 단어의 중요도가 왜곡될 수 있다는 점입니다.

문제점: 긴 문서의 함정

  • 문장1: 전체 단어 10개, "오늘" 1번 등장 → 출현 횟수: 1
  • 문장2: 전체 단어 100개, "오늘" 10번 등장 → 출현 횟수: 10

단순히 등장 횟수(count)만 보면, 문장2에서 "오늘"이라는 단어가 문장1에서보다 10배나 더 중요해 보입니다. 하지만 문장2는 길이가 10배나 길기 때문에 "오늘"이 10번 등장한 것일 수 있습니다. 오히려 전체 문서에서 "오늘"이 차지하는 비중은 문장1(1/10)과 문장2(10/100)가 동일합니다.

이처럼 단순 카운트는 긴 문서에 있는 단어에 더 높은 가중치를 부여하여, 문서 간의 공정한 비교를 어렵게 만듭니다.

해결책: 정규화된 출현 횟수, TF (Term Frequency) 사용

이 문제를 해결하기 위해 **정규화(Normalization)**를 사용합니다. 즉, 각 단어의 등장 횟수를 그 단어가 포함된 문서의 전체 단어 수로 나누어 주는 것입니다. 이것을 바로 **단어 빈도(TF, Term Frequency)**라고 합니다.

TF(단어 t, 문서 d) = (문서 d에서 단어 t의 등장 횟수) / (문서 d의 전체 단어 수)

  • 예시 DTM (TF 적용 후):
토큰 오늘 날씨 좋다 내일 알려주다
문장1 1/3 1/3 1/3 0 0
문장2 0 1/3 0 1/3 1/3

이렇게 TF를 사용하면, 각 벡터의 값은 문서 길이에 상관없이 해당 단어가 문서 내에서 차지하는 **상대적인 중요도(비중)**를 나타내게 됩니다. 따라서 문서 길이가 달라도 훨씬 공정하게 비교할 수 있습니다.


🛠️ 3. BoW의 활용 사례 (Use Cases)

BoW는 단순한 모델이지만, 그 효과가 뛰어나 여러 분야에서 널리 활용됩니다.

  • 메일 필터링 (Spam Mail Filtering)
    • 스팸 메일에는 'free', 'offer', 'money', 'win' 등의 단어가 일반 메일보다 훨씬 자주 등장합니다. BoW로 메일의 단어 빈도 벡터를 만든 후, 기계학습 모델을 통해 스팸 메일의 단어 분포 패턴을 학습시켜 스팸을 걸러낼 수 있습니다.
  • 뉴스 카테고리 분류 (News Categorization)
    • '선수', '경기', '골' 등의 단어가 많이 나오면 스포츠 기사로, '대통령', '국회', '선거' 등의 단어가 많으면 정치 기사로 분류할 수 있습니다. BoW는 문서의 주제를 파악하는 데 매우 효과적입니다.
  • 감정 분석 (Sentiment Analysis)
    • 영화 리뷰나 상품 후기에서 '최고', '추천', '만족' 같은 긍정적인 단어와 '최악', '실망', '별로' 같은 부정적인 단어의 빈도를 세어 해당 텍스트의 감정이 긍정적인지 부정적인지 판단할 수 있습니다.
  • 정보 검색 (Information Retrieval) 및 주제 추론 (Topic Modeling)
    • 사용자가 검색어를 입력했을 때, 검색어에 포함된 단어들의 빈도가 높은 문서를 찾아 보여주는 원리도 BoW에 기반합니다. 또한, 문서들의 단어 빈도 분포를 분석하여 해당 문서 집단의 주요 주제가 무엇인지 추론할 수도 있습니다.

🎓 시험 대비 최종 요약

  • BoW 프로세스: **코퍼스 → 토큰화 → 빈도수 계산 → 인코딩(DTM 생성)**의 4단계를 거친다.
  • 핵심 문제점: 문서 길이가 길수록 단어 빈도수가 부풀려져 중요도가 왜곡될 수 있다.
  • 핵심 해결책: 단순 카운트 대신, 단어 횟수를 전체 단어 수로 나눈 **정규화된 값, 즉 단어 빈도(TF)**를 사용한다.
  • 주요 활용 분야: 스팸 필터링, 뉴스 분류, 감정 분석 등 텍스트의 주제나 성격을 파악하는 대부분의 기초적인 자연어 처리 문제에 효과적으로 사용된다.

🛠️ 1. 직접 구현으로 원리 파악하기 (Manual Implementation)

이 코드는 외부 라이브러리 없이 기본적인 파이썬 문법만으로 BoW 모델의 핵심 로직을 처음부터 끝까지 만드는 과정을 보여줍니다. BoW의 동작 원리를 이해하는 데 가장 좋은 예제입니다.

코드 분석 (단계별)

  1. 데이터 준비 및 토큰화
    • 분석할 4개의 문서를 리스트로 준비합니다.
  2. Python
     
    docs = ['먹고 싶은 사과', '먹고 싶은 바나나', '길고 노란 바나나 바나나', '저는 바나나가 좋아요']
    # 실제 코드에서는 각 문서를 .split()으로 단어 리스트로 만듭니다.
    
  3. 어휘집(Vocabulary) 생성
    • 모든 문서에 있는 단어들을 모아 중복을 제거하여 고유한 단어의 집합, 즉 어휘집을 만듭니다.
    • enumerate를 사용해 어휘집의 각 단어에 0부터 시작하는 고유한 정수 인덱스를 부여한 word_to_index 딕셔너리를 생성합니다. 이것이 단어와 숫자(벡터의 위치)를 연결하는 다리 역할을 합니다.
  4. Python
     
    # 모든 문서의 단어들을 하나로 합친 후, set으로 중복을 제거해 고유 단어만 추출
    vocab = list(set(word for doc in docs for word in doc.split()))
    vocab.sort() # 순서를 보장하기 위해 정렬
    
    # 단어에 고유 인덱스 부여
    word_to_index = {word: i for i, word in enumerate(vocab)}
    
  5. BoW 벡터 생성 함수 (make_bow)
    • 이 함수가 BoW의 핵심 로직입니다.
      1. 먼저 어휘집의 크기와 동일한 길이의 0으로 채워진 리스트(bow)를 만듭니다.
      2. 입력된 문서(doc)의 단어들을 하나씩 확인합니다.
      3. word_to_index에서 해당 단어의 인덱스를 찾아, bow 리스트의 그 인덱스 위치에 있는 값을 1씩 더해줍니다.
      4. 모든 단어를 다 세면, 최종적으로 완성된 빈도수 벡터(bow)를 반환합니다.
  6. Python
     
    def make_bow(doc):
        # 1. 어휘집 크기의 0으로 채워진 벡터 생성
        bow = [0] * len(vocab)
        # 2. 입력된 문서의 단어들을 하나씩 순회
        for word in doc.split():
            # 3. 단어의 인덱스를 찾아서
            idx = word_to_index[word]
            # 4. 해당 인덱스의 값을 1 증가 (카운트)
            bow[idx] += 1
        return bow
    
  7. 실행 및 결과 확인
    • 이 make_bow 함수를 모든 문서에 대해 실행하면 슬라이드 우측에 보이는 **BoW 행렬(DTM)**이 만들어집니다.
    • 예를 들어, 3번째 문서 "길고 노란 바나나 바나나"의 벡터가 [1, 0, 2, 0, 0, 0, 0, 1]인 이유를 어휘집 ['길고', '먹고', '바나나', '사과', '싶은', '저는', '좋아요', '노란']과 비교해 보면,
      • '길고'(0번 인덱스) 1번, '바나나'(2번 인덱스) 2번, '노란'(7번 인덱스) 1번 등장했음을 정확히 알 수 있습니다.

🚀 2. Scikit-learn으로 간단하게 구현하기

이번 코드는 머신러닝 라이브러리인 Scikit-learn의 CountVectorizer를 사용합니다. 실무에서 BoW 모델을 만들 때 사용하는 표준적인 방법입니다.

코드 분석 (단계별)

  1. CountVectorizer 불러오기 및 생성
    • BoW 모델을 만들어주는 도구인 CountVectorizer를 불러와 객체를 생성합니다.
  2. Python
     
    from sklearn.feature_extraction.text import CountVectorizer
    vectorizer = CountVectorizer()
    
  3. 학습 및 변환 (fit_transform)
    • 이 한 줄이 위에서 직접 구현한 모든 과정을 알아서 처리해 줍니다.
      • fit(): 입력된 docs 데이터를 분석해서 스스로 어휘집을 만들고 단어-인덱스 매핑을 학습합니다.
      • transform(): 학습된 어휘집을 기준으로 docs 데이터를 BoW 행렬(DTM)로 변환합니다.
      • **fit_transform()**은 이 두 과정을 한 번에 수행하는 편리한 메서드입니다.
  4. Python
     
    X = vectorizer.fit_transform(docs)
    
  5. 결과 확인
    • vectorizer.get_feature_names_out(): CountVectorizer가 학습한 어휘집(DTM의 열 순서)을 보여줍니다.
    • X.toarray(): 생성된 X는 원래 메모리 효율을 위해 희소 행렬(sparse matrix) 형태로 저장되어 있습니다. .toarray()를 통해 우리가 눈으로 보기 쉬운 일반적인 행렬(Numpy 배열)로 변환하여 출력합니다.
    • 출력된 어휘집과 BoW 행렬은 위에서 직접 구현한 결과와 완전히 동일한 것을 확인할 수 있습니다.
  6. Python
     
    print("어휘집:", vectorizer.get_feature_names_out())
    print("BoW 행렬:\n", X.toarray())
    

🎓 시험 대비 최종 요약

구분 직접 구현 (Manual) Scikit-learn (CountVectorizer)
목적 BoW의 동작 원리를 단계별로 이해하기 위함 실용적이고 효율적인 BoW 모델 구현을 위함
핵심 word_to_index 딕셔너리 생성, for 루프를 통한 직접 카운팅 fit_transform() 메서드 하나로 모든 과정 자동 처리
시험 Tip 코드의 각 부분이 어휘집 생성, 인덱싱, 카운팅 중 어느 역할을 하는지 파악하는 것이 중요. CountVectorizer, fit_transform, get_feature_names_out의 기능과 역할을 암기하는 것이 중요.

결론적으로, 두 방법은 동일한 결과를 내지만, 원리 이해를 위해서는 직접 구현 코드를, 실제 문제 해결을 위해서는 Scikit-learn 코드를 알아두는 것이 좋습니다. 두 가지를 모두 이해하고 있으면 어떤 형태의 문제가 나와도 완벽하게 대비할 수 있습니다!

 

📉 Bag-of-Words (BoW)의 명확한 한계점

BoW는 구현이 간단하고 직관적이라는 장점이 있지만, 다음과 같은 4가지의 치명적인 약점을 가지고 있습니다.

1. 불용어 문제 (Stopword Problem) 🚮

  • 설명: 텍스트에는 'the', 'a', 'is'나 한국어의 '은', '는', '이', '가'와 같이 자주 등장하지만 실제 의미에는 거의 기여하지 않는 단어들이 있습니다. 이를 **불용어(Stopword)**라고 합니다.
  • BoW의 문제점: BoW는 오직 단어의 '빈도'만을 보기 때문에, 이 의미 없는 불용어들이 가장 중요한 단어인 것처럼 취급될 수 있습니다. 예를 들어, 어떤 문서의 BoW 벡터에서 '는'이라는 단어가 가장 높은 빈도수를 차지한다면, 이 벡터는 문서의 핵심 의미를 제대로 담고 있다고 보기 어렵습니다.
  • 해결책: 일반적으로 BoW 모델을 만들기 전, 텍스트 전처리 단계에서 불용어를 미리 제거하는 작업을 거칩니다.

2. 순서 정보 손실 (Loss of Word Order) 🤷

  • 설명: BoW의 가장 근본적이고 심각한 한계입니다. BoW는 단어를 '가방'에 담는다는 개념 때문에, 원래 문장에 있던 단어의 순서 정보를 완전히 무시합니다.
  • 왜 문제인가?: 단어의 순서는 문장의 의미를 결정하는 핵심적인 요소입니다.
  • (🚨 시험 단골 예시!)
    • 문장 A: "개가 사람을 물었다"
    • 문장 B: "사람이 개를 물었다"
    • 이 두 문장은 의미가 완전히 반대입니다. 하지만 BoW의 관점에서는 두 문장 모두 {'개': 1, '사람': 1, '물었다': 1} 이라는 동일한 단어 구성을 가지므로, 완전히 똑같은 벡터로 변환됩니다. 즉, BoW는 이 두 문장을 전혀 구별하지 못합니다.
  • 추가 문제: 부정(negation) 표현처럼 순서가 중요한 문맥을 파악하기 어렵습니다. 예를 들어, "이 영화는 전혀 재미있지 않다"에서 '전혀'와 '않다'의 관계를 이해하지 못하고 '재미있다'라는 단어의 존재만으로 긍정으로 잘못 판단할 수 있습니다.

3. 차원의 저주 (Curse of Dimensionality) 🌌

  • 설명: BoW 벡터의 차원(길이)은 어휘집(Vocabulary)에 있는 고유 단어의 총개수와 동일합니다.
  • 왜 문제인가?: 일반적인 텍스트 데이터(예: 뉴스 기사 모음)의 어휘집 크기는 수만 개에서 수십만 개에 달하는 경우가 흔합니다. 이는 단어 하나하나가 수만 차원의 벡터가 된다는 의미입니다.
  • 결과:
    • 계산 비용 증가: 차원이 클수록 모델이 계산해야 할 양이 기하급수적으로 늘어나 학습이 매우 느려집니다.
    • 과적합(Overfitting) 위험: 데이터의 양에 비해 차원이 너무 크면, 모델이 훈련 데이터에만 과도하게 최적화되어 새로운 데이터에 대한 예측 성능이 떨어질 수 있습니다.

4. 희소 벡터 (Sparse Representation) 문제 💨

  • 설명: 이 문제는 '차원의 저주'와 직접적으로 연결됩니다. 벡터의 차원은 수만 개로 매우 크지만, 하나의 문서에는 그중 극히 일부의 단어만 포함됩니다.
  • 왜 문제인가?: 결과적으로 BoW 벡터는 대부분의 값이 0이고 몇 개의 값만 1 이상의 수를 갖는 **희소 벡터(Sparse Vector)**가 됩니다.
  • 결과:
    • 메모리 비효율성: 수많은 0을 저장하기 위해 엄청난 메모리 공간이 낭비됩니다. (물론 실제 구현에서는 이를 해결하기 위해 희소 행렬 자료구조를 사용하지만, 근본적인 비효율성은 존재합니다.)
    • 정보 밀도 저하: 벡터 하나에 담긴 정보의 밀도가 매우 낮아, 모델이 의미 있는 패턴을 학습하기 어려워집니다.

🎓 시험 대비 최종 요약

한계점 핵심 내용 결과 및 영향
불용어 문제 의미 없는 단어(a, the, 은, 는)가 높은 빈도수를 차지함 벡터가 핵심 의미를 제대로 표현하지 못할 수 있음
순서 정보 손실 단어의 순서를 완전히 무시함 ("개가 사람을..." vs "사람이 개를...") 문맥과 뉘앙스를 파악하지 못하고, 의미가 다른 문장을 동일하게 취급함
차원의 저주 어휘집 크기만큼 벡터 차원이 커짐 계산 비용이 급증하고 모델 성능이 저하될 수 있음
희소 벡터 벡터의 대부분이 0으로 채워짐 메모리 낭비가 심하고 정보 밀도가 낮음

이러한 명확한 한계점들 때문에, 자연어 처리 기술은 BoW를 개선한 TF-IDF, 그리고 더 나아가 단어의 순서와 의미를 모두 고려하는 Word2Vec, BERT와 같은 모델로 발전하게 되었습니다. 이 전체적인 흐름을 기억해두시면 시험에 큰 도움이 될 겁니다!

반응형
반응형

https://hanglestocks.tistory.com/109

 

내가 시험공부하려고 만든 자연어처리-전처리 3탄(벡터화)

1. 벡터화(Vectorization)란 무엇인가? (정의 및 핵심 아이디어)가장 먼저 '벡터화'가 무엇인지 정확히 알아야 합니다.정의벡터화란 우리가 사용하는 자연어(예: 한국어, 영어 문장)를 컴퓨터가 이해

hanglestocks.tistory.com

🤔 1. 패딩(Padding)이란 무엇이고, 왜 필요한가?

정의

**패딩(Padding)**이란 여러 문장(데이터)의 길이를 똑같이 맞춰주기 위해, 상대적으로 짧은 문장의 남는 공간에 특정 값(보통 0)을 채워 넣는 작업을 말합니다.

"나는 밥을 먹는다" (3개 토큰) 와 "나는 오늘 학교에 간다" (4개 토큰) 처럼, 문장마다 포함된 단어(토큰)의 개수는 제각각입니다. 이 문장들을 정수 인코딩하면 [1, 2, 3] 과 [1, 4, 5, 6] 처럼 길이가 다른 숫자 배열(벡터)이 됩니다. 패딩은 이 길이를 가장 긴 문장이나 우리가 정한 특정 길이에 맞춰 통일시키는 과정입니다.

필요성 (★매우 중요★)

**결론부터 말하면, 컴퓨터의 효율적인 **행렬 연산(Matrix Operation)을 위해서입니다.

딥러닝 모델은 수많은 데이터를 한 번에 묶어서(배치, Batch) 처리합니다. 이 과정에서 컴퓨터(특히 GPU)는 데이터를 하나의 큰 행렬로 보고 병렬 계산을 수행해야 가장 빠르고 효율적입니다. 하지만 각 문장의 길이가 다르면, 이들을 가지런한 사각형 모양의 행렬로 만들 수가 없습니다.

[[1, 2, 3], [1, 4, 5, 6]] 처럼 들쭉날쭉한 데이터는 행렬 연산이 불가능합니다. 패딩을 통해 [[1, 2, 3, 0], [1, 4, 5, 6]] 처럼 모든 행의 길이를 동일하게 맞춰주어야 비로소 컴퓨터가 효율적으로 계산할 수 있는 행렬이 완성되는 것입니다.


📋 2. 패딩의 종류

패딩을 할 때 어떤 값으로 채우는지에 따라 종류가 나뉩니다.

  1. 제로 패딩 (Zero Padding)
    • 가장 흔하고 일반적인 방식으로, 부족한 공간을 숫자 0으로 채웁니다.
    • 0은 보통 단어 사전에 없는 특별 토큰으로 예약되어 있어, 실제 단어와 혼동될 여지가 적기 때문에 널리 사용됩니다.
    • 마스킹(Masking): (★시험 핵심★) 단순히 0을 채우는 것에서 그치지 않고, 모델에게 "이 0은 의미 없는 값이니 계산에서 무시해!"라고 알려주는 장치가 필요합니다. 이것을 마스킹이라고 합니다. 모델은 이 마스크 정보를 보고 실제 단어에만 집중하고 패딩된 0은 없는 셈 칩니다. (이 개념은 아래 코드 예시의 '어텐션 마스크'에서 다시 등장합니다.)
  2. 다른 상수로 패딩
    • 0이 아닌 다른 값(예: 전체 단어 벡터의 평균값, 특정 상수 등)으로 채울 수도 있습니다.
    • 하지만 자연어 처리에서는 잘 사용되지 않습니다. 왜냐하면 의미 없는 패딩 값이라도 특정 숫자 값은 문장 전체의 의미를 왜곡시킬 가능성이 있기 때문입니다. 0을 쓰고 마스킹하는 것이 가장 안전하고 표준적인 방법입니다.

💻 3. 코드 예시로 완벽 이해하기

실제 코드에서 패딩이 어떻게 사용되는지 두 가지 예시를 통해 알아보겠습니다.

예시 1: TensorFlow/Keras의 pad_sequences

이 함수는 정수 인코딩된 결과물의 길이를 맞춰주는 가장 직관적인 도구입니다.

Python
 
from tensorflow.keras.preprocessing.sequence import pad_sequences

sequences = [[1,2,3], [4,5], [6]] # 길이가 다른 3개의 문장

padded = pad_sequences(sequences, padding='post', maxlen=5)
# 출력:
# [[1 2 3 0 0]
#  [4 5 0 0 0]
#  [6 0 0 0 0]]
  • sequences: [1,2,3], [4,5], [6] 처럼 길이가 모두 다른 입력 데이터입니다.
  • pad_sequences(...): 이 함수가 패딩을 수행합니다.
    • padding='post': 'post'는 단어 뒤(post)에 0을 채우라는 의미입니다. 반대로 'pre'는 단어 앞(pre)에 채웁니다. 보통 'post'가 더 흔하게 쓰입니다.
    • maxlen=5: 모든 문장의 최대 길이(max length)를 5로 고정하라는 의미입니다. 이 길이보다 짧으면 0이 채워지고, 만약 이 길이보다 길면 잘립니다(truncation).
  • 결과: 모든 문장(행)이 길이가 5인 벡터로 변환되었고, 빈 공간은 뒤쪽에 0으로 채워진 것을 볼 수 있습니다.

예시 2: Hugging Face Transformers의 Tokenizer

BERT와 같은 최신 모델들은 토큰화 과정에서 패딩까지 한번에 자동으로 처리해줍니다.

Python
 
from transformers import BertTokenizer

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
encodings = tokenizer(["I love NLP", "I love"],
                      padding=True,
                      truncation=True,
                      return_tensors="pt")
  • tokenizer(...): 문장들을 토큰화하고 정수 인코딩하며, 동시에 패딩까지 수행합니다.
    • padding=True: 이 옵션 하나만으로 패딩을 하라고 지시할 수 있습니다. 토크나이저는 입력된 문장들 중 가장 긴 문장을 기준으로 나머지 문장들을 자동으로 패딩합니다.
  • 결과 (encodings):
    • input_ids (입력 ID): 패딩이 적용된 정수 인코딩 결과입니다. [I, love, NLP] 와 [I, love]는 각각 [101, 1045, 2293, 17953, 102] 와 [101, 1045, 2293, 102, **0**] 와 같이 변환됩니다. (숫자는 예시이며, 101=[CLS], 102=[SEP], 0=[PAD] 토큰을 의미)
    • attention_mask (어텐션 마스크): 이것이 바로 위에서 설명한 '마스킹'의 실체입니다. input_ids와 똑같은 모양을 가지며, 실제 단어 위치는 1로, 패딩된 0의 위치는 0으로 표시합니다.
      • [[1, 1, 1, 1, 1], [1, 1, 1, 1, **0**]]
      • 모델은 이 어텐션 마스크를 보고 두 번째 문장의 마지막 토큰은 가짜(패딩)라는 것을 인지하고, 해당 부분의 연산을 무시하게 됩니다.

🎓 시험 대비 최종 요약

  • 패딩이란? 문장들의 길이를 통일하기 위해 짧은 문장에 0을 채우는 것.
  • 왜 필요한가? 컴퓨터의 효율적인 일괄(배치) 행렬 연산을 위해.
  • 핵심 개념은? 제로 패딩이 표준이며, 모델이 패딩 값을 무시하도록 알려주는 마스킹(특히 어텐션 마스크)이 반드시 함께 사용된다.
  • 구현 방법은? Keras의 pad_sequences나 Hugging Face 토크나이저의 padding=True 옵션을 통해 쉽게 구현할 수 있다.
반응형
반응형

https://hanglestocks.tistory.com/109#google_vignette

 

내가 시험공부하려고 만든 자연어처리-전처리 3탄(벡터화)

1. 벡터화(Vectorization)란 무엇인가? (정의 및 핵심 아이디어)가장 먼저 '벡터화'가 무엇인지 정확히 알아야 합니다.정의벡터화란 우리가 사용하는 자연어(예: 한국어, 영어 문장)를 컴퓨터가 이해

hanglestocks.tistory.com

 

🔥 1. 원핫 인코딩(One-hot Encoding)이란?

정의

원핫 인코딩은 단어를 표현하는 방법 중 하나로, 전체 단어 사전(Vocabulary)의 크기와 동일한 길이의 벡터를 만든 뒤, 표현하고 싶은 단어의 인덱스 위치에만 1을 두고 나머지는 모두 0으로 채우는 방식입니다.

이름 그대로 '하나(One)만 뜨겁다(Hot)'는 뜻이에요. 벡터의 수많은 자리 중에 딱 하나만 1이고 나머지는 전부 0인 거죠.

  • 핵심 원리:
    1. 먼저 전체 텍스트를 기준으로 단어 사전을 만들고 각 단어에 정수 인코딩을 수행합니다. (예: '오늘': 1, '날씨': 2)
    2. 사전의 총 단어 개수만큼의 길이를 가진 0으로 채워진 벡터를 만듭니다. (사전에 단어가 5개라면 [0, 0, 0, 0, 0])
    3. 표현하려는 단어의 정수 인덱스 자리를 찾아 그 위치만 1로 바꿔줍니다.

결과적으로 각 단어는 자신만의 고유한 벡터 표현을 갖게 됩니다.


🔢 2. 원핫 인코딩 예시로 과정 이해하기

슬라이드의 예시를 통해 원핫 인코딩 과정을 단계별로 살펴보겠습니다.

  • 문장 1: "오늘 날씨가 좋아요" → ["오늘", "날씨", "좋다"]
  • 문장 2: "내일 날씨를 알려주세요" → ["내일", "날씨", "알려주다"]

1단계: 단어 사전 및 정수 인코딩 (선행 작업)

원핫 인코딩을 하려면, 먼저 정수 인코딩이 되어 있어야 합니다.

  • 단어 사전: {"오늘": 1, "날씨": 2, "좋다": 3, "내일": 4, "알려주다": 5}
  • 사전의 전체 크기(단어의 총개수)는 5개입니다. 따라서 모든 단어는 길이가 5인 벡터로 표현됩니다.

2단계: 단어를 원핫 벡터로 변환

이제 각 단어를 해당하는 원핫 벡터로 바꿔봅시다.

  • "오늘" (인덱스 1): 5개의 자리 중 첫 번째 자리에 1을 넣습니다. → [1, 0, 0, 0, 0]
  • "날씨" (인덱스 2): 5개의 자리 중 두 번째 자리에 1을 넣습니다. → [0, 1, 0, 0, 0]
  • "좋다" (인덱스 3): 5개의 자리 중 세 번째 자리에 1을 넣습니다. → [0, 0, 1, 0, 0]
  • "내일" (인덱스 4): 5개의 자리 중 네 번째 자리에 1을 넣습니다. → [0, 0, 0, 1, 0]
  • "알려주다" (인덱스 5): 5개의 자리 중 다섯 번째 자리에 1을 넣습니다. → [0, 0, 0, 0, 1]

3단계: 문장을 원핫 벡터의 시퀀스로 변환

이제 문장 전체를 변환할 수 있습니다.

  • 인코딩 1 (문장 1): "오늘 날씨 좋다" → [ [1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 1, 0, 0] ]
  • 인코딩 2 (문장 2): "내일 날씨 알려주다" → [ [0, 0, 0, 1, 0], [0, 1, 0, 0, 0], [0, 0, 0, 0, 1] ]

이렇게 문장은 원핫 벡터들의 리스트(또는 행렬) 형태로 최종 변환됩니다.


👍👎 3. 원핫 인코딩의 장점과 단점 (★시험 핵심★)

원핫 인코딩은 매우 직관적이지만, 심각한 단점 때문에 최근에는 잘 사용되지 않습니다. 장단점을 명확히 아는 것이 시험의 핵심입니다.

장점 (Advantages)

  1. 직관적이고 단순한 표현
    • 어떤 단어를 표현하는지 명확하게 알 수 있어 이해하기 쉽고 구현이 간단합니다.

단점 (Disadvantages)

  1. 단어 간 유사성 표현 불가 (치명적인 단점!)
    • 슬라이드의 "정수 인코딩보다 의미 있는 공간 표현 가능하나 유사성 없음"이라는 말은 중요합니다. 정수 인코딩은 숫자 크기(1<2<3)라는 잘못된 관계를 모델에게 줄 수 있지만, 원핫 인코딩은 모든 단어 벡터가 서로 직교(orthogonal) 관계에 있어 이런 오해는 없습니다.
    • 하지만 모든 단어 벡터 간의 거리가 동일하기 때문에, '오늘'과 '내일'이 '알려주다'보다 의미적으로 가깝다는 사실을 전혀 표현하지 못합니다. 단어의 의미를 벡터에 담지 못하는 것이죠.
  2. 차원의 저주 (Curse of Dimensionality)
    • 단어 사전의 크기가 곧 벡터의 차원(길이)이 됩니다. 사전에 단어가 10,000개 있다면, 단어 하나를 표현하기 위해 10,000차원의 벡터가 필요합니다. 이렇게 차원이 불필요하게 커지면 계산량이 폭발적으로 증가하고 모델의 성능이 저하됩니다.
  3. 희소 표현 (Sparse representation) 및 메모리 비효율
    • 벡터의 요소 대부분이 0으로 채워져 있는 것을 **희소 벡터(Sparse vector)**라고 합니다. 이는 정보를 표현하는 데 있어 매우 비효율적입니다. 10,000차원 벡터에서 단 하나의 1을 제외한 9,999개의 0은 아무런 정보를 담고 있지 않지만, 메모리 공간은 그대로 차지합니다.

🎓 시험 대비 최종 요약

  • 원핫 인코딩이란? 단어 사전 크기의 벡터에서, 해당 단어의 인덱스만 1로 표시하는 방법.
  • 장점: 직관적이고 간단하다.
  • 단점 (★매우 중요★):
    • 유사성 표현 불가: 모든 단어 사이의 관계가 없다.
    • 차원의 저주: 단어 수가 늘면 벡터 차원이 한없이 커진다.
    • 메모리 비효율: 대부분이 0인 희소 벡터라 공간 낭비가 심하다.

원핫 인코딩은 정수 인코딩의 '숫자 크기 오해' 문제를 해결했지만, '단어 의미 표현 불가'와 '차원 문제'라는 더 큰 한계를 가지고 있습니다. 이러한 문제들을 해결하기 위해 등장한 것이 바로 **워드 임베딩(Word Embedding)**입니다. 

물론입니다! 이 코드 예제들은 원핫 인코딩(One-hot Encoding)을 파이썬의 여러 라이브러리를 사용해 구현하는 방법을 보여줍니다. 시험에 어떤 코드가 나오더라도 완벽하게 해석할 수 있도록, 각 코드의 원리와 라이브러리별 차이점을 상세히 비교하며 설명해 드릴게요.


🐍 파이썬으로 구현하는 원핫 인코딩 코드 완전 정복

슬라이드에는 총 3가지 방법이 나옵니다.

  1. KoNLPy를 이용한 직접 구현 (한국어)
  2. Scikit-learn 라이브러리 활용 (영어)
  3. PyTorch 라이브러리 활용 (영어)

각각의 방법을 순서대로 마스터해 봅시다.


1. KoNLPy를 이용한 직접 구현 (한국어)

이 코드는 라이브러리에 의존하지 않고, 원핫 인코딩의 원리를 직접 파이썬 함수로 만드는 과정을 보여줍니다. 한국어 자연어 처리에 사용되는 KoNLPy가 토큰화에 사용되었습니다.

코드 분석 (단계별)

  1. 형태소 분석 (토큰화)
    • Okt는 한국어 형태소 분석기입니다. 문장을 의미를 가진 가장 작은 단위인 형태소로 쪼개줍니다. 이렇게 얻은 tokens 리스트가 우리의 단어 사전(Vocabulary)의 재료가 됩니다.
  2. Python
     
    from konlpy.tag import Okt
    okt = Okt()
    tokens = okt.morphs("나는 자연어 처리를 배운다")
    # 출력: ['나', '는', '자연어', '처리', '를', '배운다']
    
  3. 단어 사전(Vocabulary) 생성
    • enumerate를 사용하여 tokens 리스트의 각 단어에 0부터 시작하는 고유한 정수 인덱스를 부여하고, 이를 word_to_index 딕셔너리에 저장합니다.
  4. Python
     
    word_to_index = {word : index for index, word in enumerate(tokens)}
    # 출력: {'나': 0, '는': 1, '자연어': 2, '처리': 3, '를': 4, '배운다': 5}
    
  5. 원핫 인코딩 함수 정의
    • 이 함수가 바로 원핫 인코딩의 핵심 로직입니다. 입력으로 단어(word)와 단어 사전(word_to_index)을 받아서, 해당 단어의 원핫 벡터를 반환합니다.
  6. Python
     
    def one_hot_encoding(word, word_to_index):
        # 1. 단어 사전 크기만큼 0으로 채워진 벡터 생성
        one_hot_vector = [0]*(len(word_to_index))
        # 2. 원하는 단어의 인덱스 찾기
        index = word_to_index[word]
        # 3. 해당 인덱스 위치의 값을 1로 변경
        one_hot_vector[index] = 1
        return one_hot_vector
    
  7. 함수 실행 및 결과 확인
    • "자연어"는 사전에서 인덱스 2번에 해당합니다. 따라서 전체 6개의 자리 중 2번 인덱스(세 번째 자리)만 1이 되고 나머지는 0인 벡터가 정확히 반환되었습니다.
  8. Python
     
    one_hot_encoding("자연어", word_to_index)
    # 결과: [0, 0, 1, 0, 0, 0]
    

2. Scikit-learn 라이브러리 활용 (영어)

Scikit-learn은 머신러닝에서 가장 널리 쓰이는 라이브러리 중 하나이며, 데이터 전처리를 위한 강력한 도구인 OneHotEncoder를 제공합니다.

코드 분석 (단계별)

  1. 토큰화 및 정수 인코딩
    • 먼저 nltk로 토큰화를 하고, 각 단어에 정수 인덱스를 부여합니다. (코드가 일부 생략되었지만 결과적으로 indices 리스트가 생성됩니다.)
  2. Python
     
    from nltk.tokenize import word_tokenize
    tokens = word_tokenize("I love natural language processing")
    # ... 정수 인코딩 ...
    indices = [0, 1, 2, 3, 4] # 각 단어에 부여된 인덱스 리스트
    
  3. 데이터 형태 변환 (🚨 시험 포인트!)
    • 매우 중요한 부분입니다. Scikit-learn의 OneHotEncoder는 1차원 리스트 [0, 1, 2]가 아닌, 2차원 배열 [[0], [1], [2]] 형태의 입력을 받도록 설계되었습니다. reshape(-1, 1)은 1차원 배열을 n행 1열의 2차원 배열로 바꿔주는 역할을 합니다.
  4. Python
     
    import numpy as np
    indices = np.array(indices).reshape(-1, 1)
    # 변환 결과: [[0], [1], [2], [3], [4]]
    
  5. OneHotEncoder 사용
    • OneHotEncoder 객체를 생성합니다. sparse=False 옵션은 결과를 눈으로 보기 쉬운 넘파이(Numpy) 배열로 받기 위함입니다. (기본값은 메모리 효율적인 희소 행렬)
    • fit_transform은 indices 데이터를 학습(fit)하고 원핫 벡터로 변환(transform)하는 작업을 동시에 수행합니다.
  6. Python
     
    from sklearn.preprocessing import OneHotEncoder
    encoder = OneHotEncoder(sparse=False)
    onehot = encoder.fit_transform(indices)
    

3. PyTorch 라이브러리 활용 (영어)

PyTorch는 딥러닝 연구 및 개발에 널리 사용되는 프레임워크로, 원핫 인코딩을 위한 간단한 함수를 제공합니다.

코드 분석 (단계별)

  1. 데이터 준비
    • 미리 정의된 단어 사전과 변환할 단어를 준비합니다.
  2. Python
     
    import torch
    import torch.nn.functional as F
    
    word_to_index = {'I': 0, 'love': 1, 'NLP': 2}
    word = "love"
    
  3. 인덱스를 텐서(Tensor)로 변환
    • PyTorch의 모든 연산은 **텐서(Tensor)**라는 자료구조를 기반으로 합니다. 따라서 'love'의 인덱스인 1을 텐서 형태로 바꿔줍니다.
  4. Python
     
    index = torch.tensor([word_to_index[word]])
    # 결과: tensor([1])
    
  5. F.one_hot 함수 사용
    • F.one_hot 함수를 호출하여 원핫 인코딩을 수행합니다.
    • num_classes (🚨 시험 포인트!): 이 인자는 매우 중요합니다. 원핫 벡터의 전체 길이(차원)를 지정하는 값으로, 보통 단어 사전의 총개수를 넣어줍니다. 이 값을 지정하지 않으면 에러가 발생하거나, PyTorch가 index의 최댓값을 기준으로 길이를 추정하여 원치 않는 결과가 나올 수 있습니다.
  6. Python
     
    one_hot = F.one_hot(index, num_classes=len(word_to_index))
    # 결과: tensor([[0, 1, 0]])
    

🎓 시험 대비 최종 요약

구분 핵심 특징 시험에 나올 만한 포인트
직접 구현 원핫 인코딩의 기본 로직을 이해하는 데 최적. [0] * N으로 영벡터 만들고, 특정 index만 1로 바꾸는 로직 자체를 이해해야 함.
Scikit-learn 머신러닝 파이프라인에 통합하기 좋음. reshape(-1, 1): 입력 데이터를 2차원 배열로 바꿔줘야 하는 이유.
PyTorch 딥러닝 모델의 일부로 사용할 때 필수적. num_classes: 벡터의 길이를 명시적으로 지정해줘야 하는 이유.

결론적으로, 어떤 라이브러리를 사용하든 "단어 사전 생성 → 정수 인덱스 부여 → 해당 인덱스만 1로 바꾸기"라는 원핫 인코딩의 근본 원리는 동일합니다. 각 라이브러리의 고유한 사용법(데이터 형태, 필수 인자 등)을 잘 구분해서 기억해두시면 어떤 코드 문제든 해결할 수 있을 겁니다. 화이팅입니다!

반응형
반응형

https://hanglestocks.tistory.com/109#google_vignette

 

내가 시험공부하려고 만든 자연어처리-전처리 3탄(벡터화)

1. 벡터화(Vectorization)란 무엇인가? (정의 및 핵심 아이디어)가장 먼저 '벡터화'가 무엇인지 정확히 알아야 합니다.정의벡터화란 우리가 사용하는 자연어(예: 한국어, 영어 문장)를 컴퓨터가 이해

hanglestocks.tistory.com

📌 1. 정수 인코딩(Integer Encoding)이란?

정의

정수 인코딩이란 텍스트 데이터를 컴퓨터가 처리할 수 있도록, 각 단어(토큰)에 고유한 정수(Integer)를 부여(mapping)하는 과정입니다.

이전 단계에서 배운 '벡터화'의 가장 기초적인 방법 중 하나예요. 컴퓨터는 '오늘', '날씨' 같은 한글 단어를 직접 이해하지 못하기 때문에, 미리 만들어 둔 **단어 사전(Vocabulary)**을 기준으로 각 단어를 숫자 ID로 바꿔주는 거죠.

  • 핵심 원리:
    1. 가지고 있는 전체 텍스트(코퍼스)에서 모든 단어를 뽑아 중복을 제거한 단어 사전을 만든다.
    2. 사전에 있는 각 단어에 1부터 시작하는 고유한 정수 번호를 붙인다.
    3. 문장이 들어오면, 문장을 단어 단위로 쪼갠 뒤, 각 단어를 사전에서 찾아 해당 정수 번호로 변환한다.

결과적으로 "오늘 날씨가 좋다"라는 문장은 [1, 2, 3] 과 같은 **숫자의 나열(Sequence)**이 되고, 이 숫자 시퀀스가 컴퓨터 모델의 입력값으로 사용됩니다.


🔢 2. 정수 인코딩 예시로 과정 이해하기

슬라이드에 나온 예시를 통해 과정을 하나하나 뜯어보겠습니다.

  • 문장 1: "오늘 날씨가 좋아요"
  • 문장 2: "내일 날씨를 알려주세요"

1단계: 토큰화 및 단어 사전(Vocabulary) 구축

먼저, 두 문장에 있는 모든 단어를 뽑아내고 기본형으로 바꾼 뒤(표제어 추출), 중복을 제거해서 사전을 만듭니다.

  • 문장 1에서 나온 단어: "오늘", "날씨", "좋다"
  • 문장 2에서 나온 단어: "내일", "날씨", "알려주다"
  • → 최종 단어 사전: {"오늘", "날씨", "좋다", "내일", "알려주다"}

2단계: 각 단어에 고유 정수 부여

이제 사전의 각 단어에 1부터 시작하는 번호를 붙여줍니다.

  • {"오늘": 1, "날씨": 2, "좋다": 3, "내일": 4, "알려주다": 5}
  • (💡 시험 포인트!) 여기서 정수를 부여하는 기준은 여러 가지가 될 수 있습니다. 그냥 가나다순으로 할 수도 있고, 이미지에 노란색으로 강조된 것처럼 빈도수를 고려할 수도 있습니다. 즉, 텍스트 전체에서 자주 등장하는 단어에 더 낮은 번호(예: 1번, 2번)를 부여하는 방식이 일반적입니다. "날씨"는 두 문장에 모두 등장했으니 가장 빈도수가 높다고 볼 수 있겠네요. (예시에서는 편의상 순서대로 부여했습니다.)

3단계: 문장을 정수 시퀀스로 변환

만들어진 사전을 바탕으로 원래 문장을 변환합니다.

  • 문장 1 인코딩: "오늘 날씨 좋다" → [1, 2, 3]
  • 문장 2 인코딩: "내일 날씨 알려주다" → [4, 2, 5]

이제 컴퓨터는 [1, 2, 3]이라는 숫자 배열을 보고 "오늘 날씨가 좋아요"라는 문장으로 인식하고 처리할 수 있게 됩니다.


👍👎 3. 정수 인코딩의 장점과 단점 (★시험 핵심★)

정수 인코딩은 간단하지만 명확한 한계가 있습니다. 이 장단점을 비교하는 문제가 시험에 자주 나옵니다.

장점 (Advantages)

  1. 구현이 간단하고 직관적이다
    • 사전 만들고, 각 단어를 해당하는 숫자로 바꾸기만 하면 되므로 원리가 매우 단순하고 이해하기 쉽습니다.
  2. 텍스트를 빠르게 숫자로 변환 가능하다
    • 복잡한 계산 없이 사전을 조회(lookup)하는 방식이라 처리 속도가 매우 빠릅니다.

단점 (Disadvantages)

  1. 단어 간 의미/유사성 반영 불가 (가장 큰 문제!)
    • 예를 들어 '날씨'는 2번, '내일'은 4번입니다. 숫자의 크기(4 > 2)는 단어의 의미와 아무런 관련이 없습니다. '오늘'(1번)과 '내일'(4번)이 '알려주다'(5번)보다 의미적으로 더 가까운데도, 정수 값만으로는 이런 관계를 전혀 알 수 없습니다.
    • 모델이 숫자 자체의 크기나 순서에서 어떤 패턴을 학습하려는 오해를 불러일으킬 수 있습니다.
  2. 사전 크기가 커지면 관리 어려움
    • 분석해야 할 텍스트의 양이 방대해지면 사전에 있는 단어의 수가 수만, 수십만 개로 늘어납니다. 이렇게 되면 사전을 메모리에 유지하고 관리하는 것이 부담될 수 있습니다.
  3. OOV (Out-of-Vocabulary) 문제
    • OOV란 **'사전에 없는 단어'**를 의미합니다. 학습할 때 만든 사전에 없던 새로운 단어(신조어 등)가 테스트 문장에 나타나면, 이를 어떤 숫자로 바꿔야 할지 알 수 없습니다.
    • 이 문제를 해결하기 위해 보통 사전에 없는 단어는 <UNK> (Unknown) 같은 특별 토큰으로 처리하지만, 이 경우 새로운 단어의 정보는 모두 잃어버리게 됩니다.

🎓 시험 대비 최종 요약

  • 정수 인코딩이란? 단어를 고유한 정수 ID로 바꾸는 가장 기초적인 벡터화 방법.
  • 핵심 과정: 단어 사전 구축 → 각 단어에 정수 부여 (주로 빈도수 높은 순) → 문장을 정수 배열로 변환.
  • 장점: 간단하고 빠르다.
  • 치명적 단점: 단어의 의미 관계를 전혀 표현하지 못하고, OOV 문제에 취약하다.

정수 인코딩은 그 자체로 쓰이기보다는, 이후에 배울 원-핫 인코딩이나 워드 임베딩으로 나아가기 위한 중간 단계로 이해하는 것이 중요합니다. 이 단점들을 극복하기 위해 더 발전된 벡터화 기법들이 등장했기 때문입니다.

전체 과정은 "원시 텍스트 → 텍스트 정제 → 단어 빈도수 계산 → 정수 부여" 순서로 진행됩니다. 각 단계를 슬라이드와 함께 따라가 보겠습니다.

1단계: 원시 텍스트 준비 (Slide 11)

Python
 
raw_text = "A barber is a person. a barber is good person. a barber is huge person. he Knew A Secret! The Secret He Kept is huge secret. His barber kept his word. A barber kept his word. His barber kept his secret. But keeping and keeping such a huge secret to himself was driving the barber crazy. the barber went up a huge mountain."
  • 설명: 모든 것은 이 raw_text라는 변수에 담긴 하나의 긴 문자열에서 시작됩니다. 우리가 분석하고 처리해야 할 원본 데이터이죠. 문장 부호, 대소문자 등이 모두 섞여 있는, 아직 정제되지 않은 상태입니다.

2단계: 텍스트 정제 및 토큰화 (Slide 12)

이제 지저분한 원본 텍스트를 깨끗하게 다듬는, 전처리(Preprocessing) 과정을 거칩니다. 코드가 길어 보이지만, 하나씩 보면 간단합니다.

Python
 
# 문장 토큰화
from nltk.tokenize import sent_tokenize, word_tokenize
from nltk.corpus import stopwords

sentences = sent_tokenize(raw_text)
# 출력: ['A barber is a person.', 'a barber is good person.', ...]
  1. 문장 토큰화 (sent_tokenize): 먼저 긴 텍스트를 ., ?, ! 등을 기준으로 문장 단위로 자릅니다. sentences는 이제 각 문장이 하나의 원소로 들어있는 리스트가 됩니다.
Python
 
# 단어 토큰화 및 정제
preprocessed_sentences = []
stop_words = set(stopwords.words('english')) # 불용어 집합

for sentence in sentences:
    tokenized_sentence = word_tokenize(sentence) # 문장을 단어로 쪼갬
    result = []

    for word in tokenized_sentence:
        word = word.lower() # 1. 모든 단어를 소문자로 변환
        if word not in stop_words: # 2. 불용어 제거
            if len(word) > 2: # 3. 길이가 2 이하인 단어 제거
                result.append(word)

    preprocessed_sentences.append(result)

print(preprocessed_sentences)
# 출력: [['barber', 'person'], ['barber', 'good', 'person'], ...]
  1. 단어 토큰화 및 정제 (for 반복문): 각 문장을 순회하며 단어를 쪼개고 깨끗하게 만듭니다.
    • word_tokenize(sentence): 문장을 ['A', 'barber', 'is', 'a', 'person', '.'] 처럼 단어와 문장부호 단위로 쪼갭니다.
    • 핵심 정제 과정 (안쪽 for 반복문)
      1. word.lower(): 'Barber'와 'barber'를 같은 단어로 취급하기 위해 모두 소문자로 통일합니다.
      2. if word not in stop_words: stop_words는 'a', 'is', 'the' 와 같이 문법적으로는 필요하지만 의미 분석에는 큰 도움이 안 되는 불용어 목록입니다. 이들을 제거하여 핵심 단어만 남깁니다.
      3. if len(word) > 2: 길이가 짧은 단어(예: 's', 'to')는 보통 의미가 없거나 문법 요소인 경우가 많아 제거합니다.
    • 결과: preprocessed_sentences에는 위 정제 과정을 모두 거친 단어들만 문장 단위로 묶여서, 2차원 리스트 형태로 저장됩니다.

3단계: 단어 리스트 통합 (Slide 13)

Python
 
all_words_list = sum(preprocessed_sentences, [])
print(all_words_list)
# 출력: ['barber', 'person', 'barber', 'good', 'person', ...]
  • 설명: 이제 각 단어의 빈도수를 세기 위해, 문장 단위로 나뉘어 있던 2차원 리스트([['barber'], ['good', 'person']])를 하나의 긴 1차원 리스트(['barber', 'good', 'person'])로 합쳐줍니다. sum(..., [])은 이런 2차원 리스트를 1차원으로 펼치는 간단한 파이썬 기술입니다.

4단계: 단어 사전 생성 및 정수 인코딩 (Slide 14, 15)

드디어 각 단어에 정수를 부여하는 핵심 단계입니다! 빈도수 높은 순서대로 번호를 부여할 겁니다.

  1. 단어 빈도수 계산 (Slide 14, Counter 사용)
    • Counter는 리스트에 있는 각 원소(단어)가 몇 번 등장했는지 세어서 딕셔너리 형태로 만들어 줍니다. 이제 'barber'가 8번, 'secret'이 6번 나왔다는 것을 알 수 있습니다.
  2. Python
     
    from collections import Counter
    
    vocab = Counter(all_words_list)
    # 출력: Counter({'barber': 8, 'secret': 6, 'huge': 5, ...})
    
  3. 빈도수 상위 단어 추출 (Slide 14)
    • most_common(5)는 가장 빈도수가 높은 상위 5개의 단어만 (단어, 빈도수) 형태의 튜플 리스트로 추출합니다. vocab_size를 조절해 사전의 크기를 결정할 수 있습니다.
  4. Python
     
    vocab_size = 5
    vocab = vocab.most_common(vocab_size)
    # 출력: [('barber', 8), ('secret', 6), ('huge', 5), ('kept', 4), ('person', 3)]
    
  5. 단어에 정수 ID 부여 (Slide 15, 최종 단계)
    • 이제 최종 목표인 word_to_index 사전을 만듭니다.
    • 빈도수 상위 단어 리스트(vocab)를 순회하면서, 가장 빈번한 'barber'부터 1번, 그다음 'secret'에 2번, ... 이렇게 차례대로 정수를 부여합니다.
    • (💡 시험 포인트!) 코드를 보면 index + 1 또는 i = i + 1을 사용해 1부터 번호를 시작합니다. 0번은 보통 패딩(padding, 문장 길이를 맞추기 위한 빈 공간)이나 OOV(사전에 없는 단어)를 위해 남겨두는 것이 일반적이기 때문입니다. 이 관례를 기억해두는 것이 중요합니다.
  6. Python
     
    word_to_index = {}
    i = 0
    for (word, frequency) in vocab:
        i = i + 1
        word_to_index[word] = i
    
    print(word_to_index)
    # 출력: {'barber': 1, 'secret': 2, 'huge': 3, 'kept': 4, 'person': 5}
    

🎓 시험 대비 최종 요약

  • 1단계 (텍스트 준비): 하나의 긴 문자열 raw_text에서 시작.
  • 2단계 (정제): sent_tokenize로 문장 분리 → word_tokenize로 단어 분리 → lower()로 소문자화 → stopwords로 불용어 제거 → 짧은 단어 제거.
  • 3단계 (통합): 2차원 리스트를 1차원으로 펼쳐 전체 단어 리스트 생성.
  • 4단계 (인코딩): Counter로 단어 빈도수 계산 → most_common으로 상위 N개 단어 선정 → 빈도수 높은 순서대로 1부터 정수 ID 부여하여 word_to_index 사전 완성!

이제 이 word_to_index 사전을 이용하면 어떤 문장이든 정수 시퀀스로 바꿀 수 있습니다. 이 전체 흐름과 각 단계에서 사용하는 파이썬 함수들의 역할을 정확히 이해하면 시험에서 어떤 코드가 나와도 자신 있게 분석하실 수 있을 거예요!

 

반응형
반응형

1. 벡터화(Vectorization)란 무엇인가? (정의 및 핵심 아이디어)

가장 먼저 '벡터화'가 무엇인지 정확히 알아야 합니다.

정의

벡터화란 우리가 사용하는 자연어(예: 한국어, 영어 문장)를 컴퓨터가 이해할 수 있는 숫자의 나열, 즉 벡터(Vector)로 변환하는 과정입니다.

컴퓨터는 '사과'라는 단어의 의미를 직접적으로 이해하지 못합니다. 오직 숫자만을 처리할 수 있죠. 그래서 '사과'라는 텍스트 토큰(Text Token)을 [0.1, 0.7, -0.2, ...] 와 같은 고정된 차원(길이)을 가진 숫자 벡터로 바꿔주는 것입니다. 이렇게 해야만 컴퓨터 모델(예: 인공지능)이 텍스트 데이터를 입력받아 학습하고, 분석하고, 새로운 문장을 생성하는 등의 작업을 할 수 있습니다.

주요 아이디어 (핵심 원리)

벡터화가 그냥 단어를 숫자로 바꾸기만 하는 단순한 작업이 아닙니다. 여기에는 두 가지 매우 중요한 아이디어가 담겨 있습니다.

  1. 의미를 공간에 표현 (의미적 공간 매핑)
    • 모든 단어(토큰)는 고유한 벡터 값을 가지며, 이 벡터는 고차원 벡터 공간(High-dimensional Vector Space)의 한 점으로 표현됩니다.
    • * **핵심:** 단어의 '의미'가 벡터 공간상의 '위치'와 '방향'으로 표현됩니다. 이미지에 나온 그래프처럼 'shark(상어)', 'animal(동물)'과 같이 의미가 비슷한 단어들은 벡터 공간에서 서로 가까운 곳에 위치하게 됩니다. 반면, 'dictatorship(독재)'처럼 의미가 전혀 다른 단어는 멀리 떨어져 있죠.
      
  2. 유사한 의미는 가까운 벡터로 (의미 유사도 계산)
    • 단어들의 의미가 비슷할수록, 그 단어들의 벡터 값도 서로 유사해집니다.
    • 이를 통해 컴퓨터는 단어 벡터들 간의 거리를 계산하거나(예: 유클리드 거리) 각도를 계산하여(예: 코사인 유사도) 두 단어가 얼마나 의미적으로 비슷한지를 수학적으로 판단할 수 있습니다.
    • 가장 유명한 예시로 "킹" - "남자" + "여자" ≈ "퀸" 이라는 벡터 연산이 가능한 것도 바로 이 원리 덕분입니다.

🔢 2. 벡터화는 어떤 과정으로 이루어지는가? (프로세스)

그렇다면 텍스트를 벡터로 바꾸는 작업은 구체적으로 어떤 단계를 거칠까요? 총 4단계로 나눌 수 있습니다.

  1. Corpus(말뭉치) 형성
    • 설명: 분석하고 싶은 전체 텍스트 데이터를 준비하는 단계입니다. 이 텍스트 묶음을 **'코퍼스(Corpus)'**라고 부릅니다.
    • 예시: 위키피디아 전체 문서, 네이버 영화 리뷰 10만 건, 특정 소설책 한 권 등이 모두 코퍼스가 될 수 있습니다. 모델을 학습시키기 위한 '교과서'를 만드는 과정이라고 생각하면 쉽습니다.
  2. 어휘집(Vocabulary) 구축
    • 설명: 준비된 코퍼스에 있는 모든 문서를 **토큰화(Tokenization)**하여, 중복을 제거한 고유한 토큰들의 집합을 만듭니다. 이것을 **'어휘집'**이라고 합니다. 토큰화란 문장을 의미 있는 최소 단위(보통 단어)로 자르는 것을 말합니다.
    • 예시: "나는 사과를 먹는다. 너는 바나나를 먹는다." 라는 코퍼스가 있다면, 토큰화 후 어휘집은 {'나', '는', '사과', '를', '먹는다', '너', '바나나'} 가 됩니다.
  3. 벡터화 사전(Dictionary) 정의
    • 설명: 어휘집에 있는 각 토큰(단어)에 고유한 벡터 값을 할당하는 단계입니다. 즉, 단어와 벡터를 짝지어주는 '사전'을 만드는 것입니다.
    • 예시: 어휘집의 '사과'라는 단어에는 [0.1, 0.7, -0.2]를, '바나나'에는 [0.2, 0.8, -0.1]를 할당하는 식입니다. (실제 벡터는 이보다 훨씬 깁니다.)
  4. 텍스트 벡터화
    • 설명: 이제 새로운 문장이나 문서가 들어오면, 위에서 만든 벡터화 사전을 참조하여 문장을 벡터로 변환합니다.
    • 예시: "나는 바나나를 먹는다"라는 문장이 들어오면, '나', '바나나', '를', '먹는다' 각각의 토큰을 사전에서 찾아 해당 벡터로 바꾼 후, 이 벡터들을 합치거나 평균을 내는 등의 방법으로 문장 전체를 대표하는 하나의 벡터를 만들어 냅니다. 이 벡터가 모델의 최종 입력값이 됩니다.

📊 3. 벡터 표현의 종류 (매우 중요!)

벡터화를 하는 방법은 여러 가지가 있으며, 각각의 특징이 뚜렷해서 시험에 단골로 출제되는 부분입니다. 반드시 구분해서 암기해야 합니다.

1. 희소 표현(Sparse Representation) vs. 밀집 표현(Dense Representation)

  • 희소 표현 (Sparse Representation)
    • 정의: 벡터의 대부분의 값이 0으로 채워져 있는 표현 방식입니다.
    • 대표적인 예: 원-핫 인코딩(One-Hot Encoding). 어휘집의 크기가 10,000개라면, 각 단어는 10,000차원의 벡터가 되고, 자신의 인덱스에만 1을, 나머지 9,999개는 모두 0을 가집니다. (예: '사과'가 3번 인덱스라면 [0, 0, 1, 0, ..., 0])
    • 단점: **차원의 저주(Curse of Dimensionality)**에 걸리기 쉽습니다. 어휘집이 커질수록 벡터의 차원이 한없이 커져 메모리 낭비가 심하고 계산이 비효율적입니다. 또한, 모든 단어 벡터 간의 거리가 동일하여 단어의 의미적 유사도를 표현하지 못합니다.
  • 밀집 표현 (Dense Representation)
    • 정의: 벡터의 대부분의 값이 0이 아닌 실수로 채워져 있는, 저차원의 벡터 표현 방식입니다.
    • 특징: 단어의 의미를 여러 차원에 분산하여 표현합니다. (예: 200~300차원). 따라서 적은 차원으로도 단어의 의미적, 문법적 관계를 풍부하게 담을 수 있습니다.
    • 대표적인 예: Word2Vec, GloVe, FastText 와 같은 워드 임베딩(Word Embedding) 기법들이 여기에 속합니다. 우리가 위에서 말한 '의미가 비슷하면 벡터도 가깝다'는 개념은 바로 이 밀집 표현 덕분에 가능합니다.

2. 정적 임베딩(Static Embedding) vs. 문맥적 임베딩(Contextualized Embedding)

  • 정적 임베딩 (Static Embedding)
    • 정의: 한번 학습되고 나면, 단어의 벡터 값이 고정되어 변하지 않는 방식입니다. 어떤 문장에 쓰이든 같은 단어는 항상 같은 벡터를 가집니다.
    • 예시: "금융 은행에 가다" 와 "강둑 은행에 앉다" 에서 '은행'은 서로 다른 의미지만, 정적 임베딩에서는 두 '은행'의 벡터가 완전히 동일합니다.
    • 대표적인 모델: Word2Vec, GloVe, FastText
  • 문맥적 임베딩 (Contextualized Embedding)
    • 정의: 단어가 놓인 문맥에 따라 벡터 값이 동적으로 달라지는 방식입니다. 다의어(여러 의미를 가진 단어) 문제를 해결할 수 있습니다.
    • 예시: "금융 은행에 가다" 에서의 '은행' 벡터와 "강둑 은행에 앉다" 에서의 '은행' 벡터는 주변 단어들('금융', '강둑')의 영향을 받아 서로 다른 값을 갖게 됩니다.
    • 대표적인 모델: BERT, ELMo, GPT 와 같은 최신 트랜스포머(Transformer) 기반의 언어 모델들이 여기에 속합니다.

3. 규칙 기반(Rule-based) vs. 학습 기반(Learned) 표현

  • 규칙 기반 표현 (Rule-based Representation)
    • 정의: 사람이 정한 간단한 규칙에 따라 벡터를 생성하는 방식입니다.
    • 대표적인 예: 단어의 등장 횟수를 세는 **Bag-of-Words (BoW)**나 TF-IDF 가 대표적입니다. 이 방법들은 단어의 의미를 학습하는 것이 아니라, 통계적인 정보(빈도수 등)를 벡터에 직접적으로 반영합니다.
  • 학습 기반 표현 (Learned Representation)
    • 정의: 대규모 코퍼스를 신경망 모델에 학습시켜, 데이터로부터 단어의 의미 관계를 스스로 '학습'하게 하여 벡터를 생성하는 방식입니다.
    • 대표적인 예: Word2Vec, BERT 등 대부분의 밀집 표현, 문맥적 임베딩 방식이 여기에 해당합니다. 이 방법들은 예측과 같은 특정 과제를 수행하는 과정에서 최적의 벡터 표현을 찾아냅니다.

🎓 시험 대비 최종 요약

  • 벡터화란? 텍스트를 컴퓨터가 이해하는 숫자 벡터로 바꾸는 것.
  • 왜? 단어의 의미를 벡터 공간에 표현하고, 의미적 유사도를 수학적으로 계산하기 위해.
  • 과정은? 코퍼스 준비 → 어휘집 구축 → 벡터 사전 정의 → 텍스트 벡터화
  • 종류는? (★매우 중요★)
    • Sparse (희소): 대부분 0, 차원 큼 (예: One-Hot Encoding)
    • Dense (밀집): 실수로 꽉 참, 차원 작고 의미 풍부 (예: Word2Vec)
    • Static (정적): 문맥 상관없이 단어 벡터 고정 (예: Word2Vec, GloVe)
    • Contextualized (문맥적): 문맥에 따라 단어 벡터가 변함 (예: BERT, GPT)
반응형
반응형

1. 후속 처리 (Post-processing) 최종 요약

후속 처리는 토큰화된 텍스트를 모델 학습에 더 적합한 형태로 만들기 위해 한 번 더 가공하는 단계입니다.

후속 처리 기법 비교 (시험 핵심)

기법 정의 예시 장점 단점
불용어(Stopwords) 제거 의미 정보가 거의 없는 단어(is, the, 은, 는 등)를 제거 is, the 제거 차원 축소, 효율 향상 의미 손실 가능성 (뉘앙스 사라짐)
어간 추출(Stemming) 단순 규칙으로 단어의 접사를 잘라 어간만 남김 studies → studi 효율 향상 결과가 실제 단어가 아닐 수 있음
표제어 추출(Lemmatization) 사전과 품사 정보를 활용해 단어의 **기본형(원형)**을 찾음 Studies (동사) → study 실제 단어 도출 속도가 느림

⚠️ 현대 NLP에서의 중요도

슬라이드의 노란색 하이라이트가 핵심입니다. 위에서 설명한 후속 처리 기법들은 주로 전통적인 NLP 모델에서 사용되었습니다. BERT, GPT와 같은 최신 대규모 언어 모델(LLM)은 문맥을 스스로 파악하는 능력이 뛰어나기 때문에, 이러한 후속 처리를 잘 사용하지 않습니다. 오히려 불용어를 제거하거나 단어를 원형으로 바꾸면 문장의 미묘한 뉘앙스가 사라져 모델의 성능이 저하될 수 있습니다.


2. 정규 표현식 (Regular Expression) 완벽 정복

정규 표현식(Regex)은 복잡한 문자열 속에서 특정한 패턴을 찾아 처리하기 위해 사용하는 강력한 형식 언어입니다.

주요 기능

  • 검색(Search): 이메일 주소, URL, 전화번호처럼 일정한 규칙을 가진 문자열을 찾아냅니다.
  • 추출(Extract): 문자열에서 숫자만, 혹은 특정 부분만 잘라냅니다.
  • 치환(Replace): 주민등록번호 뒷자리를 *로 바꾸는 것처럼, 특정 패턴을 다른 문자로 바꿉니다.

핵심 문법 (이것만은 꼭 외우세요!)

기호 설명 기호 설명
. 아무 문자 1개 (줄바꿈 제외) \d 숫자 [0-9]
* 앞 문자가 0번 이상 반복 \D 숫자가 아닌 것 [^0-9]
+ 앞 문자가 1번 이상 반복 \w 문자, 숫자, _ [a-zA-Z0-9_]
? 앞 문자가 0번 또는 1번 등장 \W 문자, 숫자, _가 아닌 것
[] 괄호 안의 문자 중 하나 (e.g., [abc]) \s 공백 문자 (띄어쓰기, 탭 등)
[^] 괄호 안의 문자를 제외한 나머지 \S 공백이 아닌 것

Python re 모듈 주요 함수

함수 설명
re.findall(패턴, 문자열) 패턴과 일치하는 모든 부분을 찾아 리스트로 반환
re.search(패턴, 문자열) 문자열 전체를 검색하여 첫 번째로 일치하는 객체를 반환
re.sub(패턴, 바꿀문자, 문자열) 패턴과 일치하는 부분을 다른 문자로 치환
re.split(패턴, 문자열) 패턴을 기준으로 문자열을 분리하여 리스트로 반환

정규 표현식을 이용한 토큰화 예시

정규 표현식은 토큰화를 더 정교하게 만드는 데 사용될 수 있습니다.

  • RegexpTokenizer("[\w]+")
    • 의미: \w(문자, 숫자)가 +(1번 이상 반복)되는 패턴을 하나의 토큰으로 간주합니다.
    • 결과: 문장에서 구두점('.`)은 모두 버리고 오직 단어와 숫자만 추출합니다.
  • RegexpTokenizer("\s+", gaps=True)
    • 의미: \s (공백)이 +(1번 이상 반복)되는 패턴을 **토큰을 나누는 기준(gaps)**으로 삼습니다.
    • 결과: split() 함수처럼 공백을 기준으로 문장을 토큰화합니다.
반응형
반응형

WordPiece가 BPE와 비슷해서 헷갈리기 쉬운데, '어떤 기준으로 토큰을 합치는가' 라는 핵심 차이점에 집중하면 명확하게 이해할 수 있습니다.

## WordPiece의 핵심 비유: '단순 인기투표' vs. '팀 시너지'

BPE와 WordPiece를 팀을 구성하는 두 명의 다른 매니저에 비유해 볼게요.

  • BPE 매니저 (단순 인기투표): 이 매니저는 단순히 "누구랑 누가 가장 자주 같이 다니지?"만 봅니다. A와 B가 가장 자주 붙어 다니면, 그냥 둘을 한 팀으로 묶어버립니다. 매우 간단한 방식이죠.
  • WordPiece 매니저 (팀 시너지 분석): 이 매니저는 더 똑똑합니다. "어떤 두 사람을 한 팀으로 묶었을 때, 팀 전체의 성과(시너지)가 가장 크게 올라갈까?"를 분석합니다. A와 B가 자주 다니긴 하지만, 팀으로 묶었을 때 성과가 10만큼 오릅니다. 반면, C와 D는 덜 자주 다니지만, 한번 뭉치면 팀 성과가 100만큼 폭발합니다. WordPiece 매니저는 당연히 C와 D를 한 팀으로 묶습니다.

여기서 '팀 전체의 성과(시너지)' 가 바로 WordPiece가 사용하는 '확률(Likelihood)' 개념입니다.


## WordPiece의 작동 방식 (단계별 설명)

WordPiece는 어떻게 '팀 시너지'를 계산할까요? 다음 단계를 따릅니다.

1단계: 초기화

모든 단어를 글자 단위로 분해합니다. 이것은 BPE와 동일합니다.

  • hugging → h, u, g, g, i, n, g

2단계: 모든 후보 쌍의 '시너지' 점수 계산

이제 텍스트에 있는 모든 인접한 글자 쌍(h,u, u,g, g,g 등)을 후보로 올립니다. 그리고 각 후보 쌍을 합쳤을 때, 전체 텍스트가 나타날 확률이 얼마나 증가하는지 '시너지 점수'를 모두 계산합니다.

이 점수는 다음과 같은 질문에 답하는 것과 같습니다.

"두 글자(u, g)가 각각 따로따로 나타날 확률에 비해, 이 둘이 합쳐진 ug라는 하나의 덩어리로 나타날 확률이 얼마나 더 높은가?"

예를 들어, e와 r은 영어에서 매우 강력한 조합입니다. 이 둘은 거의 항상 붙어 다니죠. 따라서 (e, r)을 er로 합치는 것은 '시너지 점수'가 매우 높게 나옵니다. 반면 x와 q처럼 우연히 몇 번 붙어 나온 쌍은 점수가 낮게 나옵니다.

3단계: 최고의 '시너지'를 내는 단 하나의 쌍을 병합

수많은 후보 쌍 중에서, 계산된 '시너지 점수'가 가장 높은 단 하나의 쌍을 선택합니다. 예를 들어 그게 (e, r)이었다고 가정합시다.

그러면 WordPiece는 텍스트 전체를 훑으면서 모든 e, r 쌍을 er이라는 하나의 토큰으로 합쳐버립니다.

4단계: 목표 횟수만큼 반복

이제 텍스트의 구성이 바뀌었으니, 2단계로 돌아가 다시 모든 후보 쌍의 '시너지 점수'를 계산하고, 가장 점수가 높은 쌍을 합칩니다. 이 과정을 미리 정해둔 어휘집 크기가 될 때까지 (예: 30,000번) 반복합니다.


## BERT와 ##의 의미

이런 '시너지' 기반 방식 때문에 WordPiece는 언어적으로 의미 있는 단위를 기가 막히게 찾아냅니다. 특히 단어의 의미를 바꾸는 **접두사(un-, re-)**나 **접미사(-ing, -able)**는 시너지 점수가 매우 높게 나와 하나의 토큰으로 만들어지는 경향이 강합니다.

  • unaffordable → un + afford + able (의미 단위로 잘 쪼개짐)

이때 ## 표시는 "이 토큰은 단어의 시작이 아니라, 앞 토큰에 이어서 붙는 중간 부분입니다"라는 것을 알려주는 단순한 표식입니다.

  • tokenizing → ['token', '##izing']
  • hugging → ['hugg', '##ing']

token은 단어의 시작이지만, ##izing은 앞 단어 token에 붙는다는 의미입니다.

 

'시너지 점수'를 계산하는 방법을 구체적인 숫자로 보여드릴게요. 복잡한 수학 공식보다는 그 핵심 원리에 집중해서 설명해 드리겠습니다.

WordPiece의 시너지 점수는 이 질문에 대한 답을 찾는 과정입니다:

"두 토큰이 그냥 우연히 이웃일 확률에 비해, 실제로 함께 붙어 다닐 확률이 얼마나 더 높은가?"

이 '얼마나 더 높은가'를 나타내는 점수가 높을수록 시너지가 강한, 즉 합쳐야 할 우선순위가 높은 쌍이 됩니다.


## 점수 계산 과정 (예시)

아주 작은 텍스트에 1,000개의 토큰이 있고, 그 안에서 두 후보 쌍 (e, r)과 (u, g)의 시너지를 계산해 본다고 가정합시다.

1단계: 각 토큰의 개별 등장 횟수 계산

먼저 텍스트 전체에서 각 토큰이 몇 번 나왔는지 셉니다.

  • e의 등장 횟수: 50번
  • r의 등장 횟수: 40번
  • u의 등장 횟수: 80번
  • g의 등장 횟수: 60번

2단계: '우연히 만날 확률' 계산

만약 모든 토큰이 무작위로 배열된다면, 두 토큰이 우연히 나란히 붙어있을 확률은 각자의 등장 확률을 곱한 것과 같습니다.

  • e와 r이 우연히 만날 확률: P(e) × P(r)
    • (50/1000) × (40/1000) = 0.05 × 0.04 = 0.002
  • u와 g가 우연히 만날 확률: P(u) × P(g)
    • (80/1000) × (60/1000) = 0.08 × 0.06 = 0.0048

3단계: '실제로 함께 다닌 횟수' 계산

이제 텍스트에서 실제로 두 토큰이 붙어서 등장한 횟수를 셉니다.

  • (e, r) 쌍의 실제 등장 횟수: 38번
  • (u, g) 쌍의 실제 등장 횟수: 10번

4단계: '시너지 점수' 최종 계산

이제 '실제 확률'을 '우연 확률'로 나누어, 얼마나 더 특별한 관계인지 점수를 매깁니다. (실제로는 로그 확률을 사용하지만 원리는 동일합니다.)

  • (e, r)의 시너지 점수:
    • 실제 확률: 38 / 1000 = 0.038
    • 점수 = 실제 확률 / 우연 확률 = 0.038 / 0.002 = 19
  • (u, g)의 시너지 점수:
    • 실제 확률: 10 / 1000 = 0.01
    • 점수 = 실제 확률 / 우연 확률 = 0.01 / 0.0048 ≈ 2.08

## 결론

  • (e, r) 쌍은 우연히 만날 확률보다 19배나 더 높은 확률로 함께 등장했습니다. 이것은 둘 사이에 매우 끈끈한 '시너지'가 있다는 강력한 증거입니다.
  • (u, g) 쌍은 우연보다 약 2배 정도만 높습니다. 그냥 각자 흔하다 보니 어쩌다 마주친 것에 가깝습니다.

WordPiece 알고리즘은 이 계산을 모든 후보 쌍에 대해 수행한 뒤, 시너지 점수가 가장 높은 (e, r)을 선택하여 er이라는 새로운 토큰으로 병합하는 결정을 내립니다.

반응형
반응형

SentencePiece는 텍스트를 언어학적으로 이해해서 의미를 파악하는 것이 아닙니다. 대신, 어떤 언어든 상관없이 적용할 수 있는 매우 영리한 통계적 방법으로 텍스트를 '부품(subword)'으로 분해하여, 진짜 의미를 파악할 AI 모델이 이해하기 좋은 형태로 바꿔주는 역할을 합니다.

핵심 아이디어는 "공백(띄어쓰기)도 하나의 일반 글자로 취급하자" 입니다.


## SentencePiece의 작동 방식

기존 BPE나 WordPiece는 먼저 띄어쓰기로 단어를 나눈 뒤, 그 단어 안에서 서브워드를 학습했습니다. 이 방식은 한국어나 영어에서는 잘 통하지만, 띄어쓰기가 없는 일본어나 중국어에는 적용하기 어렵습니다.

SentencePiece는 이 문제를 해결하기 위해 다음과 같은 과정을 거칩니다.

1. 모든 것을 '날 것(Raw Text)'으로 취급

SentencePiece는 입력된 문장을 사전 처리 없이 그냥 하나의 긴 글자 시퀀스로 봅니다. 그리고 공백(space)을 ' ' (언더바)라는 특수 문자로 치환합니다.

  • 입력: 학생이 책을 읽었다
  • 변환: _학생이_책을_읽었다

이제 띄어쓰기는 사라지고, 단어의 시작을 알리는 _ 기호가 포함된 글자들의 나열이 되었습니다.


2. 공백 포함, 통계 기반으로 학습

이제 _학생이_책을_읽었다 라는 글자 덩어리에 BPE나 Unigram Language Model 같은 알고리즘을 적용합니다.

알고리즘은 데이터에서 가장 빈번하게 등장하거나, 확률적으로 가장 자연스러운 글자 쌍을 찾아 합치는 과정을 반복합니다. 이 과정에서 _ 기호도 다른 글자와 똑같이 취급되어 병합에 참여합니다.

  • (학, 생) → 학생
  • (읽, 었) → 읽었
  • (_책, 을) → _책을
  • (_학생, 이) → _학생이

이런 과정을 수없이 반복하면, 최종적으로 어휘집(Lexicon)에는 다음과 같은 다양한 종류의 토큰들이 저장됩니다.

  • 기본 글자: _, 학, 생, 이, 책, 을, ...
  • 의미 단위(형태소): 이, 을, 었, 는, 다
  • 공백이 포함된 단어/서브워드: _책, _학생, _읽었다, _대한민국

## 의미 파악의 진짜 주체

이렇게 만들어진 어휘집을 사용해 책을 읽는 학생이라는 새로운 문장을 토큰화하면, 다음과 같이 분해됩니다.

[' 책', '을', ' 읽', '는', ' 학생']

이 토큰들은 각각 고유한 숫자 ID로 변환되어 [312, 18, 540, 19, 481] 와 같은 형태로 AI 모델(BERT, GPT 등)에 전달됩니다.

결론적으로, 의미 파악은 SentencePiece가 하는 것이 아닙니다.

  • SentencePiece: 텍스트를 통계적으로 가장 효율적인 부품(토큰)으로 분해하는 **'재료 손질 전문가'**입니다.
  • AI 모델 (BERT, GPT): 손질된 재료(토큰 시-퀀스)를 수없이 많이 보고 학습하면서, ' 책'이라는 토큰 뒤에 '을'이라는 토큰이 오면 목적어일 확률이 높다는 식의 **문맥적 관계와 패턴을 익혀 진짜 '의미'를 파악하는 '메인 셰프'**입니다.

서브워드 토크나이저의 핵심 특징 세 가지를 누구나 이해할 수 있도록 쉽게 풀어서 설명해 드릴게요.

## 1. "서브워드 (통계 단위)" 란? 🤖

한마디로, 사람의 문법 지식이 아닌, 컴퓨터가 데이터만 보고 통계적으로 찾아낸 효율적인 글자 묶음이라는 뜻입니다.

  • 언어학적 단위 (형태소): 사람이 문법 규칙에 따라 의미를 기준으로 나눈 것입니다.
    • 🔬 예시: 학생이학생(명사) + (조사)
    • 사람이 "의미를 가진 가장 작은 단위"라는 기준을 세우고 나눈 것이죠.
  • 통계 단위 (서브워드): 컴퓨터가 수많은 텍스트 데이터에서 가장 자주 함께 등장하는 글자들을 통계적으로 계산해서 하나로 묶은 것입니다.
    • 🤖 예시: 학생이_학생 +
    • 컴퓨터는 '학생'이 명사인지 모릅니다. 단지 데이터 속에서 _ 다음에 학생이라는 글자 뭉치가, 그리고 그 뒤에 라는 글자가 오는 패턴이 수없이 많이 반복되는 것을 보고 "아, 이렇게 묶는 게 가장 효율적이겠구나"라고 판단한 결과입니다.

## 2. 왜 "속도가 빠름" 인가요? 🚀

복잡한 문법 분석 과정이 없기 때문입니다.

  • 형태소 분석기 (느림): '먹었다'라는 단어를 보면, "이게 '먹다'라는 동사의 과거형이 맞나?", "사전에 있는 단어인가?" 등 복잡한 문법 규칙과 사전을 일일이 대조하며 분석합니다. 마치 형사가 증거를 하나하나 따져보는 것과 같습니다.
  • 서브워드 토크나이저 (빠름): 학습 단계에서 이미 모든 계산을 끝내고 "병합 규칙"이라는 간단한 레시피 북을 만들어 둡니다. 토큰화할 때는 이 레시피 북에 따라 기계적으로 단어를 쪼개기만 하면 됩니다. 생각할 필요 없이 레시피대로 재료를 자르기만 하는 것과 같아서 속도가 매우 빠릅니다.

## 3. 어떻게 "OOV 문제를 해결" 하나요? 💡

어떤 단어가 들어와도 절대 "모른다(<UNK>)"고 하지 않기 때문입니다. 최악의 경우, 글자 단위로라도 쪼개서 표현합니다.

**OOV(Out-of-Vocabulary)**는 "어휘집에 없는 단어", 즉 처음 보는 신조어나 오타 등을 처리하지 못하는 문제입니다.

  • 기존 토크나이저: "어쩔티비"라는 신조어를 만나면, 자신의 사전에 없기 때문에 "이 단어는 모르겠어"라는 의미로 <UNK> (Unknown) 토큰으로 처리합니다. 이렇게 되면 '어쩔티비'가 가진 모든 의미 정보가 사라집니다.
  • 서브워드 토크나이저: "어쩔티비"를 만나면, 일단 어휘집에서 가장 긴 조각으로 나눠보려고 시도합니다.
    • 1순위: '어쩔티비' 통째로 있는지 확인 → 없음
    • 2순위: '어쩔''티비'로 쪼갤 수 있는지 확인 → 어휘집에 있다면 ['어쩔', '티비']로 분해 성공!
    • 최악의 경우: 만약 '어쩔'도 없다면, '어', '쩔' 과 같이 더 작은 단위로 쪼갭니다. 정말 최악의 경우라도 어휘집에는 모든 기본 글자(ㅇ, ㅓ, ㅉ, ㅓ, ㄹ, ...)가 포함되어 있으므로, 글자 단위로라도 분해해서 어떻게든 표현해냅니다.

결론적으로 서브워드 토크나이저는 모르는 단어가 나와도 포기하지 않고, 자신이 가진 레고 블록(서브워드)들을 조합해 어떻게든 그 단어를 만들어내는 방식으로 OOV 문제를 원천적으로 해결합니다.

 

SentencePiece가 _를 사용해 공백을 없앤다는 것은 '사전 준비' 단계이고, 그 다음에 어떤 방식을 사용해서 나누는지가 진짜 핵심입니다.

결론부터 말하면, SentencePiece는 자체적으로 완전히 새로운 분리 방식을 만든 것이 아니라, 기존의 강력한 알고리즘들을 자신의 프레임워크 안에서 돌리는 **'엔진'**으로 사용합니다. 그 대표적인 엔진이 바로 BPEUnigram Language Model입니다.

어떤 엔진을 선택하느냐에 따라 나누는 방식이 달라집니다.


## 자동차에 비유해볼게요 🚗

  • SentencePiece: 자동차의 **차체(프레임워크)**입니다. "띄어쓰기 없는 도로에서도 달릴 수 있다"(_ 처리)는 특징을 가진 차체죠.
  • BPE / Unigram: 이 자동차에 장착할 수 있는 두 가지 종류의 엔진입니다. 운전자는 둘 중 하나의 엔진을 선택해야 합니다.

이제 각 엔진이 _학생이_책을_읽었다 라는 도로를 어떻게 달리는지(나누는지) 살펴보겠습니다.


## 엔진 1: BPE (Byte Pair Encoding) 방식

BPE 엔진은 "가장 많이 나오는 짝꿍부터 합치자" 라는 단순하고 강력한 원리로 작동합니다.

  1. 초기 상태: 모든 것을 글자 단위로 봅니다. _, 학, 생, 이, _, 책, 을, _, 읽, 었, 다
  2. 짝꿍 찾기: 데이터에서 가장 자주 함께 나오는 글자 쌍을 찾습니다. 예를 들어 (학, 생) 이라는 쌍이 아주 많이 나온다면, 이 둘을 합쳐 학생이라는 새로운 부품(토큰)을 만듭니다.
  3. 계속 합치기: 이 과정을 계속 반복합니다.
    • (_, 책) 이라는 쌍이 자주 나오면 _책 이라는 부품을 만듭니다.
    • (_책, 을) 이라는 쌍이 자주 나오면 _책을 이라는 부품을 만듭니다.
    • (_, 학생) 이 자주 나오면 _학생 을, (읽, 었) 이 자주 나오면 읽었 을 만듭니다.
  4. 최종 결과: 이 과정을 정해진 횟수만큼 반복하면, 최종적으로 어휘집(Lexicon)에는 _학생, 이, _책을, _읽었, 다 와 같이 통계적으로 가장 자주 뭉쳐 다녔던 덩어리들이 저장됩니다.

BPE 방식의 특징: 한번 규칙이 정해지면 언제나 똑같은 결과로 나눕니다. **결정론적(Deterministic)**입니다.


## 엔진 2: Unigram Language Model 방식

Unigram 엔진은 BPE와는 정반대로, 훨씬 더 세련된 방식으로 작동합니다. "어떻게 나눠야 가장 자연스러울까?" 를 확률적으로 계산합니다.

  1. 초기 상태: 일단 가능한 모든 서브워드로 구성된 아주 큰 어휘집으로 시작합니다.
  2. 최적의 조합 찾기: _학생이_책을_읽었다 라는 문장을 받았을 때, 이 문장을 만들 수 있는 모든 토큰 조합의 확률을 계산합니다.
    • 조합 1: _학생이, _책을, _읽었다 → 이 조합이 나타날 확률은?
    • 조합 2: _학생, 이, _책, 을, _읽, 었다 → 이 조합이 나타날 확률은?
    • 조합 3: _, 학, 생, 이, _책을, _읽었다 → 이 조합이 나타날 확률은?
  3. 확률이 가장 높은 조합 선택: 수많은 조합 중에서, 전체 문장이 될 확률(Likelihood)을 가장 높이는 조합을 최종 분리 결과로 선택합니다.
  4. 어휘집 최적화: 학습 과정에서는, 전체 확률 손실을 줄이는 방향으로 어휘집에서 불필요한 토큰을 제거해 나가며 최종 어휘집을 만듭니다. (BPE가 더하는 방식이라면, Unigram은 빼는 방식입니다.)

Unigram 방식의 특징: 같은 단어라도 문맥에 따라 여러 가지 방식으로 나뉠 수 있습니다. 예를 들어, unhappy는 un, happy로 나뉠 수도 있고 u, nhappy로 나뉠 수도 있는데, 그중 가장 확률이 높은 것을 선택합니다. **확률적(Probabilistic)**입니다.


## 최종 요약

구분 BPE 엔진 Unigram 엔진
접근 방식 🧱 Bottom-up: 작은 글자에서 시작해 더해나감 🎲 Top-down: 큰 어휘집에서 시작해 빼나가며 최적화
분리 기준 빈도수: 가장 자주 나오는 쌍 확률: 가장 자연스러운 조합
결과 항상 동일한 결과 (Deterministic) 여러 후보 중 가장 확률 높은 결과 (Probabilistic)

wordpiec랑 unigram하고는 다릅니다.

둘 다 서브워드를 만든다는 목표는 같지만, **'어떻게 최적의 서브워드 조합을 찾을 것인가'**에 대한 철학과 접근법이 완전히 다릅니다.

간단히 비유하자면, WordPiece는 최고의 **'부품(토큰)'**을 만들어나가는 방식이고, Unigram은 최고의 **'완성품(문장 분할)'**을 만들기 위해 부품을 최적화하는 방식입니다.


## WordPiece: 최고의 부품을 만드는 '레고 빌더' 🧱

WordPiece는 "어떤 두 부품을 합쳐야 가장 그럴듯한 새 부품이 될까?" 에 집중합니다.

  • 접근 방식 (Bottom-up): 가장 작은 글자 단위에서 시작해서, 합쳤을 때 언어 모델의 확률(Likelihood)을 가장 많이 높여주는 쌍을 찾아 더해나갑니다(greedy merge).
  • 핵심 질문: "수많은 글자 쌍 중에서, 지금 당장 어떤 쌍을 합치는 것이 최선인가?"
  • 결과: 이 과정이 끝나면 하나의 단어는 항상 똑같은 방식으로만 쪼개집니다. **결정론적(Deterministic)**입니다.
    • unhappy → un + ##happy (결과가 항상 고정됨)

## Unigram: 최고의 완성품을 조각하는 '조각가' 🗿

Unigram은 "이 문장을 쪼개는 여러 방법 중, 전체적으로 가장 자연스러운 방법은 무엇일까?" 에 집중합니다.

  • 접근 방식 (Top-down): 일단 아주 큰 어휘집으로 시작합니다. 그 다음, 특정 토큰을 어휘집에서 제거했을 때 전체 문장들의 확률(Likelihood) 손실이 가장 적은 토큰부터 없애나갑니다. 최종적으로 최적의 어휘집을 남깁니다.
  • 핵심 질문: "주어진 문장을 통째로 봤을 때, 가장 확률 높은 토큰 조합은 무엇인가?"
  • 결과: 하나의 단어에 대해 여러 가지 분할 후보가 나올 수 있고, 각 후보는 확률값을 가집니다. **확률적(Probabilistic)**입니다.
    • unhappy → un + happy (확률 0.9), u + nhappy (확률 0.1)
    • 이 특징은 모델 학습 시 매번 다른 방식으로 토큰화를 적용해 **데이터를 증강(augmentation)**하고 모델을 더 강건하게 만드는 데 사용될 수 있습니다.

## 최종 비교 요약

구분 WordPiece (BERT에서 사용) Unigram (SentencePiece의 한 옵션)
접근 방식 Bottom-up (더하기): 글자에서 시작해 합쳐나감 Top-down (빼기): 큰 어휘집에서 시작해 제거해나감
결정 기준 가장 확률 증가량이 큰 쌍을 병합 문장 전체의 확률을 최대화하는 분할 방법
토큰화 결과 결정론적 (항상 1가지 결과) 확률적 (여러 결과와 확률값 도출 가능)
반응형

+ Recent posts