# Deepseek R1 QLoRA 파인튜닝

# 사용 개념

## Deepseek-R1-Distill-Llama-8B

이 블로그 글은 `Deepseek-R1-Distill-Llama-8B`를 4비트 양자화와 LoRA를 활용해 파인튜닝하고 허깅페이스에 모델을 호스팅하는 것까지를 설명한다.

코드 리뷰를 위해 파인튜닝하였는데, 그 코드를 변형하여 포스팅한다.

`Deepseek-R1-Distill-Llama-8B`모델은 딥시크 R1을 경량화한 모델로 8B크기라서 쓸만한 편이다.

## 양자화

모델을 그냥 파인튜닝(FP32)하면 메모리가 상당히 많이 필요하다. 이를 위해 모델의 가중치를 낮은 비트 정밀도로 변경하는 것을 양자화라 하는데 아래로 내려갈수록 성능이 떨어질 우려는 있지만 리소스 사용량과 속도 면에서 좋다.

```python
model = AutoModelForCausalLM.from_pretrained( 
	MODEL_NAME,
	load_in_4bit=True, #4비트 양자화
	device_map="auto"
)
```

## LoRA

모델 전체 업데이트가 아니라 특정 가중치만 학습하는 것이다. 전체 모델을 건드리는 것은 매우매우 거대한 작업이다...실제 필요한 작업에 비해 사이즈가 너무 크다. 따라서 일부 레이어만 변화를 주는 것.

코드에서는 `q_proj`, `v_proj`, `k_proj`같은 레이어에만 적용하고 있는 것이다. 이러면 메모리를 많이 절약할 수 있다.

## Unsloth 라이브러리

LoRA 파인튜닝을 최적화하는 라이브러리다. 위의 양자화와 LoRA기법, 그러니까 QLoRA를 위해 사용하는 라이브러리다. 장점은 뭐 이렇다고 한다.

* 트랜스포머보다 개별 모델에 최적화
    
* 수동 그래디언트 계산(파이토치 Autograd는 LoRA에서 최적화가 좀 별로일수도...)
    

## WandB

MLops 툴이다. <s>머신러닝계의 데이터독</s> 가입하면 키를 받을 수 있는데 키를 등록하고

```python
wandb.login(key="wandb키")
#프로젝트명은 알아서
wandb.init(project="Finetuning-Deepseek-R1", job_type="training")
```

이런식으로 등록하고 WandB 웹에 접속해서 모니터링할 수 있다.

---

# 코드

하단 코드는 코랩 T4 GPU면 충분하다.

## 필요한 라이브러리 설치

```python
!pip install transformers datasets bitsandbytes huggingface-hub accelerate

!pip install --upgrade --force-reinstall --no-cache-dir git+https://github.com/unslothai/unsloth.git

!pip install wandb

!pip install unsloth_zoo

!pip install xformers
```

## 허깅페이스와 wandb로그인

```python
from huggingface_hub import login
import wandb

hf_token = "HF토큰"
login(hf_token)

wandb.login(key="wandb키")
#프로젝트명은 알아서
wandb.init(project="Finetuning-Deepseek-R1", job_type="training")
```

## 데이터셋, 모델 로딩

```python
from datasets import load_dataset
dataset = load_dataset("사용자명/데이터셋", split="필요한만큼")

prompt_style = """아래는 코드와 해당 코드에 대한 리뷰 요청이다.
코드를 검토하고 3~4줄의 간단한 코드 리뷰를 제공하라.

### 코드:
{}

### 코드 리뷰:
{}
"""

def format_prompt(example):
	code = example['prompt']
	review = example['response']
	return{"text": prompt_style.format(code, reviw)}

dataset = dataset.map(format_prompt)
```

모델 로드하기

```python
from unsloth import FastLanguageModel
# 모델 로드 (4-bit 양자화 활성화)
model, tokenizer = FastLanguageModel.from_pretrained(
	model_name="unsloth/DeepSeek-R1-Distill-Llama-8B",
	max_seq_length=2048,
	load_in_4bit=True,
	token=hf_token
)
```

## LoRA

```python
model = FastLanguageModel.get_peft_model(
model=model,
r=16,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
lora_alpha=16,
lora_dropout=0,
)
```

## 훈련하기

```python
from transformers import TrainingArguments

from trl import SFTTrainer

trainer = SFTTrainer(
	model=model,
	tokenizer=tokenizer,
	train_dataset=dataset,
	dataset_text_field="text",
	args=TrainingArguments(
		per_device_train_batch_size=4,
		gradient_accumulation_steps=2,
		max_steps=100,
		warmup_steps=10,
		learning_rate=2e-4,
		fp16=True,
		logging_steps=10,
		output_dir="outputs",
	),
)

trainer.train()
```

## 모델 저장 후 호스팅

```python
model.save_pretrained("저장경로")
tokenizer.save_pretrained("저장경로")

NEW_REPO_NAME = "호스팅 경로"
model.push_to_hub(NEW_REPO_NAME)
tokenizer.push_to_hub(NEW_REPO_NAME)
```
