데이터셋

https://huggingface.co/datasets/nlp-with-deeplearning/Ko.WizardLM_evol_instruct_V2_196k/tree/main

Convert-py [ 데이터셋 MLX에 맞게 변환 ]

import json

# 입력 파일과 출력 파일 경로를 설정합니다.
input_file = "WizardLM_evol_instruct_V2_196k.train.jsonl"
output_file = "WizardLM_evol_instruct_V2_196k_mlx_chat.jsonl"

def convert_conversation(convo):
    """
    주어진 대화 리스트(convo)를 MLX LM의 chat 형식으로 변환합니다.
    - "human" -> "user"
    - "gpt" -> "assistant"
    기타 역할은 그대로 사용합니다.
    """
    role_map = {
        "human": "user",
        "gpt": "assistant"
    }
    messages = []
    for msg in convo:
        orig_role = msg.get("from", "").strip().lower()
        role = role_map.get(orig_role, orig_role)
        content = msg.get("value", "").strip()
        messages.append({"role": role, "content": content})
    return messages

with open(input_file, "r", encoding="utf-8") as fin, open(output_file, "w", encoding="utf-8") as fout:
    for line in fin:
        line = line.strip()
        if not line:
            continue
        try:
            data = json.loads(line)
        except json.JSONDecodeError as e:
            print("JSON decoding error:", e)
            continue

        # 우선 영어 대화(en_conversations)를 사용하고, 없으면 한국어(conversations)로 대체합니다.
        if "en_conversations" in data:
            conv = data["en_conversations"]
        elif "conversations" in data:
            conv = data["conversations"]
        else:
            # 대화 데이터가 없는 경우 건너뜁니다.
            continue

        # MLX LM에서 지원하는 "chat" 형식으로 변환합니다.
        converted = {"messages": convert_conversation(conv)}
        fout.write(json.dumps(converted, ensure_ascii=False) + "\\n")

print(f"컨버팅 완료! 변환된 파일: {output_file}")

Convert-py [ 데이터셋 MLX에 맞게 분활 ]

import json
import os
import random

# 입력 파일: 변환된 MLX chat 데이터셋
input_file = "WizardLM_evol_instruct_V2_196k_mlx_chat.jsonl"

# 출력 디렉토리 (존재하지 않으면 생성)
output_dir = "data"
os.makedirs(output_dir, exist_ok=True)

# 데이터를 읽어옵니다.
with open(input_file, "r", encoding="utf-8") as fin:
    lines = [line.strip() for line in fin if line.strip()]

# 데이터를 섞은 후, 90%/10%로 분할합니다.
random.shuffle(lines)
split_idx = int(0.9 * len(lines))
train_lines = lines[:split_idx]
valid_lines = lines[split_idx:]

# train.jsonl과 valid.jsonl 파일로 저장합니다.
with open(os.path.join(output_dir, "train.jsonl"), "w", encoding="utf-8") as fout:
    for line in train_lines:
        fout.write(line + "\\n")

with open(os.path.join(output_dir, "valid.jsonl"), "w", encoding="utf-8") as fout:
    for line in valid_lines:
        fout.write(line + "\\n")

print(f"Dataset split complete. {len(train_lines)} train examples, {len(valid_lines)} valid examples saved in '{output_dir}' directory.")

Convert-py 병렬

import json
import os
import random

# 입력 파일과 출력 파일 경로 설정
input_file = "WizardLM_evol_instruct_V2_196k.train.jsonl"
output_file = "WizardLM_evol_instruct_V2_196k_mlx_chat_bilingual.jsonl"

def convert_conversation(convo):
    """
    주어진 대화 리스트(convo)를 MLX LM의 chat 형식으로 변환합니다.
    여기서는 "human" -> "user", "gpt" -> "assistant"로 매핑합니다.
    """
    role_map = {
        "human": "user",
        "gpt": "assistant"
    }
    messages = []
    for msg in convo:
        orig_role = msg.get("from", "").strip().lower()
        role = role_map.get(orig_role, orig_role)
        content = msg.get("value", "").strip()
        messages.append({"role": role, "content": content})
    return messages

# 첫 번째 단계: 원본 파일에서 한국어와 영어 대화를 모두 변환하여 병렬 데이터를 생성
with open(input_file, "r", encoding="utf-8") as fin, open(output_file, "w", encoding="utf-8") as fout:
    for line in fin:
        line = line.strip()
        if not line:
            continue
        try:
            data = json.loads(line)
        except json.JSONDecodeError as e:
            print("JSON decoding error:", e)
            continue

        messages = []
        # 한국어 대화가 있다면 먼저 추가
        if "conversations" in data:
            messages += convert_conversation(data["conversations"])
        # 영어 대화가 있다면 그 다음에 추가
        if "en_conversations" in data:
            messages += convert_conversation(data["en_conversations"])
        if not messages:
            continue

        # 예시: 중간에 구분자 메시지를 넣고 싶다면 아래와 같이 추가할 수도 있음
        # korean_msgs = convert_conversation(data.get("conversations", []))
        # english_msgs = convert_conversation(data.get("en_conversations", []))
        # if korean_msgs and english_msgs:
        #     messages = korean_msgs + [{"role": "system", "content": "[BILINGUAL SEPARATOR]"}] + english_msgs
        # else:
        #     messages = korean_msgs or english_msgs

        fout.write(json.dumps({"messages": messages}, ensure_ascii=False) + "\\n")

print(f"컨버팅 완료! 변환된 파일: {output_file}")

# 두 번째 단계: 변환된 파일을 train.jsonl, valid.jsonl로 분할
shuffled_input_file = output_file
output_dir = "data"
os.makedirs(output_dir, exist_ok=True)

with open(shuffled_input_file, "r", encoding="utf-8") as fin:
    lines = [line.strip() for line in fin if line.strip()]

random.shuffle(lines)
split_idx = int(0.9 * len(lines))  # 90%를 train, 10%를 valid로 사용
train_lines = lines[:split_idx]
valid_lines = lines[split_idx:]

train_file = os.path.join(output_dir, "train.jsonl")
valid_file = os.path.join(output_dir, "valid.jsonl")

with open(train_file, "w", encoding="utf-8") as fout:
    for line in train_lines:
        fout.write(line + "\\n")

with open(valid_file, "w", encoding="utf-8") as fout:
    for line in valid_lines:
        fout.write(line + "\\n")

print(f"Dataset split complete. {len(train_lines)} train examples, {len(valid_lines)} valid examples saved in '{output_dir}' directory.")

Config yaml

# The path to the local model directory or Hugging Face repo.
model: "mlx-community/DeepSeek-R1-Distill-Llama-8B-4bit"

# Whether or not to train (boolean)
train: true

# The fine-tuning method: "lora", "dora", or "full".
fine_tune_type: lora

# Directory with {train, valid, test}.jsonl files or the path to the data file.
data: "data"

# The PRNG seed
seed: 0

# Number of layers to fine-tune
num_layers: 16

# Minibatch size.
batch_size: 4

# Iterations to train for.
iters: 600

# Number of validation batches, -1 uses the entire validation set.
val_batches: 25

# Adam learning rate.
learning_rate: 1e-5

# Number of training steps between loss reporting.
steps_per_report: 10

# Number of training steps between validations.
steps_per_eval: 200

# Load path to resume training with the given adapter weights.
resume_adapter_file: null

# Save/load path for the trained adapter weights.
adapter_path: "adapters"

# Save the model every N iterations.
save_every: 100

# Evaluate on the test set after training
test: false

# Number of test set batches, -1 uses the entire test set.
test_batches: 100

# Maximum sequence length.
max_seq_length: 2048

# Use gradient checkpointing to reduce memory use.
grad_checkpoint: false

# LoRA parameters can only be specified in a config file
lora_parameters:
  # The layer keys to apply LoRA to.
  # These will be applied for the last lora_layers.
  keys: ["self_attn.q_proj", "self_attn.v_proj"]
  rank: 8
  scale: 20.0
  dropout: 0.0

학습 시작