hushpond/llama-3-seoul-culture-lora-rag
<h1>๐ญ Seoul Culture Event Recommendation RAG System (LoRA Adapter)</h1>
<div align="center"> <img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/peft/lora_diagram.png" alt="LoRA Architecture" width="600"/> </div>
<h2>๐ Model Overview</h2> <p> ์ด ๋ชจ๋ธ์ <strong>์์ธ์ ๋ฌธํ ํ์ฌ ์ ๋ณด</strong>๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ฌ์ฉ์์๊ฒ ๋ง์ถคํ ๋ต๋ณ์ ์ ๊ณตํ๋ <strong>RAG(Retrieval-Augmented Generation)</strong> ์์คํ ์ ์ํด ๋ฏธ์ธ์กฐ์ (Fine-tuning)๋ Llama-3 ๊ธฐ๋ฐ์ <strong>LoRA Adapter</strong>์ ๋๋ค. </p> <p> ์ฌ์ฉ์์ ์ง๋ฌธ์ ๋ํด ๋จ์ํ LLM์ ๋ด๋ถ ์ง์์ผ๋ก ๋ต๋ณํ๋ ๊ฒ์ด ์๋๋ผ, ์ธ๋ถ ๋ฐ์ดํฐ๋ฒ ์ด์ค(<code>culture.csv</code>)์์ ์ค์๊ฐ์ผ๋ก ๊ฒ์๋(Retrieved) ์ ํํ ํ์ฌ ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ๋ต๋ณ์ ์์ฑํฉ๋๋ค. </p>
<ul> <li><strong>Base Model:</strong> <code>beomi/Llama-3-Open-Ko-8B</code></li> <li><strong>Fine-tuning Method:</strong> QLoRA (4-bit quantization + LoRA)</li> <li><strong>Target Task:</strong> ์์ธ ๋ฌธํ ํ์ฌ ์ถ์ฒ ๋ฐ ์ ๋ณด ์๋ด (Instruction Following)</li> <li><strong>Dataset Source:</strong> ์์ธ์ ๋ฌธํ ํ์ฌ ์ ๋ณด (4,275๊ฐ ๋ฐ์ดํฐ)</li> </ul>
<hr>
<h2>๐ ๏ธ How to Use</h2> <p>์ด ๋ชจ๋ธ์ <strong>LoRA Adapter</strong>์ด๋ฏ๋ก, ๋ฐ๋์ ๋ฒ ์ด์ค ๋ชจ๋ธ๊ณผ ํจ๊ป ๋ก๋ํด์ผ ํฉ๋๋ค.</p>
<h3>1. Install Dependencies</h3> <pre><code>pip install torch transformers peft bitsandbytes pandas accelerate</code></pre>
<h3>2. Run Inference Code</h3> <pre><code class="language-python">import torch from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig from peft import PeftModel
1. Base Model Load (4-bit Quantization for efficiency)
basemodelid = "beomi/Llama-3-Open-Ko-8B" bnbconfig = BitsAndBytesConfig( loadin4bit=True, bnb4bitquanttype="nf4", bnb4bitcompute_dtype=torch.float16 )
basemodel = AutoModelForCausalLM.frompretrained( basemodelid, quantizationconfig=bnbconfig, devicemap="auto" ) tokenizer = AutoTokenizer.frompretrained(basemodelid)
2. Load LoRA Adapter
โ ๏ธ [YOURHUGGINGFACEID/YOURMODELNAME] ๋ถ๋ถ์ ๋ณธ์ธ ๋ชจ๋ธ ID๋ก ์์ ํ์ธ์!
adaptermodelid = "YOURHUGGINGFACEID/YOURMODELNAME" model = PeftModel.frompretrained(basemodel, adaptermodelid)
3. Define Inference Function
def generateresponse(context, persona, query): inputtext = f"Context: {context} Persona: [{persona}] ์ง๋ฌธ: [{query}]" prompt = f"""### Instruction: {persona} ํค์ผ๋ก ๋ต๋ณํด ์ฃผ์ธ์.
Input:
{input_text}
Output:
""" inputs = tokenizer(prompt, returntensors="pt").to(model.device) with torch.nograd(): outputs = model.generate( **inputs, maxnewtokens=300, temperature=0.7, topp=0.9, eostokenid=tokenizer.eostokenid ) return tokenizer.decode(outputs, skipspecial_tokens=True).split("### Output:")[-1].strip()
4. Test
contextdata = "[์์ธ ํธ๋ ๋ฉ์ด๋ ํ์ด 2025, ์ฌ์ ์๋งค: 8,000์, ์์ธ ์ผ์ฑ๋ ์ฝ์์ค 1์ธต Bํ]" print(generateresponse(context_data, "์น์ ํ ๋ฌธํ ๊ฐ์ด๋", "๊ฐ๋จ๊ตฌ์์ ์ด๋ฆฌ๋ ํธ๋๋ฉ์ด๋ ํ์ฌ ์๋ ค์ค.")) </code></pre>
<hr>
<h2>๐ Training Details</h2>
<h3>Dataset</h3> <ul> <li><strong>Source:</strong> ์์ธ์ด๋ฆฐ๋ฐ์ดํฐ๊ด์ฅ (๋ฌธํ ํ์ฌ ์ ๋ณด)</li> <li><strong>Size:</strong> 4,275 rows (Original CSV), Augmented for training</li> <li><strong>Format:</strong> <code>Instruction</code> (ํ๋ฅด์๋ ์ง์), <code>Input</code> (Context + Question), <code>Output</code> (Answer)</li> <li><strong>Preprocessing:</strong> UTF-8 / CP949 ์ธ์ฝ๋ฉ ์๋ ์ฒ๋ฆฌ, ๊ฒฐ์ธก์น ์ ๊ฑฐ, 512 ํ ํฐ ๊ธธ์ด ์ ํ</li> </ul>
<h3>Hyperparameters</h3> <table border="1" cellpadding="5"> <thead> <tr> <th>Parameter</th> <th>Value</th> <th>Reason</th> </tr> </thead> <tbody> <tr> <td><strong>LoRA Rank (r)</strong></td> <td><code>32</code></td> <td>์ด๊ธฐ Rank 8/16 ์คํ ๊ฒฐ๊ณผ, Rank 32์์ Loss 1.51๋ก ๊ฐ์ฅ ์ฐ์ํ ์ฑ๋ฅ ๊ธฐ๋ก</td> </tr> <tr> <td><strong>LoRA Alpha</strong></td> <td><code>32</code></td> <td>Scaling factor</td> </tr> <tr> <td><strong>Target Modules</strong></td> <td><code>qproj</code>, <code>kproj</code>, <code>vproj</code>, <code>oproj</code>, <code>gateproj</code>, <code>upproj</code>, <code>downproj</code></td> <td>๋ชจ๋ Linear Layer ํ์ต์ผ๋ก ์ถ๋ก ๋ฅ๋ ฅ ๊ทน๋ํ</td> </tr> <tr> <td><strong>Batch Size</strong></td> <td><code>1</code></td> <td>Colab T4 GPU ๋ฉ๋ชจ๋ฆฌ ์ ์ฝ ๊ทน๋ณต</td> </tr> <tr> <td><strong>Gradient Accumulation</strong></td> <td><code>8</code></td> <td>์ค์ Batch Size 8 ํจ๊ณผ (์์ ์ ์๋ ด)</td> </tr> <tr> <td><strong>Learning Rate</strong></td> <td><code>2e-4</code></td> <td>-</td> </tr> <tr> <td><strong>Epochs</strong></td> <td><code>3</code></td> <td>-</td> </tr> <tr> <td><strong>Optimizer</strong></td> <td><code>pagedadamw_8bit</code></td> <td>๋ฉ๋ชจ๋ฆฌ ํจ์จ ์ต์ ํ</td> </tr> </tbody> </table>
<h3>Performance Metrics</h3> <ul> <li><strong>Validation Loss:</strong> <code>1.5152</code> (Rank 32 ๊ธฐ์ค) <ul> <li>Rank 8 Loss: 2.1565</li> <li>Rank 16 Loss: 1.7950</li> <li><strong>Rank 32 Loss: 1.5152 (Best)</strong></li> </ul> </li> <li><strong>ROUGE-L Score:</strong> <code>0.83</code> (๋์ ๋ต๋ณ ์ ํ๋ ๋ฐ ์ผ๊ด์ฑ ํ์ธ)</li> <li><strong>Temperature Analysis:</strong> Temperature 0.5~0.7 ๊ตฌ๊ฐ์์ ROUGE ์ ์ ์ต์ ํ ํ์ธ (๋๋ฌด ๋ฎ์ผ๋ฉด ๋ฐ๋ณต, ๋๋ฌด ๋์ผ๋ฉด ํ๊ฐ ๋ฐ์)</li> </ul>
<div align="center"> <table border="1" cellpadding="5"> <tr> <th>Rank</th> <th>Validation Loss</th> </tr> <tr> <td>8</td> <td>2.1565</td> </tr> <tr> <td>16</td> <td>1.7950</td> </tr> <tr> <td><b>32</b></td> <td><b>1.5152</b></td> </tr> </table> </div>
<hr>
<h2>โ ๏ธ Limitations & Future Work</h2> <ol> <li><strong>Hallucination (ํ๊ฐ ํ์):</strong> <ul> <li>๋ชจ๋ธ์ด ํ์ตํ์ง ์์ ์ธ๋ถ ์ ๋ณด(Unknown Data)์ ๋ํด ์ฝ <strong>50%์ ํ๊ฐ ๋ฐ์๋ฅ </strong>์ ๋ณด์์ต๋๋ค.</li> <li>RAG ์์คํ ์์ ๊ฒ์๋์ง ์์ ์ ๋ณด์ ๋ํด "์ ์ ์์"์ด๋ผ๊ณ ๋ต๋ณํ๋๋ก ์ถ๊ฐ ํ์ต(Negative Sampling)์ด ํ์ํฉ๋๋ค.</li> </ul> </li> <li><strong>Hardware Constraints:</strong> <ul> <li>Colab T4 (free tier) ํ๊ฒฝ์ ์ ์ฝ์ผ๋ก ์ธํด Batch Size๋ฅผ 1๋ก ์ค์ ํ์ต๋๋ค. ๋ ํฐ VRAM ํ๊ฒฝ์์ Full Fine-tuning ์ ์ฑ๋ฅ ํฅ์์ด ๊ธฐ๋๋ฉ๋๋ค.</li> </ul> </li> <li><strong>Scope:</strong> <ul> <li>ํ์ฌ ์์ธ์ ๋ฐ์ดํฐ์ ํนํ๋์ด ์์ด, ํ ์ง์ญ ํ์ฌ๋ ์ผ๋ฐ ์์ ์ง๋ฌธ์๋ ๋ต๋ณ ํ์ง์ด ๋ฎ์ ์ ์์ต๋๋ค.</li> </ul> </li> </ol>
<hr>
<h2>๐ Citation & License</h2> <ul> <li><strong>License:</strong> Llama 3 Community License</li> <li><strong>Base Model:</strong> <a href="https://huggingface.co/beomi/Llama-3-Open-Ko-8B">beomi/Llama-3-Open-Ko-8B</a></li> </ul>
