๋ฉ๋ชจ๊ฐ 663๊ฐ๊น์ง ๋์ด๋์ ํ์ํ ๋ด์ฉ์ ์ฐพ๋ ๋ฐ ๋๋ ์๊ฐ์ด ์ ๋ ์๊ฐ๋ณด๋ค ๊ธธ์ด์ก๋ค. ์ ๋ฌธ๊ฒ์๊ธฐ๋ฅผ ๋ถ์ด๊ธฐ๋ก ํ๊ณ SQLite FTS5๋ฅผ ๊ณจ๋๋ค. ํ์ผ ํ๋์ ๋ค์ด๊ฐ๊ณ ์์กด์ฑ์ด ์๋ค.
์ฒซ ์ง์์์ ๋งํ๋ค.
$ sqlite3 index.db "SELECT count(*) FROM t WHERE t MATCH '์ฉ๋'"
0์์ธ๋ ๋ฌธ์์ ๋ฐ์ดํฐ์คํ ์ด ์ฉ๋์ ์ฆ์คํ๋ค๊ฐ ๋ถ๋ช
ํ ๋ค์ด ์๋๋ฐ 0๊ฑด์ด๋ค. ์ฆ์ค๋ 0๊ฑด์ด๋ค. ๊ทธ๋ฐ๋ฐ ๋ฐ์ดํฐ์คํ ์ด๋ 1๊ฑด์ด ๋์จ๋ค.
์ด ๊ธ์ ๊ทธ 0๊ฑด์์ ์ถ๋ฐํด ์ธ ๋งํ ํ๊ตญ์ด ๊ฒ์๊ธฐ๊ฐ ๋๊ธฐ๊น์ง ๋ฐ์ ํจ์ ์ ์ ๋ฆฌํ ๊ฒ์ด๋ค. ๊ฒฐ๋ก ๋ถํฐ ๋งํ๋ฉด ํ ํฌ๋์ด์ ๋ฅผ ๊ณ ์น๋ ๊ฒ์ผ๋ก ๋๋์ง ์๋๋ค. ์์ธ, ์ง์, ์์ ์ตํฉ ์ธ ์ธต์์ ๊ฐ๊ฐ ๋ค๋ฅด๊ฒ ๊นจ์ง๋ค.
๊ธฐ๋ณธ ํ ํฌ๋์ด์ ๋ ๊ฐ๊ฐ ๋ชจ๋ ํ๊ตญ์ด๋ฅผ ๋ชป ์ด๋ค
SQLite 3.49.1์์ ๊ฐ์ ๋ฌธ์ ํ๋๋ฅผ ์ธ ๊ฐ์ง ๋ฐฉ์์ผ๋ก ์์ธํ๊ณ ๊ฐ์ ์ง์๋ฅผ ๋์ก๋ค.
| ์ง์ | unicode61 |
trigram |
2-gram ์ ์ฒ๋ฆฌ |
|---|---|---|---|
์ฉ๋ |
0๊ฑด | 0๊ฑด | 1๊ฑด |
์ฆ์ค |
0๊ฑด | 0๊ฑด | 1๊ฑด |
๋ฐ์ดํฐ์คํ ์ด |
1๊ฑด | 1๊ฑด | 1๊ฑด |
์ด์ ๋ ๊ฐ๊ฐ ๋ค๋ฅด๋ค.
unicode61์ ๊ณต๋ฐฑ๊ณผ ๋ฌธ์ฅ๋ถํธ๋ก๋ง ์๋ฅธ๋ค.fts5-tokenizers ๋ฌธ์์ ํ ํฐ์ ๋ฐ์ดํฐ์คํ ์ด, ์ฉ๋์, ์ฆ์คํ๋ค ์ธ ๊ฐ๋ค. ํ๊ตญ์ด๋ ์กฐ์ฌ์ ์ด๋ฏธ๊ฐ ๋ฑ๋ง์ ๋ถ์ด ๋ฒ๋ฆฌ๋ฏ๋ก ์ฉ๋์ ์ด๋ค ํ ํฐ๊ณผ๋ ๊ฐ์ง ์๋ค. ๋ฐ์ดํฐ์คํ ์ด๋ง ๊ฑธ๋ฆฐ ์ด์ ๋ ๊ทธ ๋ฑ๋ง ๋ค์ ์กฐ์ฌ๊ฐ ์์๊ธฐ ๋๋ฌธ์ด๋ค. ์ฐ์ฐ์ด๋ค.
trigram์ ์ฐ์ํ ์ธ ๊ธ์๋ฅผ ํ๋์ ํ ํฐ์ผ๋ก ๋ณธ๋ค.fts5-tokenizers ๋ถ๋ถ ๋ฌธ์์ด ๊ฒ์์ด ๋๋ ์กฐ์ฌ ๋ฌธ์ ๋ ํ๋ฆฐ๋ค. ๊ทธ๋ฐ๋ฐ ๋ ๊ธ์ ์ง์๋ ํ ํฐ์ด ์์ ๋ง๋ค์ด์ง์ง ์์ 0๊ฑด์ด๋ค. ํ๊ตญ์ด ์ค๋ฌด ๋ฑ๋ง์ ๋ ๊ธ์๊ฐ ๋๋ถ๋ถ์ด๋ค. ์ฉ๋, ์ฆ์ค, ํ์ฅ, ์ฅ์ , ์ ์, ๊ถํ, ๋ฐฑ์
, ๋ณต๊ตฌ. ๊ฐ์ฅ ์์ฃผ ์น๋ ์ง์๊ฐ ์ ๋ถ ๋งํ๋ค.
โ ๏ธ ์์ด๋ก ํ ์คํธํ๋ฉด ๋ ๋ค ๋ฉ์ฉกํด ๋ณด์ธ๋ค.
capacity๋unicode61์์ ์ ๊ฑธ๋ฆฌ๊ณtrigram์์๋ ์ธ ๊ธ์๋ฅผ ๋๋๋ค. ํ๊ตญ์ด๋ฅผ ๋ฃ์ด ๋ณด๊ธฐ ์ ๊น์ง๋ ๋ฌธ์ ๊ฐ ๋๋ฌ๋์ง ์๋๋ค.
์์ธ ์ธต: ํ๊ธ ๊ตฌ๊ฐ๋ง ๊ฒน์น๋ 2-gram์ผ๋ก ์ชผ๊ฐ ๋ค
ํด๋ฒ์ ์์ธ์ ๋ฃ๊ธฐ ์ ์ ํ๊ธ ๊ตฌ๊ฐ๋ง ๊ฒน์น๋ 2-gram์ผ๋ก ๋ฏธ๋ฆฌ ์ชผ๊ฐ๋ ๊ฒ์ด๋ค. ํ ํฌ๋์ด์ ๋ unicode61์ ๊ทธ๋๋ก ์ด๋ค.
import re
HANGUL = re.compile(r"[๊ฐ-ํฃ]+")
def ngramize(text: str) -> str:
"""ํ๊ธ ๊ตฌ๊ฐ๋ง ๊ฒน์น๋ 2-gram์ผ๋ก ์ชผ๊ฐ ๋ค. ์๋ฌธ๊ณผ ์ซ์๋ unicode61์ด ์์์ ์๋ฅธ๋ค."""
out, pos = [], 0
for m in HANGUL.finditer(text):
out.append(text[pos:m.start()])
w = m.group()
if len(w) == 1:
out.append(f" {w} ")
else:
out.append(" " + " ".join(w[i:i + 2] for i in range(len(w) - 1)) + " ")
pos = m.end()
out.append(text[pos:])
return "".join(out)"๋ฐ์ดํฐ์คํ ์ด ์ฉ๋์ ์ฆ์คํ๋ค"
-> "๋ฐ์ด ์ดํฐ ํฐ์ค ์คํ ํ ์ด ์ฉ๋ ๋์ ์ฆ์ค ์คํ ํ๋ค"๋ ๊ฐ์ง๊ฐ ์๋์ ์ด๋ค.
์๋ฌธ๊ณผ ์ซ์๋ ๊ฑด๋๋ฆฌ์ง ์๋๋ค. unicode61์ด ์ด๋ฏธ ์ ๋๋ก ์๋ฅด๊ณ ์๋ ์์ญ์ด๋ค. ์ ๋ถ 2-gram์ผ๋ก ๋ฐ์ด ๋ฃ์ผ๋ฉด rocky๊ฐ ro oc ck ky๊ฐ ๋์ด ์์ธ๋ง ์ปค์ง๊ณ ์ ํ๋๋ ๋จ์ด์ง๋ค. ์ ๊ท์์ผ๋ก ํ๊ธ ๊ตฌ๊ฐ๋ง ๊ณจ๋ผ๋ด๋ ์ด์ ๊ฐ ์ด๊ฒ์ด๋ค.
๊ฒน์ณ์ ์๋ฅธ๋ค. ์ฉ๋์์ ์ฉ๋ / ๋์๋ก ๋ง๋ค์ด์ผ ์ฉ๋์ผ๋ก ์ฐพ์ ์ ์๋ค. ๊ฒน์น์ง ์๊ฒ ์๋ฅด๋ฉด(์ฉ๋ / ์) ๊ฒฝ๊ณ๊ฐ ์ด๊ธ๋ ์ง์๊ฐ ์ ๋ถ ๋น๋๊ฐ๋ค.
์ง์์๋ ๊ฐ์ ๋ณํ์ ์ ์ฉํ๋ค. ์์ธ๊ณผ ์ง์๊ฐ ๊ฐ์ ํจ์๋ฅผ ํต๊ณผํด์ผ ํ๋ฏ๋ก ๋ณํ๊ธฐ๋ฅผ ํ ๊ณณ์ ๋๊ณ ์์ชฝ์์ ๋ถ๋ฅธ๋ค.
์ง์ ์ธต: ์กฐ๊ฐ์ AND๋ก ํ๋ฉด ๋ค๋ฅธ ๋ฌธ์๊ฐ ๊ฑธ๋ฆฐ๋ค
์ฌ๊ธฐ์ ๋ ๋ฒ์งธ ํจ์ ์ด ๋์จ๋ค. ๋ฐ์ดํฐ์คํ ์ด๋ฅผ 2-gram์ผ๋ก ์ชผ๊ฐ๋ฉด ์กฐ๊ฐ์ด ๋ค์ฏ ๊ฐ๋ค. ์ด๊ฑธ ๊ทธ๋๋ก ๋์ง๋ฉด FTS5๋ ์๋ฌต์ AND๋ก ํด์ํ๋ค.fts5-phrases
๋ฐ์ด AND ์ดํฐ AND ํฐ์ค AND ์คํ AND ํ ์ด๋ค์ฏ ์กฐ๊ฐ์ด ํ ๋ฌธ์ ์์ ๋ค ์๊ธฐ๋ง ํ๋ฉด ๊ฑธ๋ฆฐ๋ค. ์๋ก ๋ถ์ด ์์ ํ์๊ฐ ์๋ค. ์ค์ธก์ผ๋ก ํ์ธํ๋ค.
[๋ฌธ์] ๋ฐ์ดํฐ ์ผํฐ์ค ์คํ ์ด ์ด์ผ๊ธฐ
gram: ๋ฐ์ด ์ดํฐ ์ผํฐ ํฐ์ค ์คํ ํ ์ด ์ด์ผ ์ผ๊ธฐ
๋ฐ์ด AND ์ดํฐ AND ํฐ์ค AND ์คํ AND ํ ์ด -> 1๊ฑด โ ๊ฑธ๋ฆฌ๋ฉด ์ ๋๋ ๋ฌธ์
"๋ฐ์ด ์ดํฐ ํฐ์ค ์คํ ํ ์ด" -> 0๊ฑด โ
๋ฐ์ดํฐ์คํ ์ด๋ผ๋ ๋ฑ๋ง์ด ์๋ ๋ฌธ์๊ฐ ๊ฑธ๋ ธ๋ค. ์กฐ๊ฐ๋ค์ด ์ธ ๋ฑ๋ง์ ํฉ์ด์ ธ ์์์ ๋ฟ์ด๋ค.
ํ ๋ฑ๋ง์์ ๋์จ ์กฐ๊ฐ๋ค์ ํ๋์ ์ธ์ phrase๋ก ๋ฌถ์ด์ผ ํ๋ค. ํฐ๋ฐ์ดํ๋ก ๊ฐ์ธ๋ฉด FTS5๋ ๊ทธ ์์ ๊ทธ๋๋ก ์ด์ด์ ธ ์๋ ๋ฌธ์๋ง ๋๋ ค์ค๋ค.fts5-phrases
def gram_phrase(*parts: str) -> str:
"""์กฐ๊ฐ๋ค์ gram์ ์ด์ด ํ๋์ ์ธ์ phrase๋ก ๋ง๋ ๋ค."""
g = [x for p in parts for x in grams(p)]
return '"' + " ".join(g) + '"' if g else ""์ธ์ ์กฐ๊ฑด์ NEAR๋ก ํํํ๋ฉด ์ ๋๋ค
์ธ์ ์ ๋ํ๋ด๋ ค๊ณ NEAR๋ฅผ ๋ ์ฌ๋ฆฌ๊ธฐ ์ฌ์ด๋ฐ ์ด๊ฑด ํ๋ฆฐ ๋๊ตฌ๋ค. NEAR๋ ๊ฑฐ๋ฆฌ๋ง ๋ณด๊ณ ์์๋ฅผ ๋ฐ์ง์ง ์๋๋ค.fts5-near
[๋ฌธ์] ์คํ ์ด๋ฐ์ดํฐ ์ ๋ฆฌ
NEAR("์คํ ํ ์ด" "๋ฐ์ด ์ดํฐ", 3) -> 1๊ฑด โ
"๋ฐ์ด ์ดํฐ ํฐ์ค ์คํ ํ ์ด" -> 0๊ฑด โ
์คํ ์ด๋ฐ์ดํฐ๊ฐ ๋ฐ์ดํฐ์คํ ์ด ์ง์์ ๊ฑธ๋ ธ๋ค. ์กฐ๊ฐ์ ์ด์ด ๋ถ์ธ phrase๋ ์์๊ฐ ๋ณด์กด๋๋ฏ๋ก ์ด ์คํ์ด ์์ฒ์ ์ผ๋ก ์๊ธฐ์ง ์๋๋ค.
2-gram์ ๋์ด์ฐ๊ธฐ์ ํ์ชฝ์ผ๋ก๋ง ๋ซ๋ ค ์๋ค
์ธ ๋ฒ์งธ ํจ์ ์ ํ๊ตญ์ด ํน์ ์ ๊ฒ์ด๋ค. ๊ฐ์ ๋ฑ๋ง์ ์ด๋ค ์ฌ๋์ ๋ถ์ฌ ์ฐ๊ณ ์ด๋ค ์ฌ๋์ ๋์ด ์ด๋ค.
[๋ฌธ์] ๋ฐฑ์
์๋ฒ ์ด์คํ
gram: ๋ฐฑ์
์๋ฒ ์ด์ค ์คํ
"๋ฐฑ์
์
์ ์๋ฒ" -> 0๊ฑด โ ๋ถ์ฌ ์ด ์ง์ "๋ฐฑ์
์๋ฒ"
"๋ฐฑ์
์๋ฒ" -> 1๊ฑด โ ๋ ์กฐ๊ฐ์ผ๋ก ๊ฐ๋ฅธ ํ๊ธฐ๋ฐฑ์
์๋ฒ์ ์กฐ๊ฐ์ ๋ฐฑ์
/์
์/์๋ฒ์ธ๋ฐ, ๋ฌธ์์ ๋ฐฑ์
์๋ฒ๋ผ๊ณ ์ ํ ์์ผ๋ฉด ๊ฐ์ด๋ฐ ์
์๊ฐ ์กด์ฌํ์ง ์๋๋ค. ๊ณต๋ฐฑ์ด ๋ ๊ธ์์ ์ฐ๊ฒฐ์ ๋๊ธฐ ๋๋ฌธ์ด๋ค.
๋ซ๋ ค ์๋ ๋ฐฉํฅ์ ํ์ชฝ๋ฟ์ด๋ค. ๋ฐ๋ ๋ฐฉํฅ(๋์ด ์ด ์ง์์์ ๋ถ์ฌ ์ด ๋ฌธ์๋ฅผ ์ฐพ๋ ๊ฒฝ์ฐ)์ ๋ฐฑ์
๊ณผ ์๋ฒ๊ฐ ๊ฐ๊ฐ ๋ฌธ์์ ์กฐ๊ฐ์ ๋ค์ด ์์ผ๋ฏ๋ก ๋ฑ๋ง AND๋ก ์ด๋ฏธ ๊ฑธ๋ฆฐ๋ค. ๊ทธ๋์ ๋ถ์ฌ ์ด ์ง์๋ง ๋ณด์ํ๋ฉด ๋๋ค. ๋ฑ๋ง์ ๋ ์กฐ๊ฐ์ผ๋ก ๊ฐ๋ฅธ ํ๊ธฐ๋ phrase๋ก ๋ง๋ค์ด OR๋ก ํจ๊ป ๋์ง๋ค.
๋ฐฑ์
์๋ฒ -> "๋ฐฑ์
์
์ ์๋ฒ" OR "๋ฐฑ์
์๋ฒ"๊ฐ๋ฅด๋ ์์น๋ ์ ๋ถ ์๋ํ๋ ์ํ์ ๋๋ค. ์ฌ๋ ๊ธ์๋ฅผ ๋๋ ๋ฑ๋ง์ ์กฐํฉ๋ง ๋๊ณ ์ค์ ๋ก ๊ฑธ๋ฆฌ๋ ๊ฒ์ด ์๋ค.
๋ฑ๋ง ์ ๋ถ AND๋ ํ๋ฆฐ ๊ธฐ๋ณธ๊ฐ์ด๋ค
์ฌ๊ธฐ๊น์ง ์ค๋ฉด ๋ฑ๋ง ํ๋๋ ์ ํํ ๊ฑธ๋ฆฐ๋ค. ๊ทธ๋ฐ๋ฐ ์ฌ๋์ ๋ฑ๋ง ํ๋๋ฅผ ์น์ง ์๋๋ค.
$ vs "vmware ์ฉ๋ ์ฆ์ค"
0๊ฑด์ธ ๋ฑ๋ง ๋ชจ๋ ๋ฉ๋ชจ ์ด๋๊ฐ์ ์๋ค. ํ ์น์ ์์ ์ ์ด ๊ฐ์ด ์๋ ๊ณณ์ด ์์์ ๋ฟ์ด๋ค. ์ฌ๋์ ์์ฐ์ค๋ฌ์ด ์ด๊ตฌ๋ฅผ ๋์ง๋๋ฐ AND๋ ํ๋๋ง ๋น๋๊ฐ๋ฉด 0๊ฑด์ ์ค๋ค.
๊ทธ๋ ๋ค๊ณ OR์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ๋๋ฉด ํํ ๋ฑ๋ง ํ๋์ ๊ฑธ๋ฆฐ ์ก์์ด ์์๋ฅผ ๋จน๋๋ค. ๋ ๋จ๊ณ๋ก ๊ฐ๋ค.
rows = fetch(" AND ".join(phrases), limit * 25) # ์ ๋ถ ๊ฑธ๋ฆฐ ๊ฒ ๋จผ์
if len(rows) < limit:
rows += fetch(" OR ".join(phrases), 4000) # ๋ชจ์๋ผ๋ฉด ๋ํ๋ค๋ํ ๋ค์๋ ์ค์ ์ ์ค ๋ฑ๋ง ์๋ก ์ค์ ์ธ์ด๋ค. ์ฌ๊ธฐ์ ํ ๋ฒ ๋ ํจ์ ์ด ์๋ค.
์ ์ค ์๋ฅผ ์ ๋ ฌ ํค๋ก ๋ฐ๋ก ์ฐ๋ฉด ๋ค๋ฅธ ๊ฐ์ค์น๊ฐ ์์ ์ ์ฉ๋์ง ์๋๋ค.
sort(key=(์ ์ค์, ์ ์))๋ก ์ฐ๋ฉด ์ ์ค ์๊ฐ ๊ฐ์ ๊ฒ๋ผ๋ฆฌ๋ง ์ ์ ๋น๊ต๊ฐ ์ผ์ด๋๋ค. ์ ์ค ์๊ฐ ํ๋๋ผ๋ ๋์ผ๋ฉด ๋ค๋ฅธ ๋ชจ๋ ๋ณด์ ์ ์ด๊ธด๋ค. ์ ์ค ์๋ ์ ๋ ฌ ํค๊ฐ ์๋๋ผ ์ ์์ ์ ์ด ๋ฃ์ด์ผ ํ๋ค.
score -= 4.0 * coverage # bm25๋ ์์์๋ก ์์๋ผ, ๋นผ๋ ๊ฒ์ด ๊ฐ์ฐ์ ์ด๋คbm25()๋ ๋ฎ์์๋ก ์ข์ ์ ์๋ค.fts5-bm25 ๋ถํธ๋ฅผ ๋ฐ๋๋ก ์ก์ผ๋ฉด ๊ฐ์ฅ ๊ด๋ จ ์๋ ๊ฒฐ๊ณผ๊ฐ 1๋ฑ์ผ๋ก ์ฌ๋ผ์ค๋๋ฐ, ๊ฒฐ๊ณผ๊ฐ ๊ทธ๋ด๋ฏํด์ ํ์ฐธ ๋ค์์ผ ์์์ฐจ๋ฆฐ๋ค.
์๋ฏธ ๊ฒ์์ ์ฌ์ ๋ ฌ๋ก ๋ง๋ค๋ฉด ๋ชป ์ฐพ๋ ๋ฌธ์๊ฐ ์๊ธด๋ค
ํค์๋ ๊ฒ์์ด ์์ฑ๋ผ๋ ๋จ๋ ๊ตฌ๋ฉ์ด ์๋ค.
"๋์คํฌ ๋๋ฆฌ๊ธฐ" ๋ก ๊ฒ์ -> ใ๋์คํฌ ์ฉ๋ ์ฆ์คใ ์น์
์ด ์ ๋์จ๋ค๋๋ฆฌ๊ธฐ์ ์ฆ์ค์ ํ ๊ธ์๋ ๊ฒน์น์ง ์๋๋ค. ์๋ฒ ๋ฉ ๋ชจ๋ธ์ ๋ถ์ผ ์ฐจ๋ก๋ค.e5
์ฌ๊ธฐ์ ๋๋ถ๋ถ์ด ํํ๋ ๊ตฌ์กฐ๊ฐ ์ฌ์ ๋ ฌ(reranking) ์ด๋ค. ํค์๋๋ก ํ๋ณด 200๊ฐ๋ฅผ ๋ฝ๊ณ ๊ทธ ์์์ ์ฝ์ฌ์ธ ์ ์ฌ๋๋ก ์์๋ฅผ ๋ฐ๊พผ๋ค. ๊ตฌํ์ด ๊ฐ๋จํ๊ณ ๋น ๋ฅด๋ค.
๊ทธ๋ฆฌ๊ณ ์ ๋ฌธ์ ๋ฅผ ์ ํ ํ์ง ๋ชปํ๋ค. ใ๋์คํฌ ์ฉ๋ ์ฆ์คใ ์น์ ์ ์ ์ด์ ํค์๋ ํ๋ณด 200๊ฐ์ ๋ค์ด์ค์ง ์๋๋ค. ํ๋ณด์ ์๋ ๊ฒ์ ์ฌ์ ๋ ฌํด๋ ๋์ค์ง ์๋๋ค.
์ฌ์ ๋ ฌ์ ๊ฒ์์ด ์๋๋ผ ์ ๋ ฌ์ด๋ค. ๊ฒ์ ๋ฒ์๋ฅผ ๋ํ๋ ์ผ์ ํ๋ณด๋ฅผ ๋ง๋๋ ๋จ๊ณ์์๋ง ํ ์ ์๋ค.
ํ์ํ ๊ฒ์ ํฉ์งํฉ์ด๋ค. ํค์๋ ์์ N๊ณผ ๋ฒกํฐ ์ ๋ ๋น๊ต ์์ M์ ๊ฐ๊ฐ ๋ง๋ค์ด ํฉ์น๋ค. 26,000ํ์ 384์ฐจ์์ด๋ฉด ํ๋ ฌ๊ณฑ์ด ๋ฐ๋ฆฌ์ด ๋จ์๋ผ ์ ๋ ๋น๊ต๋ฅผ ํผํ ์ด์ ๊ฐ ์๋ค.
sims = M @ qv # ๋ ๋ค ์ ๊ทํ๋ผ ์์ผ๋ฏ๋ก ๋ด์ ์ด ๊ณง ์ฝ์ฌ์ธbm25 ์ ์์ ์ฝ์ฌ์ธ์ ๋๊ธ์ด ๋ฌ๋ผ ์ง์ ๋ํ ์ ์๋ค. ์์๋ง ์ฐ๋ RRF(Reciprocal Rank Fusion)๋ก ํฉ์น๋ค.rrf
rrf = 1.0 / (60 + kw_rank) + alpha / (60 + sem_rank)RRF๋ง์ผ๋ก ์ค ์ธ์ฐ๋ฉด ์ ํํ ์ผ์นํ ๊ฒฐ๊ณผ๊ฐ ๋ฐ๋ ค๋๋ค
ํฉ์งํฉ๊น์ง ๋ง๋ค์ด ๋๊ณ ๋ ์ตํฉ์์ ๋ค์ ์ง๋ค.
๋ฐฑ์
์๋ฒ๋ก ๊ฒ์ํ๋๋ ์ ๋ชฉ์ ๊ทธ ๋ฑ๋ง์ด ๊ทธ๋๋ก ๋ฐํ ๋ฌธ์๊ฐ 10์ ๋ฐ์ผ๋ก ๋๊ฐ๋ค. ํค์๋ ๋จ๋
์ผ๋ก๋ 1์์ 2์์๋ ๋ฌธ์๋ค. ์ฌ์ฉ์ ๋์๋ "๋ฐฉ๊ธ ๋ง๋ ํ์ผ์ด ํ์ฐธ ์๋ ์๋ค"๋ก ๋ณด์ธ๋ค.
RRF๋ ์์๋ง ๋ณด๊ธฐ ๋๋ฌธ์ด๋ค. ์ฐ์๋ฅผ ํด ๋ณด๋ฉด ๋ฐ๋ก ๋๋ฌ๋๋ค.
| ๋ฌธ์ | ํค์๋ ์์ | ์๋ฏธ ์์ | RRF ์ ์ |
|---|---|---|---|
| ์ ๋ชฉ์ด ์ ํํ ์ผ์น | 1์ | ํ๋ณด์ ์์ | 1/60 = 0.0167 |
| ๊ทธ๋ด๋ฏํ๊ฒ ๊ด๋ จ๋จ | 21์ | 4์ | 1/80 + 1/63 = 0.0284 |
๋ ์ธต ๋ชจ๋์์ ์ด์ค๊ฐํ ๋ฌธ์๊ฐ, ํ ์ธต์์ 1๋ฑ์ธ ๋ฌธ์๋ฅผ ์ด๊ธด๋ค. RRF์ ์ค๊ณ๊ฐ ์๋ ๊ทธ๋ ๋ค. ์ฌ๋ฌ ๋ญ์ปค์ ํฉ์๋ฅผ ์ค์ํ๋ ์๊ณ ๋ฆฌ์ฆ์ด๋ฏ๋ก ํ์ชฝ์๋ง ๋ํ๋ ๊ฒฐ๊ณผ๋ ๊ตฌ์กฐ์ ์ผ๋ก ๋ถ๋ฆฌํ๋ค.
์๋ฏธ ์ชฝ ๋น์ค(alpha)์ ๋ฎ์ถ๋ ๊ฒ์ผ๋ก๋ ์ ํ๋ฆฐ๋ค. ์ ์์น๋ก ๊ณ์ฐํด ๋ณด๋ฉด 0.3 ์๋๊น์ง ๋ด๋ ค์ผ ๊ฒจ์ฐ ๋๋ฅ ์ธ๋ฐ, ๊ฑฐ๊ธฐ๊น์ง ๋ด๋ฆฌ๋ฉด ์๋ฏธ ์ธต์ ์ผ ์๋ฏธ๊ฐ ์์ด์ง๋ค.
ํด๋ฒ์ ์ตํฉ ์ ์ ๋ฌถ์์ ๊ฐ๋ฅด๋ ๊ฒ์ด๋ค.
# ์ง์ ๋ฑ๋ง์ด ์ ๋ถ ๊ฑธ๋ฆฐ ๊ฒ๊ณผ ์๋ ๊ฒ์ ๋จผ์ ๊ฐ๋ฅธ๋ค
r["exact"] = 0 if r["cov"] >= len(terms) else 1
out = sorted(merged.values(), key=lambda x: (x["exact"], -x["rrf"]))๋ฑ๋ง์ด ์ ๋ถ ๊ฑธ๋ฆฐ ๊ฒ์ ์ ๋ฌถ์์ ๋๊ณ ๊ทธ ์์์๋ง RRF๋ฅผ ์ด๋ค. ๋ฑ๋ง์ด ํ๋๋ ์ ๊ฒน์น๋ ์์ ์๋ฏธ ๊ฒฐ๊ณผ๋ ๋ท ๋ฌถ์์ ๊ทธ๋๋ก ์ด์ ์๋ค. ์ ํํ ๋ง์ ๊ฒ์ ์์ ๋๋ฉด์ ์๋ฏธ ์ธต์ ๋ฐ๊ฒฌ ๋ฅ๋ ฅ์ ์์ง ์๋๋ค.
๋ฒกํฐ๋ ์ธ์ ๋ ๋ต์ ์ค๋ค
์๋ฏธ ๊ฒ์์๋ "๊ฒฐ๊ณผ ์์"์ด ์๋ค. ๋ฑ๋ง์ด ํ๋๋ ์ ๊ฒน์ณ๋ ํญ์ ์์ K๊ฐ๋ฅผ ๋๋ ค์ค๋ค. ์กด์ฌํ์ง์๋๋ฑ๋ง ๊ฐ์ ์ง์์๋ ๋ฌด์ธ๊ฐ ๋์จ๋ค.
์ ์ฌ๋ ๋ฐ๋ฅ์ ์ด ํ์ํ๋ค. ์ค์ธกํด ๋ณด๋ฉด ๋ค๊ตญ์ด e5 ๊ณ์ด์ ์ฝ์ฌ์ธ์ด 0.85~0.89 ์ฌ์ด์ ๋ชฐ๋ ค ์๋ค. ๊ฐ์ ์ ๋ ํฌ๊ธฐ๊ฐ ์๋๋ผ ๋ถํฌ๋ฅผ ๋ณด๊ณ ์ ํด์ผ ํ๋ค. 0.5 ๊ฐ์ ์์์ ์ธ ๊ฐ์ ๋ฃ์ผ๋ฉด ์๋ฌด๊ฒ๋ ๊ฑธ๋ฌ์ง์ง ์๋๋ค.
๊ฐ์ค์น๋ ์์ชฝ ์์์ ๊ฐ์ด ๊ฑธ์ด์ผ ํ๋ค
๊ฐ์ ์ฌ์ค์ด ๋ ์ง๋ณ ์ผ์ง์ ์ ๋ฆฌ ๋ ธํธ ์์ชฝ์ ์ ํ ์์ผ๋ฉด ์ ๋ฆฌ๋ณธ์ ๋จผ์ ๋ณด์ฌ์ฃผ๊ณ ์ถ๋ค. ์ต๊ทผ์ ์ ์ ๊ฒ๋ ๋จผ์ ๋ณด์ฌ์ฃผ๊ณ ์ถ๋ค. ๋ ๋ค ํค์๋ ์ ์์ ๊ฐ์ฐ์ ์ผ๋ก ๋ฃ์๋ค.
ํ์ด๋ธ๋ฆฌ๋๋ฅผ ์ผ๋ ์๊ฐ ํจ๊ณผ๊ฐ ์ฌ๋ผ์ก๋ค. ์๋ฏธ ์ชฝ ์์์๋ ๋ณด์ ์ด ์์ผ๋, ์ตํฉํ ๋ ๋ณด์ ์๋ ์์๊ฐ ์ ๋ฐ์ ์ฐจ์งํ๊ณ ๊ทธ๋งํผ ํฌ์๋๋ค.
# ์๋ฏธ ์ชฝ ์์์๋ ๊ฐ์ ๋ณด์ ์ ๊ฑด๋ค. ์ ์ฌ๋๊ฐ 0.85~0.89์ ๋ชฐ๋ ค ์์ด
# 0.01 ๋จ์ ๋ณด์ ์ผ๋ก ์ถฉ๋ถํ ์์ง์ธ๋ค.
rank = sim - 0.01 * (dir_weight + recency_bonus)์ผ๋ฐํํ๋ฉด ์ด๋ ๋ค. ์ตํฉํ๋ ๋ญ์ปค๊ฐ ๋์ด๋ฉด ์ ์ฑ ์ ์์ชฝ์ ๋ค ๊ฑธ์ด์ผ ํ๋ค. ํ์ชฝ์๋ง ๊ฑด ์ ์ฑ ์ ์ตํฉ ๋น์ค๋งํผ ๋ฌฝ์ด์ง๋ค. ์ต์ ๊ฐ์ฐ์ ์์ ํ ๋ฒ ๊ฒช๊ณ , ๋๋ ํ ๋ฆฌ ๊ฐ์ค์น์์ ๋๊ฐ์ด ๋ ๊ฒช์๋ค.
์ต์ ๊ฐ์ฐ์ ์ ๊ณ๋จ์ด ์๋๋ผ ์ง์ ๊ฐ์ ๋ก ๋ฃ๋๋ค. ๋ ์ง ๊ตฌ๊ฐ์ ๊ณ๋จ์ผ๋ก ๋๋๋ฉด ๊ฒฝ๊ณ ํ๋ฃจ ์ฐจ์ด๋ก ์์๊ฐ ํญ ํ๋ค. ์ค๋ ๋ฌธ์์ ์ต๋ ๋ณด์ ์ ์ฃผ๊ณ ๋ฐ๊ฐ๊ธฐ 60์ผ๋ก ์ค์๋ค.
๋ง์ง๋ง ํ ์นธ: ํ๊ธ ์ ๋ ฅ๊ธฐ์์ Enter๋ "์ด๊ธฐ"๊ฐ ์๋๋ค
์น UI๋ฅผ ๋ถ์ด๊ณ ๊ฒ์์ฐฝ์ ๋ฐฑ์
์๋ฒ๋ฅผ ์ณค๋๋, ๋ฑ๋ง์ ๋ค ์น๊ธฐ๋ ์ ์ ํ์ผ์ด ์ด๋ ค ๋ฒ๋ ธ๋ค.
ํ๊ธ ์
๋ ฅ๊ธฐ๋ Enter๋ก ์กฐํฉ ์ค์ธ ๊ธ์๋ฅผ ํ์ ํ๋ค. ์๋ฒ๋ฅผ ์์ฑํ๋ ค๊ณ ๋๋ฅธ Enter๊ฐ ๊ทธ๋๋ก "๊ฒฐ๊ณผ ์ด๊ธฐ"๋ก ์ ๋ฌ๋ ๊ฒ์ด๋ค. ์๋ฌธ์ผ๋ก ํ
์คํธํ๋ฉด ์ ๋ ์ฌํ๋์ง ์๋๋ค.
if (e.isComposing || e.keyCode === 229) return;isComposing์ ์
๋ ฅ๊ธฐ๊ฐ ๊ธ์๋ฅผ ์กฐํฉํ๋ ๋์ ์ฐธ์ด๋ค.mdn-iscomposing ๊ตฌํ ๋ธ๋ผ์ฐ์ ๋์์ผ๋ก keyCode === 229๋ฅผ ๊ฐ์ด ๋ณธ๋ค.
๊ทธ๋ฆฌ๊ณ ๊ฒ์์ฐฝ์์ Enter์ ๊ธฐ๋ณธ ๋ป์ "์ง๊ธ ์ฐพ๊ธฐ" ๋ก ๋๋ ํธ์ด ์์ ํ๋ค. ์ฌ๋ ๊ฒ์ Ctrl+Enter, ๋๋ ํ์ดํ๋ก ๊ฒฐ๊ณผ์ ๋ด๋ ค๊ฐ ๋ค์ Enter๋ค. ํ ๋ฒ์ ์ค์กฐ์์ด ํ์ผ์ ์ฌ๋ ๊ฒ์ด๋ฉด ๋๋๋ฆด ์ ์์ง๋ง, ์ญ์ ๋ ์ ์ก์ด๋ฉด ๊ทธ๋ ์ง ์๋ค.
์ค์ธก
๊ฐ์ธ ๋ฉ๋ชจ 663๊ฐ ํ์ผ์ ์ ๋ชฉ(heading) ๋จ์ 26,777๊ฐ ์น์ ์ผ๋ก ์ชผ๊ฐ ์์ธํ ๊ฒฐ๊ณผ๋ค.
| ํญ๋ชฉ | ๊ฐ |
|---|---|
| ์์ธ | 26,777 ์น์ / DB 88.6MB / ์ ์ฒด 15.3์ด / ์ฆ๋ถ 0.22์ด |
| ์๋ฒ ๋ฉ | 26,076๊ฑด(100%), 384์ฐจ์ fp16, CPU |
| ์๋ต | ํค์๋๋ง 53 |
| ํ ์คํธ | 88๊ฐ |
์๋ฏธ ์ธต์ด ์ค์ ๋ก ๊ฑด์ง๋ ๊ฒ๋ค์ด๋ค. ๋์คํฌ ๋๋ฆฌ๊ธฐ๋ก ใ๋์คํฌ ์ฉ๋ ์ฆ์คใ, ์ ์์ด ์ ๋จ์ผ๋ก ใpsql ์ ๊ทผ ์๋๋๋ฐใ, ๋ก๊ทธ์ธ ์คํจ ์์ธ์ผ๋ก ใAuthentication test failedใ. ์ธ ์ง์ ๋ชจ๋ ๋ฑ๋ง์ด ํ๋๋ ๊ฒน์น์ง ์๋๋ค.
์๋ฒ ๋ฉ ์ ์ฌ์จ์ด ์ ๋ฐ ๋ฏธ๋ง์ด๋ฉด ์๋ฏธ ์ธต์ ์๋์ผ๋ก ๋๊ณ ํค์๋ ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋๋ก ๋ธ๋ค. ์์ธ์ ๋ค์ ๋ง๋๋ ๋์์๋ ๊ฒ์์ ๊ณ์๋ผ์ผ ํ๋ค.
์ ๋ฆฌ
- ๊ธฐ๋ณธ ํ ํฌ๋์ด์ ๋ ๊ฐ๊ฐ ํ๊ตญ์ด์์ ์๋ก ๋ค๋ฅธ ์ด์ ๋ก ์คํจํ๋ค.
unicode61์ ์กฐ์ฌ์,trigram์ ๋ ๊ธ์ ์ง์์ ๋งํ๋ค. ์์ด ํ ์คํธ๋ก๋ ๋ ๋ค ์ ๋ณด์ธ๋ค - 2-gram์ ์์ธ๋ง์ด ์๋๋ผ ์ง์์๋ ๊ฐ์ด ์ ์ฉํด์ผ ํ๊ณ , ํ ๋ฑ๋ง์ ์กฐ๊ฐ์ phrase๋ก ๋ฌถ์ด์ผ ํ๋ค. AND๋ก ํ๋ฉด ์กฐ๊ฐ์ด ํฉ์ด์ง ๋ฌธ์๊ฐ ๊ฑธ๋ฆฐ๋ค
- ์ธ์ ์กฐ๊ฑด์
NEAR๋ก ํํํ๋ฉด ์ ๋๋ค. ์์๋ฅผ ์ ๋ณด๋ฏ๋ก์คํ ์ด๋ฐ์ดํฐ๊ฐ๋ฐ์ดํฐ์คํ ์ด์ ๊ฑธ๋ฆฐ๋ค - 2-gram์ ๋ถ์ฌ ์ด ์ง์์์ ๋์ด ์ด ๋ฌธ์๋ฅผ ๋ชป ์ก๋๋ค. ๊ฐ๋ฅธ ํ๊ธฐ๋ฅผ OR๋ก ํจ๊ป ๋์ ธ ํ์ชฝ ๋ฐฉํฅ๋ง ๋ณด์ํ๋ค
- ์ฌ์ ๋ ฌ์ ๊ฒ์์ด ์๋๋ค. ํ๋ณด๋ฅผ ๋ง๋๋ ๋จ๊ณ์์ ํฉ์งํฉ์ ๋ง๋ค์ง ์์ผ๋ฉด ๋ฑ๋ง์ด ์ ๊ฒน์น๋ ๋ฌธ์๋ ์์ ๋ชป ์ฐพ๋๋ค
- RRF๋ง์ผ๋ก ์ค ์ธ์ฐ๋ฉด ํ ์ธต์์ 1๋ฑ์ธ ๋ฌธ์๊ฐ ๋ ์ธต์์ ์ด์ค๊ฐํ ๋ฌธ์์ ์ง๋ค. ์ ํํ ์ผ์นํ ๋ฌถ์์ ๋จผ์ ๊ฐ๋ฅด๊ณ ๊ทธ ์์์ ์ตํฉํ๋ค
- ๋ญ์ปค๊ฐ ๋์ด๋ฉด ์ ์ฑ ์ ์์ชฝ์ ๋ค ๊ฑธ์ด์ผ ํ๋ค. ํ์ชฝ์๋ง ๊ฑด ๋ณด์ ์ ์ตํฉ ๋น์ค๋งํผ ๋ฌฝ์ด์ง๋ค
๊ฐ์ฅ ์ค์ํ ํ๋๋ฅผ ๊ณ ๋ฅด๋ผ๋ฉด ๋ค์ฏ ๋ฒ์งธ๋ค. ๋๋จธ์ง๋ ๊ณ ์น๋ฉด ๋๋ ๋ฒ๊ทธ์ง๋ง, ์ฌ์ ๋ ฌ๋ก ๋ง๋ค์ด ๋ ๊ตฌ์กฐ๋ ๋ชป ์ฐพ๋ ๋ฌธ์๊ฐ ์๋ค๋ ์ฌ์ค ์์ฒด๊ฐ ๋๋ฌ๋์ง ์๋๋ค. ๊ฒฐ๊ณผ๋ ์ธ์ ๋ ๊ทธ๋ด๋ฏํ๊ฒ ๋์ค๊ณ , ์๋ ๊ฒ์ ์กฐ์ฉํ ์๋ค.
- ํ๊ตญ์ด ๋ฌธ์๋ฅผ ์์ธํ๊ธฐ ์ ์ ๋ ๊ธ์ ๋ฑ๋ง๋ก ๋จผ์ ๊ฒ์ํด ๋ณธ๋ค. 0๊ฑด์ด๋ฉด ํ ํฌ๋์ด์ ๋ฌธ์ ๋ค
- 2-gram ๋ณํ๊ธฐ๋ฅผ ํ ๊ณณ์ ๋๊ณ ์์ธ๊ณผ ์ง์๊ฐ ๊ฐ์ ํจ์๋ฅผ ๋ถ๋ฅด๊ฒ ํ๋ค
- ์๋ฌธ๊ณผ ์ซ์๋ 2-gram ๋์์์ ๋บ๋ค. ์์ธ๋ง ์ปค์ง๊ณ ์ ํ๋๋ ๋จ์ด์ง๋ค
- ํ ๋ฑ๋ง์์ ๋์จ ์กฐ๊ฐ์ ํฐ๋ฐ์ดํ๋ก ๋ฌถ์ด ์ธ์ phrase๋ก ๋์ง๋ค
- ์ธ์ ์กฐ๊ฑด์
NEAR๋ฅผ ์ฐ์ง ์๋๋ค. ์์๋ฅผ ๋ณด์ง ์๋๋ค - ๋ถ์ฌ ์ด ์ง์๋ฅผ ๋ ์กฐ๊ฐ์ผ๋ก ๊ฐ๋ฅธ ํ๊ธฐ๋ OR๋ก ํจ๊ป ๋์ง๋ค
- ๋ฑ๋ง ์ ๋ถ AND๋ฅผ ์ ์ผํ ๊ฒฝ๋ก๋ก ๋์ง ์๋๋ค. 0๊ฑด์ด๋ฉด OR๋ก ๋ํ๊ณ ์ ์ค ์๋ก ์ค ์ธ์ด๋ค
- ์ ์ค ์๋ฅผ ์ ๋ ฌ ํค๋ก ๋ฐ๋ก ์ฐ์ง ๋ง๊ณ ์ ์์ ์ ์ด ๋ฃ๋๋ค
-
bm25()๋ ๋ฎ์์๋ก ์ข์ ์ ์๋ค. ๋ถํธ๋ฅผ ํ์ธํ๋ค - ์๋ฏธ ๊ฒ์์ ์ฌ์ ๋ ฌ์ด ์๋๋ผ ํฉ์งํฉ์ผ๋ก ๋ถ์ธ๋ค
- RRF ์ ์ "์ง์ ๋ฑ๋ง์ด ์ ๋ถ ๊ฑธ๋ฆฐ ๊ฒ"์ ์ ๋ฌถ์์ผ๋ก ๊ฐ๋ฅธ๋ค
- ๋๋ ํ ๋ฆฌ ๊ฐ์ค์น์ ์ต์ ๊ฐ์ฐ์ ์ ์๋ฏธ ์ชฝ ์์์๋ ๋๊ฐ์ด ๊ฑด๋ค
- ๋ฒกํฐ ๊ฒฐ๊ณผ์ ์ ์ฌ๋ ๋ฐ๋ฅ์ ์ ๋๋ค. ๊ฐ์ ์ค์ ๋ถํฌ๋ฅผ ๋ณด๊ณ ์ ํ๋ค
- ํ๊ธ ์
๋ ฅ๊ธฐ ๋์์ผ๋ก
isComposing์ ํ์ธํ๋ค. ๊ฒ์์ฐฝ์ Enter๋ "์ง๊ธ ์ฐพ๊ธฐ"๋ก ๋๋ค
๋ค์ ๊ธ์์๋ ์๋ฒ ๋ฉ ๋ชจ๋ธ์ CPU์์ ์ด์ํ ๋์ ์ ํ ๊ธฐ์ค์ ๋ค๋ฃฌ๋ค. ํฐ ๋ชจ๋ธ์ด ํญ์ ์ข์ ๊ฒ์ ์๋๋ผ๋ ๊ฒ, int8 ์์ํ๊ฐ CPU์์ ๊ธฐ๋๋งํผ ์ ๋นจ๋ผ์ง๋ ๊ฒ, ๋ฒกํฐ๋ฅผ ๋ฌด์์ผ๋ก ํค์ํด์ผ ์ฌ์์ธ ๋น์ฉ์ด ์ฌ๋ผ์ง๋์ง๋ฅผ ์ค์ธก์ผ๋ก ์ ๋ฆฌํ๋ค.
์ฐธ๊ณ ๋ฌธํ
์ด์ด์ ์ฝ๊ธฐ
- ๋ณด์ ๊ถ๊ณ ๋ฌธ ๋ชจ๋ํฐ๋ง ์๋ํ: ์์กด์ฑ 0์ผ๋ก ๋ง๋๋ RSS ์์ง๊ธฐ
- SQL ๊ธฐ์ด ์์ ์ ๋ฆฌ 1ํธ: SELECT, WHERE, JOIN, GROUP BY ํต์ฌ ๋ฌธ๋ฒ
- ๋คํฌ ๋ชจ๋์์ ๊ธ์๊ฐ ๋ฐฐ๊ฒฝ์ ๋ฌปํ๋ค: ์์ธ ์ ํ์ 19๊ฐ์ ์ธ๋ผ์ธ ์คํ์ผ 38๊ฐ๊ฐ ์ฌ๋ผ์ง ํ ํฐ ์ ๋ฆฌ๊ธฐ
- SQLite. "SQLite FTS5 Extension - Tokenizers" (unicode61์ ๊ณต๋ฐฑ๊ณผ ๋ฌธ์ฅ๋ถํธ ๊ธฐ์ค, trigram์ ์ฐ์ํ ์ธ ๊ธ์๋ฅผ ํ ํฐ์ผ๋ก ๋ณธ๋ค). https://www.sqlite.org/fts5.html#tokenizersโฉ
- SQLite. "SQLite FTS5 Extension - FTS5 Phrases" (ํฐ๋ฐ์ดํ๋ก ๋ฌถ์ ๋ฌธ์์ด์ ์์๊ฐ ๋ณด์กด๋ ํ๋์ phrase๊ฐ ๋๋ค). https://www.sqlite.org/fts5.html#fts5_phrasesโฉ
- SQLite. "SQLite FTS5 Extension - NEAR Queries" (๊ฑฐ๋ฆฌ N ์์ ๋ชจ์ฌ ์์ผ๋ฉด ๋งค์น, ์์๋ ์กฐ๊ฑด์ด ์๋๋ค). https://www.sqlite.org/fts5.html#fts5_near_queriesโฉ
- SQLite. "SQLite FTS5 Extension - The bm25() function" (๋ ๋์ ๋งค์น์ผ์๋ก ์์น๊ฐ ๋ฎ๋ค). https://www.sqlite.org/fts5.html#the_bm25_functionโฉ
- Wang, L. et al. "Multilingual E5 Text Embeddings: A Technical Report" (2024). https://arxiv.org/abs/2402.05672โฉ
- Cormack, G. V., Clarke, C. L. A., Buettcher, S. "Reciprocal Rank Fusion Outperforms Condorcet and Individual Rank Learning Methods" SIGIR 2009. https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf (DOI 10.1145/1571941.1572114)โฉ
- MDN Web Docs. "KeyboardEvent.isComposing" (์ ๋ ฅ๊ธฐ๊ฐ ๊ธ์๋ฅผ ์กฐํฉํ๋ ๋์ true). https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/isComposingโฉ




