KOSAC Lexicon 사용 매뉴얼¶
kosac-lexicon은 KOSAC(한국어 감정 분석 코퍼스)에서 도출한 형태소 단위 한국어
감정 사전을 파이썬 패키지로 제공합니다. 배포 이름은 kosac-lexicon, import
이름은 kosac입니다.
목차¶
1. 소개¶
이 사전은 어휘의 감정 특성이 그것을 포함하는 핵심 주관 표현(Seed)에서 도출된다는
가정 아래, Seed에서 추출한 형태소 N-그램을 표제어로 삼아 구축되었습니다. 데이터는
2016년에 공개되었으며 그 내용은 고정되어 있습니다(데이터 버전 2016).
하나의 텍스트를 분석하는 흐름은 다음과 같습니다.
한국어 문장 → (형태소 분석) surface/POS 토큰 → (사전 매칭) 감정 표현 추출 → 라벨 확률 분포
2. 설치¶
pip install kosac-lexicon # 핵심: 사전 데이터 + 조회 API (pandas/numpy만 필요)
pip install "kosac-lexicon[kiwi]" # + Kiwi 형태소 분석기 (순수 pip, 자바 불필요)
pip install "kosac-lexicon[transformers]" # + HuggingFace 서브워드 토크나이저
pip install "kosac-lexicon[sklearn]" # + scikit-learn 피처 추출기
pip install "kosac-lexicon[all]" # 전체
자바(JVM)는 필요 없습니다. 형태소 분석은 순수 pip로 설치되는 Kiwi(
kiwipiepy)를 사용합니다.핵심 설치만으로 사전 데이터 로드·조회·필터링이 모두 됩니다. 문장 토큰화가 필요할 때만
[kiwi]를 추가하세요.
3. 핵심 개념¶
3.1 표제어 형식¶
표제어는 형태소/품사 토큰을 공백으로 이은 N-그램입니다(세종 태그셋). 예:
유니그램:
좋/VA,힘/NNG바이그램:
행복/NNG 하/XSA
각 사전은 16,361개 표제어(유니그램 3,476 · 바이그램 6,579 · 트라이그램 6,307)를 가집니다.
3.2 여섯 가지 의미 특성¶
kosac.FEATURES로 확인할 수 있습니다.
특성(feature) |
라벨 값 |
|---|---|
|
COMP, NEG, NEUT, None, POS |
|
High, Low, Medium, None |
|
dir-action, dir-explicit, dir-speech, indirect, writing-device |
|
0, 1, 2, 3 |
|
COMP, NEG, NEUT, POS |
|
Agreement, Argument, Emotion, Intention, Judgment, Others, Speculation |
각 라벨 값의 의미 요약은 kosac.describe_feature("polarity")로 볼 수 있습니다(§10).
6개 정식 특성 외에, v0.5.0은 분류 정확도를 위해 튜닝된 파생 polarity-blend
사전(POS/NEG)을 함께 배포합니다 — 이름으로 로드되지만 kosac.FEATURES에는 일부러
넣지 않았습니다. §6.5 참고.
3.3 사전 데이터 구조¶
사전은 entry(공백으로 이은 형태소)를 인덱스로 하는 pandas DataFrame이며 열은
다음과 같습니다.
(라벨별 열): 각 라벨 값을 가지는 Seed의 개수(절대빈도) — CSV에 저장되는 유일한 데이터
freq: Seed 개수(= 행 합계). 로더가 계산max.value/max.prop: 최빈 라벨과 그 비율. 로더가 카운트에서 계산
4. 빠른 시작¶
import kosac
# (1) 사전만 조회 — 토크나이저/자바 불필요
lex = kosac.load_lexicon("polarity", ngrams=[1], min_freq=5)
lex.get_entry("힘/NNG")
# (2) 문장 분석 — Kiwi 필요: pip install "kosac-lexicon[kiwi]"
from kosac import SentimentAnalyzer
analyzer = SentimentAnalyzer("polarity")
result = analyzer.analyze("이 영화 정말 좋다")
result["features"]["polarity"]["label"] # 'POS'
5. 사전 다루기 (저수준 API)¶
5.1 로드¶
import kosac
from kosac.lexicon import PolarityLexicon
lex = kosac.load_lexicon("polarity", ngrams=[1, 2, 3], min_freq=0, threshold=0.0)
lex = PolarityLexicon.load(ngrams=[1]) # 클래스 메서드도 동일
lex = PolarityLexicon(filepath="my-lexicon.csv") # 직접 만든 CSV 사용
ngrams: 사용할 N-그램 길이 목록(기본[1]).load_lexicon의feature는kosac.FEATURES중 하나여야 합니다.
5.2 필터링·조회¶
lex.set_lexicon(min_freq=5, threshold=0.6) # freq>=5 그리고 max.prop>0.6 만 남김
lex.set_lexicon(min_freq=1) # 다시 느슨하게 — 원본 기준으로 재필터됨
lex.get_size() # 표제어 개수
lex.get_labels() # ['COMP','NEG','NEUT','None','POS']
lex.get_lexicon() # 현재(필터된) DataFrame
lex.get_original_lexicon() # 필터 이전 원본
lex.get_entry("좋/VA") # 한 표제어의 행 (레거시 별칭: lex.get("좋/VA"))
set_lexicon은 매번 원본 사전을 기준으로 필터링하므로 임계값을 더 좁히거나
다시 넓힐 수 있습니다.
5.3 매칭·점수 (토크나이저 필요)¶
from kosac.tokenizers import KiwiTokenizer
tok = KiwiTokenizer()
lex.match_patterns("나는 정말 행복하다", tok) # 매칭된 표제어 문자열 목록 (별칭: lex.match)
lex.get_match_info("나는 정말 행복하다", tok) # (표제어, max.value, max.prop) 목록
lex.get_sent_probs("나는 정말 행복하다", tok) # 라벨별 확률(softmax) Series
sorting=True(기본)는 긴 N-그램·높은 확률 표제어부터,sorting=False는 원래 순서대로 매칭합니다.
더 풍부한 결과(문자 스팬, 부정 처리 등)는 §6 통합 분석기를 사용하세요.
match_patterns/get_sent_probs는 단순 사전 룩업입니다.
5.4 커스텀 사전 만들기·확장¶
빈 사전에서 시작하거나 코퍼스로부터 빈도를 학습할 수 있습니다.
from kosac.lexicon import GenericLexicon
from kosac.corpora import Corpus
lex = GenericLexicon(ngrams=[1, 2])
lex.set_labels(["POS", "NEG"]) # GenericLexicon 전용
lex.add_token("좋/VA", "POS") # 표제어에 라벨 카운트 1 추가
lex.update([("싫/VA", "NEG"), ("좋/VA", "POS")])
corpus = Corpus("data/example.csv") # 헤더 없는 text,label CSV
lex.update_from_corpus(corpus, KiwiTokenizer())
lex.export_user_dict("user_dictionary.txt") # 유니그램을 form\tPOS 로 내보내기
lex.save("my-lexicon.csv") # 패키지 CSV 형식(ngram + 카운트)으로 저장
GenericLexicon(filepath="my-lexicon.csv", ngrams=[1]) # 다시 로드(라벨 자동 추론)
6. 통합 분석기 (고수준 API)¶
SentimentAnalyzer는 사전과 토크나이저를 묶어 한 번의 호출로 텍스트를 분석합니다.
권장 진입점입니다.
from kosac import SentimentAnalyzer
analyzer = SentimentAnalyzer(
features="polarity", # 'all', 'polarity-blend', ['polarity', ...] 도 가능
tokenizer=None, # 기본 KiwiTokenizer()
ngrams=(1, 2, 3),
min_freq=0, threshold=0.0,
smoothing=True,
scoring="multiscale", # 기본값; 'greedy'는 기존 매처 (§6.5)
ngram_weights=None, # 길이별 가중치, 예: {1:1, 2:1, 3:1}
align=False, # Kiwi 사용자사전을 사전으로 시드 (§7.3)
negation=False, # 부정 처리 (§6.3)
intensifier=False, # 강조 처리 (§6.3)
window=2, intensifier_factor=2.0,
)
v0.5.0부터 기본 스코어러는 멀티스케일입니다(§6.5): 중첩되는 모든 n-gram 매칭을
합산합니다. 유니그램만 쓸 때(ngrams=[1])는 기존 scoring="greedy"와 동일하며,
바이그램·트라이그램이 매칭될 때만 달라집니다.
6.1 analyze(text) 반환 구조¶
r = analyzer.analyze("이 영화 정말 좋다")
{
"text": "이 영화 정말 좋다",
"tokens": ["이/MM", "영화/NNG", "정말/MAG", "좋/VA", "다/EF"],
"features": {
"polarity": {
"label": "POS", # 최상위 라벨
"prob": 0.97, # 그 라벨의 확률
"probs": {"COMP": ..., "NEG": ..., "POS": 0.97, ...},
"matches": [
{"entry": "좋/VA", "span": [8, 9], "text": "좋",
"max_value": "POS", "max_prop": 0.92,
"negated": False, "weight": 1.0},
...
]
}
}
}
span은 원문 문자 오프셋[시작, 끝)이라text[span[0]:span[1]]로 다시 잘라낼 수 있습니다.결과는 JSON 직렬화 가능한 순수 dict입니다.
6.2 여러 특성 동시 분석 / 배치¶
SentimentAnalyzer("all").analyze("이 영화는 정말 좋았고 너무 행복했다")
# polarity=POS, intensity=Medium, expressive-type=dir-speech,
# nested-order=1, subjectivity-polarity=POS, subjectivity-type=Argument
analyzer.analyze_batch(["좋다", "싫다"]) # 결과 dict 목록
analyzer.analyze_frame(["좋다", "싫다"]) # tidy pandas DataFrame
# text polarity.label polarity.prob
# 0 좋다 POS ...
6.3 부정·강조 처리¶
옵트인 휴리스틱입니다. 매칭된 표현 주변 window 토큰 안에 마커가 있으면 적용합니다.
a = SentimentAnalyzer("polarity", negation=True, intensifier=True)
a.analyze("이 영화는 안 좋다")["features"]["polarity"]["label"] # 'NEG'
부정(
안/못/않/없 …): 매칭 표현의 POS↔NEG 질량을 교환. POS·NEG 라벨을 모두 가진 특성(polarity,subjectivity-polarity)에만 적용됩니다.강조(
정말/너무/매우 …): 매칭 표현의 가중치를intensifier_factor배.마커 집합은
kosac.analyzer.DEFAULT_NEGATIONS/DEFAULT_INTENSIFIERS이며, 생성자 인자negations=,intensifiers=로 교체할 수 있습니다.
from kosac.analyzer import DEFAULT_INTENSIFIERS
SentimentAnalyzer("polarity", intensifier=True,
intensifiers=DEFAULT_INTENSIFIERS | {"완전/MAG"}, window=3)
⚠️ 윈도 기반 휴리스틱이라 인접한 다른 형태소까지 부정으로 표시될 수 있습니다. 최종 라벨에는 보통 영향이 없지만 정밀한 부정 범위 판정이 필요하면
window를 조절하거나 직접 후처리하세요.
6.4 빈도 계산 방식¶
확률 대신 감정 단어의 빈도(긍정/부정 단어 수와 점유율)로 텍스트를 분류할 수
있습니다 — 내용 분석·사회과학 연구에서 흔히 쓰는 방식입니다. count()가 이를
제공하며, 매칭된 형태소를 각자의 대표 라벨(max.value)로 집계합니다.
a = SentimentAnalyzer("polarity")
c = a.count("빗물이 흐르고 내 눈물도 흐르고 잃어버린 첫사랑도 흐르네")["features"]["polarity"]
c["counts"] # {'NEG': 6, 'POS': 3, ...} — 라벨별 단어 수
c["proportions"] # 라벨별 점유율 (합 1)
c["total"] # 매칭된 단어 수
c["label"] # 가장 빈도 높은 라벨 → 'NEG'
a.count_batch(texts) # 결과 dict 목록
a.count_frame(texts) # 문서별 라벨 카운트 DataFrame (<특성>.POS, <특성>.NEG, ...)
analyze()(확률 방식)와 count()(빈도 방식)는 동일한 매칭 결과를 다르게 집계합니다.
부정·강조 옵션은 빈도 방식에는 적용되지 않습니다(단어 수 집계이므로).
6.5 고성능 극성 분류 (멀티스케일 + polarity-blend)¶
POS/NEG 분류에서는 v0.5.0의 두 가지 추가 기능이 — 특히 도메인 밖 텍스트에서 — 분석기를 크게 강화합니다.
멀티스케일 스코어링(기본값). 기존 매처는 greedy leftmost-longest 비중첩이라
트라이그램이 매칭되면 그 자리의 유니그램이 억제됐습니다. 멀티스케일은 대신
중첩되는 모든 n-gram 매칭을 합산해 분류 성능이 확실히 좋아집니다. 기존 방식은
scoring="greedy", 스케일별 가중치는 ngram_weights로 조정합니다.
SentimentAnalyzer("polarity", scoring="greedy") # 기존 매처
SentimentAnalyzer("polarity", ngram_weights={1: 1, 2: 1, 3: 1}) # 균등(기본)
count()는 항상 비중첩 매처를 씁니다(단어 집계엔 그게 맞음). 멀티스케일은
analyze() / polarity_score()에 적용됩니다.
polarity-blend 사전. frozen polarity 사전은 도메인 밖에서 거의 찍기
수준입니다. polarity-blend는 frozen KOSAC 시드에 NSMC 코퍼스 학습 사전을 섞은
파생 POS/NEG 사전으로 전이가 훨씬 잘 됩니다(NIKL 도메인 밖 균형 정확도 ≈0.53 →
0.73). gzip으로 배포되고 이름으로 로드되지만, 6개 정식 kosac.FEATURES에는 포함되지
않으므로 SentimentAnalyzer("all")은 그대로입니다.
clf = SentimentAnalyzer("polarity-blend") # 멀티스케일 기본
clf.predict_polarity("이 영화 정말 재미있다") # 'POS'
clf.polarity_score("시간 낭비 최악의 영화") # 음수 float: P(POS) − P(NEG)
clf.predict_polarity_batch(reviews) # ['POS'/'NEG', ...]
polarity_score(text)→[-1, 1]범위의 연속 점수P(POS) − P(NEG).predict_polarity(text, threshold=0.0)→ 점수가 임계값을 넘으면'POS'(불균형 데이터에서 임계값을 올리면 정밀도↔재현율 조정).
블렌드는 파생 데이터(CC BY-SA; §12)입니다. 재생성하거나 더 강한
“챔피언” 버전을 만들려면 python -m benchmarks.build_shipped_blend [--full],
전체 벤치마크 방법론은 benchmarks/README.md를 참고하세요.
7. 토크나이저¶
kosac.tokenizers 모듈.
7.1 종류¶
클래스 |
설명 |
필요 익스트라 |
|---|---|---|
|
공백 분리(기본). 미리 태깅된 입력 테스트용 |
없음 |
|
Kiwi 형태소 분석(권장). |
|
|
HuggingFace 서브워드 |
|
7.2 공통 메서드¶
tok.tokenize("나는 좋다") # ['나/NP', '는/JX', '좋/VA', '다/EF']
tok.get_tokens_str("나는 좋다") # '나/NP 는/JX 좋/VA 다/EF'
tok.tokenize_with_offsets("나는 좋다") # [('나/NP', 0, 1), ('는/JX', 1, 2), ...]
tok.get_ngrams("나는 좋다", ns=[1, 2]) # 유니그램+바이그램 문자열 목록
7.3 Kiwi 사용자사전 정렬¶
사전 표제어를 Kiwi 사용자사전에 등록하면 분절이 사전과 더 잘 맞습니다(태그셋 불일치 완화).
from kosac.tokenizers import KiwiTokenizer
tok = KiwiTokenizer(user_words=[("행복하", "VA")]) # 직접 등록
tok.add_user_words(["좋/VA", ("멋지", "VA")]) # 'form/tag' 또는 (form, tag)
lex = kosac.load_lexicon("polarity", ngrams=[1])
tok = KiwiTokenizer.from_lexicon(lex, tags={"NNG", "VV", "VA", "XR"}) # 사전 유니그램으로 시드
SentimentAnalyzer(..., align=True)는 위 from_lexicon을 자동으로 적용합니다.
8. 명령줄 인터페이스 (CLI)¶
설치하면 kosac 명령이 생깁니다(또는 python -m kosac).
kosac analyze "이 영화 정말 좋다" # 보기 좋은 JSON (확률 방식)
kosac analyze "이 영화는 안 좋다" --negation # 부정 처리
kosac analyze "좋다" --features all # 6개 특성 모두
kosac analyze "빗물이 흐르고 눈물도 흐르고" --count # 빈도 계산 방식
kosac features # 특성 목록
kosac citation # BibTeX
printf '좋다\n싫다\n' | kosac analyze --compact # 한 줄에 JSON 하나(JSONL)
analyze 옵션: --features(쉼표 구분 또는 all), --ngrams, --min-freq,
--negation, --intensifier, --align, --count(빈도 방식), --compact.
텍스트 인자를 생략하면 표준입력에서 한 줄당 한 문장을 읽습니다.
9. scikit-learn 연동¶
KosacVectorizer는 텍스트를 <특성>=<라벨> 확률 피처로 변환합니다([sklearn] 필요).
from kosac.sklearn import KosacVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
clf = make_pipeline(KosacVectorizer("all", negation=True), LogisticRegression())
clf.fit(train_texts, labels)
clf.predict(test_texts)
vec = KosacVectorizer("polarity").fit(train_texts)
vec.get_feature_names_out() # ['polarity=COMP', 'polarity=NEG', ...]
생성자 인자는 SentimentAnalyzer와 동일(features, ngrams, min_freq,
negation, intensifier, align, tokenizer)합니다.
10. 유틸리티¶
import kosac
kosac.FEATURES # 6개 특성 이름 튜플
kosac.__version__ # 패키지 버전 (예: '1.0.0')
kosac.__data_version__ # 데이터 빈티지 ('2016')
kosac.citation() # BibTeX 문자열
kosac.describe_feature("polarity") # {'feature','values','reference'}
kosac.FEATURE_VALUES["intensity"] # 라벨 값 → 설명
11. 자주 묻는 질문·주의사항¶
자바가 필요한가요? 아니요. Kiwi는 순수 pip 설치입니다.
태그셋 차이. 표제어는 세종 태그셋 기반입니다. Kiwi 태그셋도 세종 기반이라 주요 형태소 태그(NNG/VV/VA/JKS/EC …)는 일치하지만, 일부 기호·웹 태그나 분절은 원본 KOSAC 구축에 쓰인 분석기와 다를 수 있습니다.
align=True로 완화할 수 있습니다.부정 처리는 완벽하지 않습니다. §6.3의 휴리스틱 한계를 참고하세요.
와일드카드 표제어. 일부 표제어에는 정규식 특수문자
*가 포함됩니다(예:가*/JKS). 내부적으로 이스케이프 처리되어 안전하게 리터럴 매칭됩니다.빈 매칭. 매칭이 하나도 없으면
analyze의 해당 특성label은None,probs는 빈 dict입니다.
12. 인용·라이선스¶
print(kosac.citation())
코드: MIT (
LICENSE)사전 데이터 (
kosac/data/*.csv): CC BY-SA 4.0, KOSAC(서울대학교)에서 도출 (src/kosac/data/LICENSE). 재배포 시 출처 표시와 동일조건 변경허락을 지켜 주세요.polarity-blend(kosac/data/polarity-blend.csv.gz): 파생 데이터, CC BY-SA 4.0 — CC BY-SA인 KOSAC 시드와 CC0인 NSMC 코퍼스를 섞었으므로 결과물은 CC BY-SA 입니다.src/kosac/data/polarity-blend.NOTICE참고.