Open6
sarashina2.2のunslothを使ったfinetuningについて

unslothを使ったsarashina2.2のfinetuningのメモを残します
そのままやると、train_on_responses_only
のところで以下のエラーが出て学習が終わりません。
chat tenplateが誤ってるっぽいです。
ZeroDivisionError Traceback (most recent call last)
<ipython-input-10-4f9c74a86b96> in <cell line: 0>()
1 from unsloth.chat_templates import train_on_responses_only
----> 2 trainer = train_on_responses_only(
3 trainer,
4 instruction_part = "<|start_header_id|>user<|end_header_id|>\n\n",
5 response_part = "<|start_header_id|>assistant<|end_header_id|>\n\n",
/usr/local/lib/python3.11/dist-packages/unsloth_zoo/dataset_utils.py in train_on_responses_only(trainer, instruction_part, response_part, force_match, tokenizer, return_function)
359 # Check if all labels randomnly got masked to nothing - maybe wrong chat template?
360 from .training_utils import fix_zero_training_loss
--> 361 fix_zero_training_loss(None, tokenizer, trainer.train_dataset)
362 return trainer
363 pass
/usr/local/lib/python3.11/dist-packages/torch/utils/_contextlib.py in decorate_context(*args, **kwargs)
114 def decorate_context(*args, **kwargs):
115 with ctx_factory():
--> 116 return func(*args, **kwargs)
117
118 return decorate_context
/usr/local/lib/python3.11/dist-packages/unsloth_zoo/training_utils.py in fix_zero_training_loss(model, tokenizer, train_dataset)
70
71 elif seen_bad / (seen_bad + seen_good) == 1:
---> 72 raise ZeroDivisionError(
73 "Unsloth: All labels in your dataset are -100. Training losses will be all 0.\n"\
74 "For example, are you sure you used `train_on_responses_only` correctly?\n"\
ZeroDivisionError: Unsloth: All labels in your dataset are -100. Training losses will be all 0.
For example, are you sure you used `train_on_responses_only` correctly?
Or did you mask our tokens incorrectly? Maybe this is intended?
Maybe you're using a Llama chat template on a non Llama model for example?

unsloth/unsloth/chat_templates.py
に以下を追加
chat_templates.py
自環境では、site-packageを直接編集して追加した。
# =========================================== sarashina22
# Obtained via
# print(tokenizer.chat_template.replace("}\n", "####").replace("\n", "\\n").replace("####", "}\n"))
sarashina22_template = \
"""\n{%- set user_messages = messages | selectattr('role', 'equalto', 'user') | list %}
{%- macro output_available_tools(tools, message) %}
{%- if tools and (message == user_messages[-1]) %}
{{- '<|available_tools|>[' }}
{%- for tool in tools %}
{%- set tool = tool.function %}
{{- "{" }}
{%- for key, val in tool.items() if key != "return" %}
{%- if val is string %}
{{- "'" + key + "': '" + val + "'" }}
{%- else %}
{{- "'" + key + "': " + val|string }}
{%- endif %}
{%- if not loop.last %}
{{- ", " }}
{%- endif %}
{%- endfor %}
{{- "}" }}
{%- if not loop.last %}
{{- ", " }}
{%- else %}
{{- "]" }}
{%- endif %}
{%- endfor %}
{{- eos_token -}}
{%- endif %}
{%- endmacro %}
\n{%- macro output_tool_results(tool_results) %}
{{- '<|tool_results|>[' }}
{%- for tool_result in tool_results %}
{{- "{'content': " + tool_result.content|string + ", 'call_id': '" + tool_result.call_id + "'}" }}
{%- endfor %}
{{- ']' }}
{{- eos_token -}}
{%- endmacro %}
\n{%- macro output_tool_calls(tool_calls) %}
{{- '<|tool_calls|>[' }}
{%- for tool_call in tool_calls %}
{{- "{'id': '" + tool_call.id + "', 'name': '" + tool_call.name + "', 'arguments': " + tool_call.arguments|string + '}' }}
{%- endfor %}
{{- ']' }}
{%- endmacro %}
\n{%- for message in messages %}
{%- if message['role'] == 'user' %}
{%- if tools is defined %}
{{- output_available_tools(tools, message) }}
{%- endif %}
{{- '<|user|>' + message['content'] + eos_token -}}
{%- elif message['role'] == 'system' %}
{{- '<|system|>' + message['content'] + eos_token -}}
{%- elif message['role'] == 'assistant' %}
{% set assistant_content = "" %}
{%- if message.content is defined %}
{% set assistant_content = message.content %}
{%- endif %}
{%- if message.tool_calls is defined and message.tool_calls -%}
{{- '<|assistant|>' + assistant_content + output_tool_calls(message['tool_calls']) + eos_token -}}
{%- else %}
{{- '<|assistant|>' + assistant_content + eos_token }}
{%- endif %}
{%- elif message['role'] == 'tool_results' %}
{{- output_tool_results(message.tool_results) }}
{%- endif %}
{%- if loop.last and add_generation_prompt -%}
{{- '<|assistant|>' -}}
{%- endif -%}
{%- endfor %}
"""
# Ollama from https://ollama.com/library/gemma3/blobs/e0a42594d802
sarashina22_ollama = \
'''
FROM {__FILE_LOCATION__}
TEMPLATE """{{ if .System }}<|system|>{{ .System }}</s>{{ end }}{{ if .Prompt }}<|user|>{{ .Prompt }}</s>{{ end }}<|assistant|>{{ .Response }}</s>"""
PARAMETER stop "<|system|>"
PARAMETER stop "<|user|>"
PARAMETER stop "<|assistant|>"
PARAMETER temperature 0.1
PARAMETER min_p 0.0
PARAMETER top_k 64
PARAMETER top_p 0.95
'''
sarashina22_template_eos_token = "<s>"
CHAT_TEMPLATES["sarashina22"] = (sarashina22_template, sarashina22_template_eos_token, False, sarashina22_ollama,)
DEFAULT_SYSTEM_MESSAGE["sarashina22"] = None # No system message in Gemma-3
CHAT_TEMPLATES["sarashina-2-2"] = (sarashina22_template, sarashina22_template_eos_token, False, sarashina22_ollama,)
DEFAULT_SYSTEM_MESSAGE["sarashina-2-2"] = None # No system message in Gemma-3
多分46行目から1017行目の間なら多分どこでもいいです。

デフォルトのノートブック内のchat templateの指定を変更
from unsloth.chat_templates import get_chat_template
tokenizer = get_chat_template(
tokenizer,
chat_template = "sarashina22",
)
def formatting_prompts_func(examples):
convos = examples["conversations"]
texts = [tokenizer.apply_chat_template(convo, tokenize = False, add_generation_prompt = False) for convo in convos]
return { "text" : texts, }
pass
from datasets import load_dataset
dataset = load_dataset("mlabonne/FineTome-100k", split = "train")

train_on_responses_only
のinstruction_part
とresponse_part
の指定を以下に変更
from unsloth.chat_templates import train_on_responses_only
trainer = train_on_responses_only(
trainer,
instruction_part = "<|user|>",
response_part = "<|assistant|>",
)

コード周りはできるだけgithubのリポジトリに残すことにした。

別途作成していた大きくしたモデル
kurogane/sarashina2.2-5.48B-instruct-no-tuning
なにか間違っているのか、ギリギリ崩壊していない日本語を返してくるので謎